In games it is often important to broadcast information to the player through the use of a HUD (heads-up display). Often this is the player's health, the amount of ammunition they are carrying, or a message about an objective. Multiple different elements normally comprise this HUD. Here we'll discuss how to create those elements and use them to display information to the user.
HUD elements use the VGUI2 library to render their state. This allows them to not only look and feel like the other VGUI elements within the game, but also allows them to use scripted, animated components, greatly increasing their visual quality. This requires them to have both code components declared on the client, and script files residing on the client as well, external to the code.
HUD element classes should descend from the
CHudElement base class. This base class deals with updating, drawing, as well as hiding HUD elements based on certain game state (a HUD element can be set to disappear when the player dies). This class also interprets the
HudLayout.res file in the
/scripts directory of the game to determine the positions and behaviors of all the elements. Most elements will descend from the
vgui::Panel base class. This provides them with a rudimentary canvas on which to draw text, shapes or textures. For more information on the
vgui::Panel class, see the VGUI2 documentation provided in this SDK.
Elements use a simple system for capturing messages sent from the server. A HUD element declares a callback function and the corresponding message to link it to. When a message is received, the callback function generally reads some piece of information out of the message and displays or otherwise updates its visual state. An example of this is the Damage message sent by the server when a player takes damage. The message is encoded with the amount of damage, as well as the location. The Health HUD element receives the message and plays animations as well as alters its numerical read-out to reflect the new health of the player.
Message handlers are declared by the HUD element via the following macro:
DECLARE_HUD_MESSAGE( CMyHUDClass, MyHUDMessage );
The first parameter specifies the class declaring the message handler, the second is the message in question. The macro expands the message's name into a callback function which must be defined in the HUD element class. In this case, the callback function created would be:
void MsgFunc_MyHUDMessage( bf_read &msg );
This function's prototype and body must be filled out using the above declaration. The
bf_read class is a data buffer with various query methods. It is used to transmit unformatted data between the server and client. The server uses the
bf_writeclass to send this data.
The HUD element must also include the
HOOK_HUD_MESSAGE macro definition, generally called within the
Init() function of the HUD element. Extending our above example, the definition would be:
HOOK_HUD_MESSAGE( MyHUDMessage );
This macro registers the message and links it to the callback function we defined earlier. Failure to include this declaration will result in an assertion and failure when a user message is sent from the server.
Sending Messages From the Server
For messages to be properly received by the client, they must be declared and sent from the server. This is accomplished via the Register() function in the usermessages singleton.
|This function creates a message definition and retains it for the life of the session. When a message is sent, the name specified here is used to identify the message as it is sent to the client.
(Note: A value of -1 implies the size is variable or unknown).
All user messages should be declared in the
RegisterUserMessages() global function. This function is called upon the instantiation of the usermessage singleton. User messages not declared will not be properly received by the client and will produce an error on attempting to do so.
If all the steps above are followed, the HUD element should now have a working infrastructure for sending and receiving messages to and from the server and client. To send a message, we use the
MessageEnd(), and supporting macros listed below.
The UserMessageBegin() function is defined as:
void UserMessageBegin( IRecipientFilter& filter, const char *messagename )
This function constructs a user message of the given type (by name), and prepares it for accepting data from the user. The filter can be of any
CBroadcastRecipientFilter, etc) as defined in
- Filter used to send message to proper recipients.
- Identifying name for this message (as registered previously).
The following macros provide functionality for writing data into the stream sent to the client. They must be received and processed in the order they were sent. The macros are written as follows:
... WRITE_BYTE( m_uchMyByte ); WRITE_VEC3COORD( m_vecMyOrigin ); WRITE_BOOL( m_bMyState ); ...
The following is a description of all available macros for writing data the message stream:
Unsigned 8-bit angle
Compressed coordinate value
Compressed coordinate value from Vector type
Compressed normal value from Vector type
Compressed angles value from Vector type
Entity index (short)
One bit boolean value
Unsigned bit long value
Signed bit long value
Number of bit values, as specified via parameter
WRITE_macros, the message must be terminated and sent via the
Receiving Messages on the Client
Once a message has been sent by the server, the client will receive it via the callback function hooked to that message. The receiving callback function is passed a bf_read class instance which contains the data passed from the server. The class contains utility functions to read formatted data out of the stream. Again, the data must be read in the order it was sent.
Now that data is sent and received between the server and client, the painting functions (as described in the VGUI2 document) may be used to draw any information it wishes based on that data.
An example HUD element can be found in the following files, included with the sample application: