L4D2 EMS/Appendix: HUD

From Valve Developer Community
< L4D2 EMS
Revision as of 19:40, 25 March 2013 by Rayman1103 (talk | contribs) (There are actually 4 "system timers", not 3.)

Jump to: navigation, search

There is now a (fairly lo-fi, but functional) script controllable "HUD" for making simple text based UI's for your game modes. This won't replace hand-editing .res files and other fanciness if you want to do graphics and tons of customization. But for simple UI's of text boxes, numbers, and timers, it works pretty well.

Below we describe the HUD system in some detail, and then the "Ticker" helpers if you want a simple way to send messages to the players via the HUD/have it timeout/some other convenience stuff.

The Simplified Abstract HUD

The Basic idea is that you will build a table defining your in-game HUD layout. In it you can have Static Data (display options, locations, static values) as well as, if you want, a function to call to update the data fields if you need to (for things like gamemode specific variables you want to show). The table can also hold a few extra things like special pre and post callback functions.

This Table is "Called" a few times a second from C++ (remember this is running on the server... and the UI is shown on the client... so any "highspeed" or uber fast dependent work really cant be done here, it wont update fast enough over the net). This calls the precallback, then the Fields are iterated and any with a datafunc are called, and then the postcallback is called - after which the C++ side looks at the table and updates.

  • HUDSetLayout(HUDTable): Passes a table that defines your in-game HUD to the engine. From there on, you can modify the table to cause changes. Though often you wont, you will instead use a "datafunc" entry to define a simple lambda that returns the up-to-date value to the HUD system.
  • Example Table:
NewHUD <-
   Fields = 
      cooldown = { slot = HUD_LEFT_TOP, special = HUD_SPECIAL_COOLDOWN, flags = HUD_FLAG_COUNTDOWN_WARN | HUD_FLAG_BEEP, name = "cooldown" },
      supply   = { slot = HUD_RIGHT_TOP, staticstring = "Supplies: ", datafunc = @() g_MapScript.Resources.CurrentCount, name = "supply" }
  • Format of Table:
  • PreCallback/PostCallback - a function to call before or after the fields are executed, rarely needed or used
  • Fields: This contains whichever of these you want - though you _need_ a slot and at least one data element!
  • slot - which HUD_ slot on screen you want this to use - NECESSARY, w/o a slot nothing makes sense
  • dataval - if you have a static value (or something you are going to update yourself)
  • datafunc - usually a lambda: if here, it will get called each HUD update and return value used as dataval
  • staticstring - for putting a prefix/postfix string to go with a value, i.e. this is composed with dataval
    • NOTE: you cant use this and a string dataval or func... in that case do it all in one dataval or datafunc
  • special - a field for putting a custom system value (TIMER2, COOLDOWN, MAPNAME, etc)
  • flags - custom flags for background, time, alignment, which team, pre or postfix, etc
  • name - cosmetic name for debugging, not used by code at the moment
  • Note that there are several subtle interactions between the fields
    • dataval is used for data, unless there's a datafunc, in which case we use the return value of the call
    • staticstring is used _with_ data, and is prefixed or postfixed with that data (from val or func, as above)
    • so, for instance:
      • if you just want "MOOSE", you'd set dataval = "MOOSE"
      • if you want "Cows: <dynamic value>" you'd do staticstring = "Cows: " and datafunc = @() GetCowCount()
  • Special field definitions:
HUD_SPECIAL_TIMER0/TIMER1/TIMER2/TIMER3 - these are the 4 "system timers" - use these if you want smooth timing on client!
HUD_SPECIAL_MAPNAME/MODENAME - shows the string for the current MapName or Game Mode Name
HUD_SPECIAL_COOLDOWN - the special COOLDOWN timer - for the COOLDOWN stage type
  • Flags:
HUD_FLAG_PRESTR/POSTSTR - do you want a string/value pair to start(pre) or end(post) with the static string (default is PRE)
HUD_FLAG_BEEP - Makes a countdown timer blink
HUD_FLAG_BLINK - do you want this field to be blinking
HUD_FLAG_COUNTDOWN_WARN - auto blink when the timer gets under 10 seconds
HUD_FLAG_NOBG - dont draw the background box for this UI element
HUD_FLAG_ALLOWNEGTIMER - by default Timers stop on 0:00 to avoid briefly going negative over network, this keeps that from happening
HUD_FLAG_SHOW_TIME - treat this float value as a Time (i.e. --:--) instead of a floating value
HUD_FLAG_NOTVISIBLE - if you want to keep the slot data but keep it from displaying
HUD_FLAG_ALIGN_LEFT - Left justify this text
HUD_FLAG_ALIGN_CENTER - Center justify this text
HUD_FLAG_ALIGN_RIGHT - Right justify this text
HUD_FLAG_TEAM_SURVIVORS - only show to the survivor team
HUD_FLAG_TEAM_INFECTED - only show to the special infected team

  • Slots:

Slot Positions:
Hud slots.jpg

Slot Positions showing HUD_MID_BOX in the center. If you like you can turn on HUD_MID_BOT and set the HUD_FLAG_NOBG flag and display text in the lower portion of the box.
Or you could write a long string and use line breaks to make the text fit the box. Hud slots midbox.jpg

  • HUDPlace(slot,x,y,w,h): moves the given HUD slot to the XY position specified, with new W and H. This is for doing occasional highlight/make a point type things, or small changes to layout w/o having to build a new .res to put in a VPK. We suspect if you want to do a super fancy HUD you will want to create your own hudscriptedmode.res file, just making sure to use the same element naming conventions so you can still talk to them from script.
    • NOTE: x,y,w,h are all 0.0-1.0 screen relative coordinates (actually, a bit smaller than the screen, but anyway).
    • So a box near middle might be set as (0.4,0.45,0.2,0.1) or so.

Example of using HUDPlace to move the position of HUD_MID_BOX:
Hud slots place.jpg

Ticker Helper Functions

We often found it useful to have a single-line wide HUD element near the top of the screen for quick messages for the players. As such, we added some "Ticker" helper functions, to attach such an element to your HUD, and make it easy to add strings to it, add timeouts, set it to blink or not, and so on. The basic idea is that you define a HUD table yourself as above, and then call the Ticker_AddToHud below (with your hudTable)

So these are basically just a shortcut for putting strings on the TICKER line of the HUD and manage timeouts and such. You can certainly just use that line yourself using the direct HUD system. But this is a little helper if you want to use it in a simple standard way.

  • Ticker_AddToHud(hudTable, strInit, blink = false): Adds a ticker entry to a hud table - and sets up an initial string. Will use the HUD_TICKER slot. Note: This also adds a SlowPoll for the ticker so it can manage it's timeout.
  • Ticker_NewStr(newStr, newTimeout = -1): Puts a new string on the ticker, and optionally sets a new timeout value at the same time.
  • Ticker_Hide(): Hides the Ticker
  • Ticker_SetTimeout(val): If you want to set a new timeout value
  • Ticker_SetBlink(blinkOn): Configure Ticker to be blinking or not
  • Ticker_SetBlinkTime(val): If you want the ticker to blink for a bit then automatically stop blinking.

Note: If you just want a Ticker

So, if you don't want a HUD, but you do want a ticker to display quick messages and such, you could do something like this (in your OnActivate, say).

function CreateTickerOnlyHUD( startStr = "" )
   TickerHUD <- {}                                   // start with an empty HUD Table
   Ticker_AddToHud( TickerHUD, startStr )            // add a ticker, defaulting to the empty string
   HUDSetLayout( TickerHUD )                         // send this table (w/Ticker now) to start
   HUDPlace( HUD_TICKER, 0.25, 0.04, 0.5, 0.08 )     // Move the Ticker from default to top of screen