Welcome back to lesson number seven in the GUI scripting series.
In this tutorial we'll assemble the first parts of our new HUD, and we'll deal with the most basic types of interaction between the HUD system and the game itself. Since I'm splitting these HUD tutorials in several mini-lessons, this one will only cover the primary on-screen items.
Complete GUI Scripting - 7: Creating basic HUD items
We're now ready to take a step further and make our HUD actually work. While the previous lesson was just a mini-tutorial on getting an empty HUD script file ready, now we'll add real content to our customized HUD.
Like I said on the previous lesson, we'll build a custom HUD script based on nothing less than the classic DOOM 1 HUD. Let's have a look at it.
Image from idsoftware.com
It's pretty much straightforward: there's a bar at the bottom showing how much ammo the player has left, his health, the guns he have available, his actual state (by using his face), his armor, the keys he have available, and the amount of ammo he has left for all kinds of guns.
In this lesson (and on the next ones), we'll create this HUD from scratch. What we
will implement is:
- The amount of ammo, health, and armor left
- The marine face based on his health and some events
- The guns available (up to gun 7, or the Plasma Gun)
- Everything complete with the original fonts (*depends on SDK/.dat info release)
What we will
not implement however, is:
- The keys display (has no use in DOOM 3)
- The marine face looking to the side he takes damage from
- Display for weapons 8 (Rocket Launcher) and above
- Total ammo display at the right side (the HUD scope apparently can't know the ammo of off-hand guns)
It's important to notice, however, that this is just an example of basic HUD situations and even though some of the elements from the classic HUD will be out, this tutorial will be complete in essence.
Ok, time to get down to business. Open up the HUD script we created on the last lesson, and delete everything there. As the previous script was just a test, we'll create a new one now. Let's use this code for a start.
Code:
windowDef Desktop {
rect 0,0,640,480
visible 1
noevents 1
nocursor 1
}
Ok, this creates our main windowDef. The most important thing to notice is that we're using the "nocursor 1" property -- since this is not a GUI that the player will actually interact with, this is important so a dead mouse cursor won't show.
Fire up DOOM 3 using the command-line arguments for our HUD testing as mentioned in the previous lesson, and we're ready to switch between the editor and the game to test our HUD.
Code:
<your doom 3 folder>\Doom3.exe +seta r_mode 3 +set r_fullscreen 0 +set fs_game classichud +map game/mp/d3dm1
We're now ready to add graphic assets to the HUD. Now, when creating a HUD (or any GUI script for that matter) on a specific MOD, you have to adopt some standard location from files. While there isn't any specific path you have to use, it's important to use some paths that make sense for you and for your MOD team (if you're working on a group), so things won't get chaotic (of course, if you're overwriting the original game assets, you have an specific path and filename you have to use, but that's not the case with our HUD graphics, just with the HUD script itself).
In this lesson's case, I've decided to put all GUI assets for our MOD/HUD on the <doom 3>/classichud/guis/assets folder. So create this directory, grab the example files at the end of this article, and unpack all images there.
The images we'll use were created straight from the original DOOM shareware doom1.wad file, and from some screenshots I found on google. This is an scripting tutorial, so we won't worry about creating the graphics; we'll just use the original ones and that's that.
Okey. Now, with the images unpacked at <doom 3>/classichud/guis/assets, we'll add one of the most basic items from our HUD, the bottom bar.
Code:
windowDef Desktop {
rect 0,0,640,480
visible 1
noevents 1
nocursor 1
//=============================================================================
// Main bar
//=============================================================================
windowDef base {
rect 0,416,1024,64
visible 1
background "guis/assets/hud_base"
matcolor 1,1,1,1
}
}
So we just added an static graphic at the bottom. Pretty simple. Of course, the lines starting with "//" are just comment lines that can be omitted; they have been added for ease of read only.
Getting back to the windowed game and doing our reloadguis command will show our progress so far.

Getting down to business, let's start adding the basic HUD fields: first, the health.
Code:
windowDef Desktop {
rect 0,0,640,480
visible 1
noevents 1
nocursor 1
//=============================================================================
// Main bar
//=============================================================================
windowDef base {
rect 0,416,1024,64
visible 1
background "guis/assets/hud_base"
matcolor 1,1,1,1
}
//-----------------------------------------------------------------------------
// Standard indications: ammo, armor, health
//-----------------------------------------------------------------------------
windowDef bar_health {
rect 110,420,60,49
visible 1
forecolor 1,1,1,1
text "gui::player_health"
textscale 0.6
textalign 2
font "fonts/english"
}
}
Here's something interesting. You'll notice that, on the text property of the HUD script, instead of using an static string, we used a reference to a variable -- "gui::player_health". This is something new on these GUI scripting tutorials in two aspects.
First, it's a "hard-coded" variable from the GUI scripting system -- "player_health". The GUI system has several variables that can be used to read the player health, ammo, and lots of other stuff, and "player_health" is one of them. So, "gui::<variable name>" is the right way to refer to those variables.
Second, since it's a variable, it's passed as a reference, not as a value. It means that instead of just reading the value when it starts, the GUI system will update the "text" property when that variable changes. So whenever "gui::player_health" changes, the text property of that windowDef will change, updating the health.
Now, getting back to game again, and reload the guis, let's see how it works.

And, if we take damage...

Fine. This isn't quite like the original, though; the font is wrong, and there's no "%" in the value caption.
While we can't add our custom fonts just yet, we can add the "%" value already. We can't do that by transforming the text property of the windowDef, or by concatenating any string -- the GUI system doesn't allow such 'powerful' features. We'll have to do a little hack by adding a new windowDef with the "%" text. So it gets us down to,
Code:
(...strip...)
//-----------------------------------------------------------------------------
// Standard indications: ammo, armor, health
//-----------------------------------------------------------------------------
windowDef bar_health {
rect 110,420,60,49
visible 1
forecolor 1,1,1,1
text "gui::player_health"
textscale 0.6
textalign 2
font "fonts/english"
}
windowDef bar_health_percent {
rect 170,420,40,49
visible 1
forecolor 1,1,1,1
text "%"
textscale 0.6
textalign 0
font "fonts/english"
}
}
Notice I've cut out part of the code, or else our source code will start to get bigger and bigger and difficult to read what has actually changed.
So, I just added the "bar_health_percent" windowDef. Checking results again...

Fine. Let's do the same with the armor and ammo fields. It's kind of lengthy but pretty simple.
Code:
(...strip...)
//-----------------------------------------------------------------------------
// Standard indications: ammo, armor, health
//-----------------------------------------------------------------------------
windowDef bar_health {
rect 110,420,60,49
visible 1
forecolor 1,1,1,1
text "gui::player_health"
textscale 0.6
textalign 2
font "fonts/english"
}
windowDef bar_health_percent {
rect 170,420,40,49
visible 1
forecolor 1,1,1,1
text "%"
textscale 0.6
textalign 0
font "fonts/english"
}
windowDef bar_armor {
rect 370,420,60,49
visible 1
forecolor 1,1,1,1
text "gui::player_armor"
textscale 0.6
textalign 2
font "fonts/english"
}
windowDef bar_armor_percent {
rect 430,420,40,49
visible 1
forecolor 1,1,1,1
text "%"
textscale 0.6
textalign 0
font "fonts/english"
}
windowDef bar_ammo {
rect 15,420,60,49
visible 1
forecolor 1,1,1,1
text "gui::player_ammo"
textscale 0.6
textalign 2
font "fonts/english"
}
}
Pretty much the same thing applies to these new windowDefs - using the "gui::player_ammo" and "gui::player_armor" variables, we can retrieve the values of the user ammo and armor, respectively.

Nice, we're getting there. Now, we'll add the weapon numbers, right after those health/armor/ammo windowDefs.
Code:
(...strip...)
//-----------------------------------------------------------------------------
// Weapon numbers
//-----------------------------------------------------------------------------
windowDef gun_2 {
rect 218, 416, 40, 40
visible 1
forecolor 1,1,0,1
text "2"
textscale 0.25
font "fonts/micro"
}
windowDef gun_3 {
rect 242, 416, 40, 40
visible 1
forecolor 1,1,1,0.4
text "3"
textscale 0.25
font "fonts/micro"
}
windowDef gun_4 {
rect 266, 416, 40, 40
visible 1
forecolor 1,1,1,0.4
text "4"
textscale 0.25
font "fonts/micro"
}
windowDef gun_5 {
rect 218, 436, 40, 40
visible 1
forecolor 1,1,1,0.4
text "5"
textscale 0.25
font "fonts/micro"
}
windowDef gun_6 {
rect 242, 436, 40, 40
visible 1
forecolor 1,1,1,0.4
text "6"
textscale 0.25
font "fonts/micro"
}
windowDef gun_7 {
rect 266, 436, 40, 40
visible 1
forecolor 1,1,1,0.4
text "7"
textscale 0.25
font "fonts/micro"
}
}
You'll notice most of the weapons have a "forecolor" property of "1,1,1,0.4" - that is, 40% black. The first one, though, has a forecolor of "1,1,0,1" - yellow. That's because we have already picked it up; we'll make the weapon numbers turn to yellow as guns are picked up.
Well, let's check the progress for now.

Okey. Now, to finally add the marine face..
Code:
(...strip...)
//=============================================================================
// Marine faces
//=============================================================================
//-----------------------------------------------------------------------------
// Normal face
//-----------------------------------------------------------------------------
windowDef marineFace {
rect 290,418,64,64
visible 1
background "guis/assets/hud_face_100_center"
matcolor 1,1,1,1
}
}
And testing...

Looking good.
Well, this is it for adding basic HUD functionalities. On the next lessons, we'll make the marine face act accordingly to what's happening, and make the weapon numbers update. See you there.
Download source and example files (8kb)