Hud
The in-game heads-up display: a port of the original's bottom-of-screen information bar, drawn last over the 3D scene.
Location
| Header | src/GalaxyEggbert/Game/Hud.hpp |
|---|---|
| Sources | src/GalaxyEggbert/Game/Hud.cpp; helpers ObjectiveHint.hpp (compass arrows) and TextMetrics.hpp (proportional font widths) in the same folder |
| Namespace | GalaxyEggbert::Game |
| Engine dependency | CNA (GraphicsDevice, Texture2D, BasicEffect) and cna-engine's GpuMesh |
Purpose
Hud draws what the player sees on top of the world during Play: lives, held keys, the treasure counter,
glue shots, dynamite, decoys, the breath and power gauges, the training hint banner, the game-speed indicator, the
"fly to the HUD" animation of a picked-up item, and the optional compass. Its layout reproduces the original's
Decor::DrawInfo in the original's 640 x 480 reference space. The space is scaled uniformly by
viewport height / 480 and centred horizontally, so the proportions are the same at any window size.
The HUD is drawn as textured quads through a BasicEffect with an orthographic pixel projection, not
through SpriteBatch. That keeps the draw order explicit: quads submitted last are drawn on top on every
renderer. Text uses Content/icons/text.png with the original's proportional glyph widths
(TextMetrics, since 2026-10-25).
The class has no gameplay state of its own: the host passes every value to Draw each frame.
Collaborators
Uses
- Six sprite sheets from
Content/icons/:blupi.png,element.png,text.png,pad.png,jauge.png,button.png, each in its 2x version when 2x textures are in use (TextureScale.hpp). If one is missing, the HUD disables itself. TextMetricsfor text layout,ObjectiveCompassArrows(ObjectiveHint.hpp) for the compass.
Used by
- GalaxyEggbertGame:
LoadContentinGalaxyEggbertGameLoad.cpp;DrawUnderwaterTintandDrawinDrawOverlay(GalaxyEggbertGameDraw.cpp). The values come from InteractionSystem (lives, keys, treasures, ammunition, the pickup flight, the compass target) and BlupiController (gauges, animation icon, facing). Hud::ProjectWorldToHudSpaceis also used by the host (death and pickup animations) and by the editor (WorldEditorUpdate.cpp, to pick the object nearest the screen centre).
Key members
The complete public API of Hud.hpp is small; this is all of it.
| Member | Signature | What it does |
|---|---|---|
LoadContent | void LoadContent(GraphicsDevice& device) | Loads the HUD's own texture instances and effects. |
DrawUnderwaterTint | void DrawUnderwaterTint(GraphicsDevice& device, int viewportW, int viewportH, float immersion) | A full-screen tint while the camera is under water; drawn after the world and before any HUD or menu quad. |
Draw | see below | Draws the whole HUD. Call last, with depth testing off; restores the opaque blend state. |
ProjectWorldToHudSpace | static bool ProjectWorldToHudSpace(Vector3 worldPos, const Matrix& view, const Matrix& projection, int viewportW, int viewportH, float& outX, float& outY) | Projects a 3D point into the 640 x 480 HUD space; returns false when it is behind the camera. |
void Draw(GraphicsDevice& device, int viewportW, int viewportH,
int lives, bool key1, bool key2, bool key3,
int treasures, int totalTreasures, bool showTreasureCounter,
int bullets, int dynamite, int perso,
bool waterGaugeVisible, int waterGaugeLevel,
bool powerGaugeVisible, int powerGaugeLevel,
std::string_view trainingHint,
const char* overlayMessage,
int animIcon, bool animIconUsesElementSheet,
bool voyageActive, int voyageIconId, bool voyageIsButtonChannel,
bool voyageIsBlupiChannel,
float voyageX, float voyageY,
GalaxyEggbert::Def::GameSpeed gameSpeed, float animOpacity = 1.0f,
std::optional<ObjectiveHint> objective = std::nullopt,
bool rightTouchControls = false, float blupiYaw = 0);
What Draw shows
Positions are in the 640 x 480 reference space, taken from the constants in Hud.cpp.
| Element | Drawn as | Shown when |
|---|---|---|
| Lives | blupi.png icon 48 from (210, 417), 16 px apart | always in Play |
| Keys | element.png icons 215, 222, 229 at x 520, 530, 540 | while that key is held |
| Treasure counter | pad.png panel (410, 445)-(510, 480) at 0.6 opacity, "N/M" centred at (460, 450) | not in hubs (the host hides it for mission 1 and multiples of 10) |
| Glue shots | element.png icon 176 from (570, 442), 4 px apart | while any are held |
| Dynamite | element.png icon 252 at (505, 414) | while dynamite > 0 |
| Decoys | button.png icon 108 at (0, 438) and "= N" at (32, 452) | while perso > 0 |
| Breath gauge | jauge.png at (90, 450), blue, red at 25 or below | while swimming |
| Power gauge | jauge.png at (90, 428), yellow | during a timed state: shield, power, cloud, hide, invert, balloon, squash (one shared count) |
| Training hint | full-width pad.png banner (0, 0)-(640, 40), text centred and shrunk to fit | when the host passes a non-empty hint (TrainingHints or the world's hint zones) |
| Game speed | "0.5x", "0.25x", "0.125x" or "Nx" on a panel at the lower left | when the speed is not normal |
| Pickup flight | the item's icon at voyageX, voyageY | while the original's "voyage" animation runs |
| Compass | three rows at the lower right: rounded straight-line distance, turn (< | >), height (^ - v) | when the compass cheat is on; it moves left to clear the touch buttons |
| Animation indicator | Blupi's current sprite icon, bottom right | first person only (the host passes -1 in third person) |
overlayMessage would draw one large centred message instead of the HUD. As of 2026-11-10 the host's
PhaseOverlayMessage() returns nullptr for every phase, because Pause, Win and Lost have real screens
drawn by InputPad.
How it is used
The host calls Draw from GalaxyEggbertGame::DrawOverlay only when the current phase has no
full screen of its own (Pause, Win, Lost, Setup, Resume, Wait, Init and Editor do), and after it has turned depth testing
off. An excerpt from src/GalaxyEggbertCNA/GalaxyEggbertGameDraw.cpp:
const int hudMission = worldRuntime_.GetMissionNumber();
const bool showTreasureCounter = hudMission != 1 && hudMission % 10 != 0;
hud_.Draw(device, viewport.getWidthProperty(), viewport.getHeightProperty(),
interaction_.Lives(),
interaction_.Key1Count() > 0, interaction_.Key2Count() > 0,
interaction_.Key3Count() > 0,
interaction_.TreasuresCollected(), interaction_.TotalTreasures(), showTreasureCounter,
interaction_.BulletCount(), interaction_.DynamiteCount(), interaction_.PersoCount(),
blupi_.IsNage(), blupi_.GetWaterGaugeLevel(),
...
cheatCompass_ && GalaxyEggbert::Game::kCheatsEnabled
? InteractionSystem::NearestObjective(
worldRuntime_, blupi_.GetX(), blupi_.GetY(), blupi_.GetZ()) : std::nullopt,
tickInput_.touchControlsEnabled, blupi_.GetYaw());
debug
diagnostics panel (lines built by DebugOverlayLines in DebugOverlay.hpp), the F1 help and all menu
screens are drawn by InputPad.Rules and invariants
- Draw the HUD last in the frame, with depth testing disabled; it sets its own blend states and leaves
Opaqueset. - Layout is in the 640 x 480 reference space only; never place HUD items in window pixels.
- The HUD is presentation: it reads values and changes nothing in the simulation, so it cannot affect determinism, golden traces or autoplay results.
- The compass is the direct 3D distance to the nearest active chest, or, when none is left, to the nearest exit
(an object whose registry entry has
levelExit); it is not a route length.
Tests
There is no dedicated Hud verifier. Coverage comes from:
VerifyInteractionSystem(tools/VerifyInteractionSystem.cpp, linksHud.cpp): checksHud::ProjectWorldToHudSpace, the compass target (NearestObjective) and its arrows (ObjectiveCompassArrows).- The golden frames in
tests/golden/capture whole Play frames of world 999, HUD included (tools/verify_golden_frames.sh; needs a display, not part of ctest). - The editor verifiers link
Hud.cppbecause the editor usesProjectWorldToHudSpace.
Related pages
- InputPad: menus, touch pad, cheats list and help overlay
- InteractionSystem, BlupiController: where the HUD's values come from
- GalaxyEggbertGame: the draw order
- Gameplay, Cheats & Diagnostics
- Rendering, Game Loop