HardDriverz is an arcade racing game where players customize karts to race across gravity-defying tracks. Racers collect power-ups, navigate loops, and compete for lap times across single-player and split-screen multiplayer modes.
Gameplay trailer
Core contributions
As UI systems programmer on the team, I implemented the in-game HUD and the messaging architecture connecting it to gameplay systems:
- Split-screen HUD framework (
UMG_HudPannel,BPC_KartHud): An adaptive HUD system that repositions layout anchors, sizes, and widget instances according to active player counts (1, 2, or 4 players). Each element (lap counter, pickup, ranking, timer) runs as an independent component. - Event bus (
BP_EventModule,TABLE_RegisterEvent): A decoupled message bus allowing gameplay systems to broadcast events to the HUD without holding direct widget references. Designers add new event types by adding rows to a DataTable.
Technical overview
The primary goal was maintaining responsive UI updates across varying player counts without polling in Tick functions. The framework uses a push-based architecture where gameplay actors broadcast state changes to the message bus, and widgets react only when their bound events fire.
Architecture
The Event Module coordinates communication between gameplay actors and HUD panels:
graph TD
subgraph Gameplay Logic
P[Player Pawn] --> BPC_KH[BPC_KartHud Component]
P --> BPC_L[BPC_LapCounter Logic]
P --> BPC_P[Pickup System]
end
subgraph Messaging Layer
EB[BP_EventModule Message Bus]
DB[TABLE_RegisterEvent] -- Defines --> EB
BPC_L -- Broadcasts --> EB
BPC_P -- Broadcasts --> EB
end
subgraph UI Framework
UHP[UMG_HudPannel Controller]
EB -- Triggers Update --> UHP
UHP --> LC[UMG_HudLapCounter]
UHP --> RK[UMG_HudRanking]
UHP --> PK[UMG_HudPickup]
UHP --> TM[UMG_HudTimer]
end
HUD implementation
Adaptive split-screen layouts
UMG_HudPannel handles layout transitions across screen configurations:
- View modes: An
E_Layoutenum configuresfunc_RearrangeLayoutto shift widget anchors between single-player, two-player horizontal split, and four-player quad split modes. - Context injection:
BPC_KartHudacts as an injection component on the player pawn, passing initial player references into the HUD on initialization to avoid runtime searches.
Modular widget components
Each HUD element operates as a standalone unit with its own event subscriptions:
- Ranking: Updates player positions along the track using track spline distances broadcast over the event bus.
- Timing and lap counters: The timer and lap counter cache incoming split events and update only when crossing sector gates, avoiding per-frame text updates.
- Pickup display: Displays the currently held power-up and updates when item state changes occur.
Global event bus
BP_EventModule standardizes messaging across systems:
- DataTable registration: Designers register event definitions in
TABLE_RegisterEvent, creating new events without modifying Blueprint graphs. - Typed event holders: Each event maps to a
DA_EventHolderdata asset, keeping event signatures consistent across UI, audio, and visual effects.
Design decisions
Push updates over polling
Widgets do not read game state on tick. Instead, updates fire only when gameplay systems broadcast events over the bus, minimizing layout invalidation costs during races.
Data-driven layout anchors
Split-screen layouts reuse a single UMG_HudPannel instance rather than maintaining distinct widget hierarchies per split mode. The panel adjusts anchor coordinates and visibility parameters based on player count.
Component independence
Individual HUD elements manage their own lifecycle and event subscriptions. Disabling or adding a widget does not affect other UI elements.
Designer-accessible event tables
Game events are registered in DataTables, allowing non-programmers to define new event channels for audio, VFX, and UI triggers.
Decoupled system boundaries
Gameplay pawns and lap systems do not store UI widget references. BPC_KartHud establishes references at startup, and subsequent communications pass through the event bus.