Half-Life Graphics Part1: 2D Sprites

Intro

This series of tutorials covers a number of Half-Life graphics topics. This first one covers printing 2D sprites on the screen. These functions are used in the SDK e.g. for displaying the weapons list, ammo, health indicators etc.

2D Sprites are 2D images, and are drawn directly on the screen. Dont confuse this with "3d sprites", which are drawn at a position in the world, and specified at a 3d location (in which case the engine calculates the correct position on the screen). The effect of drawing a 2D sprite is just to add a flat image to the players screen. No matter what direction the player looks, the image will still appear at the same place on the screen. All 2D sprite drawing is done from client.dll.

Drawing 2D Sprites

The process of drawing a 2D sprite on the screen is:

  1. Load the sprite from disk into memory.
  2. Select the sprite ready to draw.
  3. Draw the sprite on the screen

Simple 2D Sprite Loading / Drawing

The following code will load a sprite and display it at a coordinate on the screen.

HSPRITE m_hRadarSpr;                                    // Declare the variable as a HSPRITE type (int)

m_hRadarSpr = LoadSprite("sprites/radar1.spr"); // Load the sprite into memory from disk
SPR_Set( m_hRadarSpr, 255, 255, 255);                    // Set the sprite ready to display when SPR_Draw* is called
SPR_DrawHoles( 0, x, y, NULL);                               // Draw the current "SPR_Set" sprite at position x,y on the screen

LoadSprite() / SPR_Load()

LoadSprite() calls SPR_Load() to load the sprite into memory. It returns a HSPRITE, which is an integer index into the global sprite array. This is the LoadSprite() sourcecode in util.cpp:

HSPRITE LoadSprite(const char *pszName)
{

    // Declare variables
    int i;
    char sz[256];

    if (ScreenWidth < 640)
        i = 320;
    else
        i = 640;

    sprintf(sz, pszName, i);

    return SPR_Load(sz); // Do the actual loading of the sprite into memory
}

SPR_Set( HSPRITE Index, red, green, blue )

SPR_Set() sets the selected sprite ready for display. It takes r/g/b parameters for shading the sprites (you can use this to make the sprites lighter/darker).

SPR_Draw( sprite animation frame, x, y, const wrect_t *sprite draw area)

All the SPR_Draw() functions take the same parameters, but they have slightly different behaviour. The last parameter is a wrect_t which defines the area of the sprite to be displayed. This is used for small sprites, where it is more memory efficient to pack multiple sprites together in a single sprite. If you leave it as NULL, the whole sprite is used.

SPR_Draw()

    This is a fast way to draw rectangular opaque sprites. In practise, it's never used, since most sprites are non-rectangular.

SPR_DrawAdditive()

    This draws translucent sprites on the screen. Any black pixels on the sprite will be completely transparent.

SPR_DrawHoles()

    This draws sprites with and pixels that use the 255th pallete colour treated as completely transparent.

Client side Precacheing and HUD.txt

Most of the code in the client.dll doesnt do its own LoadSprite() call. Instead, it gets it's sprites from the HUD.TXT file. The CHud class has a number of methods for reading and cacheing all the sprites from this file, and we can use the global gHUD object to access them:

CHud::VidInit()

This is called on startup, opens the hud.txt file, and loads all the sprites into memory. You shouldnt need to change anything in this code - providing you add your new sprite to hud.txt, it should just get picked up with all the others.

void CHud :: VidInit( void )
{
...
    // we need to load the hud.txt, and all sprites within.
    // This just reads a list of all the sprite definitions. It doesnt load the sprites into memory.
    m_pSpriteList = SPR_GetList("sprites/hud.txt", &m_iSpriteCountAllRes);
...

    // Then, for each sprite, call SPR_Load to load it into memory.
    char sz[256];
    sprintf(sz, "sprites/%s.spr", p->szSprite);
    m_rghSprites[index] = SPR_Load(sz); // Our old friend
...
}

CHud::GetSpriteIndex( const char* SpriteName)

Returns the CHud sprite index for the relevant sprite.

e.g. m_HUD_number_0 = gHUD.GetSpriteIndex( "number_0" );

CHud::GetSprite()

Translates the CHud sprite index into the global HSPRITE for the relevant sprite.

e.g. HSPRITE hsprNumber0 = gHUD.GetSprite(m_HUD_number_0);

CHud::GetSpriteRect()

This gets the displayable area for the relevant sprite.

e.g. wrect_t recNumber0 = gHUD.GetSpriteRect(m_HUD_number_0);

Example - Displaying a sprite from hud.txt

So, to display the sprite from the basic code at the top of the tutorial sprite, we do:

int m_hRadarSpr;                                    // This is the integer index of the sprite in gHud

m_hRadarSpr = gHUD.GetSpriteIndex( "radar1");                      // Get the sprite index from gHud
SPR_Set( gHud.GetSprite( m_hRadarSpr ), 255, 255, 255);         // Set the sprite ready to display when SPR_Draw* is called
SPR_DrawHoles( 0, x, y, &gHUD.GetSpriteRect(m_hRadarSpr);   // Draw the current "SPR_Set" sprite at position x,y on the screen


In the next tutorial we'll cover the EFX API, including 3D sprites, beam entities etc.

steve