In this tutorial, you'll learn how to change the sample program (GTEST.EXE) to enable an overlaid Heads Up Display (HUD). This simply draws some 2d textures over the frame after all the 3D work has been done, so that it appears to sit on top of the screen. (See the screen shot below).

The code is by no means polished or optimised, but it will give you a good idea as to how to create a HUD (very important for most games). Please use this code, but please attribute me or let me know if you are using it.
Possible improvements:

This tute was written by Ewen.

ok, here we go...remember to give or take a few when it comes to line numbers...

Here is a screen shot of the Heads Up Display that I'm working on.

 

First download the files (there is a link at the bottom of the page).

Second: you must make changes to Client.C and GENVS.C.

In GENVS.C you must add a line near the beginning of the file to include HUD.H

Line 35: #include "HUD.h"

Next add a pointer to a HUD object (and a rectangle object we'll use to position the HUD), about line 72:

// Misc objects
static Console_Console *Console;
Host_Host *Host;

geSound_System *SoundSys;
HUD *theHUD;
geRect gR = {0,0,0,0};

Now we need some changes to the WinMain function, to set the clipping rectangle of the HUD and to create the HUD object, this is around about line 415 or so:

if (HostInit.LevelHack[0])
{
Host = Host_Create(Engine, &HostInit);

if (!Host)
GenVS_Error("Could not create the host!\n");
}
else
strcpy(HostInit.LevelHack, "Levels\\GenVS.BSP");

gR.Left = 0; // left of the screen
gR.Top = CHeight - 1 - 24; // 25 pixels from the bottom of the screen is where we draw
gR.Right = CWidth - 1; // the width of the screen
gR.Bottom = CHeight - 1; // the bottom of the screen
theHUD = CreateClientHUD(Engine, &gR); // create the HUD object


QueryPerformanceFrequency(&Freq);
QueryPerformanceCounter(&OldTick);

Finally, we need to destroy the HUD on shutdown by modifying the ShowdownAll function:

//=====================================================================================
// ShutdownAll
//=====================================================================================
void ShutdownAll(void)
{
ReleaseCapture();
ShowCursor(TRUE);

//undone
Text_Destroy();
GMenu_Destroy();
DestroyClientHUD(theHUD);
....etc.

 


In Client.C you must add a line near the beginning of the file to include HUD.H

Line 28:. #include "HUD.h"

After the includes, you must add a line to make a reference to theHUD object in GENVS.C:

Line 35: extern HUD *theHUD;

Next, you must add one line of code to the Client_Frame function:

geBoolean Client_Frame(Client_Client *Client, Host_Host *Host, float Time)
{
Client->Time += Time; // Update client time

ReadServerMessages(Client, Host, Time); // Update any thing the server wants us to update

if (!Client_SendMove(Client, Host, Time)) // Send movement commands
{
assert(!"Client_SendMove");
return GE_FALSE;
}

ControlTempPlayers(Client, Time);

// Only render the world if it has not changed...
// BeginFrame needs to be called with this world before we can use it...
if (!Host->WorldChanged)
{
if (!RenderWorld(Client, Host, Time))
{
assert(!"RenderWorld");
return GE_FALSE;
}
}

// It is now safe to reset this flag since geEngine_BeginFrame will be called before we
// render the world again...
Host->WorldChanged = GE_FALSE;

if (!PrintClientScores(Client, Host))
return GE_FALSE;

if (!DrawHUD(theHUD, Client)) // this is where we draw the HUD
return GE_FALSE;

return GE_TRUE;
}

 


Here is the listing for the HUD header file.

/****************************************************************************/
/* FILE: HUD.h */
/* Ewen Vowels */
/* 29 August 1998 */
/* Copyright (c) 1998, Ewen Vowels; All rights reserved. */
/* NB: This code is free to use / modify, just let me know if you are */
/* using it or attribute it in your program. */
/****************************************************************************/
#ifndef _EVHUD_H
#define _EVHUD_H

#include <Windows.h>

#include "Genesis.h"
#include "Ram.h"
#include "Client.H"
#include "Host.H"
#include "Text.h"
#include "Host.h"
#include "Server.h"
#include "GPlayer.h"
#include "GenVSI.h"

#ifdef __cplusplus
extern "C" {
#endif
typedef
struct evHUD {
geEngine *theEngine;
geRect *theHUDLocation; // technically not needed
geTextLib_Texture *HUDTextures;
} HUD;

HUD * CreateClientHUD(geEngine *Engine, geRect *HUDLoc);
geBoolean DestroyClientHUD(HUD * hud);
geBoolean DrawHUD(HUD * hud, Client_Client *client);

#ifdef __cplusplus
}
#endif

#endif

 


Here is the listing for the HUD source file.

/****************************************************************************/
/* FILE: HUD.c */
/* Ewen Vowels */
/* 29 August 1998 */
/* Copyright (c) 1998, Ewen Vowels; All rights reserved. */
/* NB: This code is free to use / modify, just let me know if you are */
/* using it or attribute it in your program. */
/****************************************************************************/
#include <Windows.h>
#include <assert.h>
#include "Genesis.h"

#include "HUD.h"

#include "Ram.h"
#include "Client.H"
#include "Host.H"
#include "Text.h"

#define evItemWidth 24
#define evItemHeight 24

HUD* CreateClientHUD(geEngine *Engine, geRect *HUDLoc) {
HUD *h = malloc(sizeof(HUD)); // create the memory for the HUD
assert(h);
h->theEngine = Engine; // get a copy of the engine object
h->theHUDLocation = HUDLoc; //store the location
h->HUDTextures = geEngine_LoadTexture(Engine, "GFX\\HUD\\HUD_items.GFX", NULL); //load the texture
return h;
}
geBoolean DestroyClientHUD(HUD * hud) {
if (hud->HUDTextures)
{
assert(hud->theEngine);
geEngine_FreeTexture(hud->theEngine, hud->HUDTextures);
}

assert(hud);
free(hud); // free the memory
return GE_TRUE;
}

geBoolean DrawHUD(HUD * hud, Client_Client *client) {
Client_ClientInfo *clientInfo;
geRect textClip;
int32 remainder, health;
uint32 Ammo;
geBoolean HasItem;
// currently there are too many magic numbers in here, and there could be some serious optimisations,
// but this is more of a proof of concept demo.

assert(hud->theEngine);
clientInfo = client->ClientInfo;
if (!clientInfo->Active)
return GE_TRUE;
// draw the health box
textClip.Top = evItemHeight;// health icon is on the second row of the HUD ITEMS texture
textClip.Bottom = 2 * evItemHeight;
textClip.Left = 0;
textClip.Right = evItemWidth;
if (!geEngine_DrawTexture(hud->theEngine, hud->HUDTextures, &textClip, hud->theHUDLocation->Left + 4*evItemWidth - 15, hud->theHUDLocation->Top))
return GE_FALSE;

textClip.Top = 0; // numbers are the top row of the HUD ITEMS texture, so wew clip to show only the 24 pixels
textClip.Bottom = evItemHeight;
health = clientInfo->Health;
remainder = health / 100;
if (remainder > 0) { // only display the first digit if health >= 100
textClip.Left = remainder * evItemWidth;
textClip.Right = remainder * evItemWidth + evItemWidth;
if (!geEngine_DrawTexture(hud->theEngine, hud->HUDTextures, &textClip, hud->theHUDLocation->Left + evItemWidth, hud->theHUDLocation->Top))
return GE_FALSE;
}
health = health % 100;
remainder = health / 10;
textClip.Left = remainder * evItemWidth;
textClip.Right = remainder * evItemWidth + evItemWidth;
if (!geEngine_DrawTexture(hud->theEngine, hud->HUDTextures, &textClip, hud->theHUDLocation->Left + 2*evItemWidth - 5, hud->theHUDLocation->Top))
return GE_FALSE;
health = health % 10;
remainder = health;
textClip.Left = remainder * evItemWidth;
textClip.Right = remainder * evItemWidth + evItemWidth;
if (!geEngine_DrawTexture(hud->theEngine, hud->HUDTextures, &textClip, hud->theHUDLocation->Left + 3*evItemWidth - 10, hud->theHUDLocation->Top))
return GE_FALSE;

// from Client.c
Ammo = client->Inventory[client->CurrentItem];

HasItem = (Ammo & (1<<15));

Ammo &= 0xff;

if (client->CurrentItem == 0) // if it is the blaster, display an infinity symbol
{
textClip.Top = evItemHeight;
textClip.Bottom = 2 * evItemHeight;
textClip.Left = evItemWidth;
textClip.Right = 2 * evItemWidth;
if (!geEngine_DrawTexture(hud->theEngine, hud->HUDTextures, &textClip, hud->theHUDLocation->Left + 8*evItemWidth - 15, hud->theHUDLocation->Top))
return GE_FALSE;
}
else if (HasItem) // if the user has an item, show the AMMO
{
textClip.Top = 0; // numbers are the top row of the HUD ITEMS texture
textClip.Bottom = evItemHeight;

remainder = Ammo / 100;
if (remainder > 0) { // only draw the first digit if necessary
textClip.Left = remainder * evItemWidth;
textClip.Right = remainder * evItemWidth + evItemWidth;
if (!geEngine_DrawTexture(hud->theEngine, hud->HUDTextures, &textClip, hud->theHUDLocation->Left + 6*evItemWidth, hud->theHUDLocation->Top))
return GE_FALSE;
}
Ammo = Ammo % 100;
remainder = Ammo / 10;
if (remainder > 0) { // only draw the second digit if necessary
textClip.Left = remainder * evItemWidth;
textClip.Right = remainder * evItemWidth + evItemWidth;
if (!geEngine_DrawTexture(hud->theEngine, hud->HUDTextures, &textClip, hud->theHUDLocation->Left + 7*evItemWidth - 5, hud->theHUDLocation->Top))
return GE_FALSE;
}
Ammo = Ammo % 10;
remainder = Ammo;
textClip.Left = remainder * evItemWidth;
textClip.Right = remainder * evItemWidth + evItemWidth;
if (!geEngine_DrawTexture(hud->theEngine, hud->HUDTextures, &textClip, hud->theHUDLocation->Left + 8*evItemWidth - 10, hud->theHUDLocation->Top))
return GE_FALSE;
}
else
{
//if (!Console_XYPrintf(Host->Console, 30, 30, " Ammo: No Weapon"))
// return GE_FALSE;
}

textClip.Top = 2 * evItemHeight ;// weapons are on the third row of the HUD ITEMS texture
textClip.Bottom = 3 * evItemHeight ;
switch(client->CurrentItem)
{
case 0: // BLASTER
{
textClip.Left = 0;
textClip.Right = evItemWidth;
break;
}
case 1: // GRENADES
{
textClip.Left = 2*evItemWidth;
textClip.Right = 3*evItemWidth;
break;
}
case 2: // ROCKETS
{
textClip.Left = 3*evItemWidth;
textClip.Right = 4*evItemWidth;
//if (!Console_XYPrintf(Host->Console, 30, 31, " Rockets"))
// return GE_FALSE;
break;
}
case 3: // SHREDDER
{
textClip.Left = evItemWidth;
textClip.Right = 2*evItemWidth;
break;
}
}
if (!geEngine_DrawTexture(hud->theEngine, hud->HUDTextures, &textClip, hud->theHUDLocation->Left + 9*evItemWidth - 15, hud->theHUDLocation->Top))
return GE_FALSE;
return GE_TRUE;
}

 


Download the files: hud.zip
This zip file contains "hud.c", "hud.h", "HUD_items.bmp" and "HUD_items.gfx".
Put the source files (".C", ".H") in your code / source directory and create a directory call HUD in the gfx directory, and then copy the ".GFX" and ".BMP" files into this dir.

Any problems contact Ewen.