C++ for Engines
A third-person, ability-driven action prototype built from a blank Unreal Engine 5 C++ project — five spells, turrets, a Giant Ball hazard, and every system talking anonymously through a single GameplayEvents bus.
- Type
- University Module
- Module
- GDEV50010
- Level
- 5 · Year 2
- Engine
- UE5 (C++)
- Period
- Oct – Dec 2025
- Team
- Solo
- VCS
- Perforce P4V
- Patterns
- 6
What is C++ for Engines?
C++ for Engines is a Level 5 solo module project (GDEV50010) at Staffordshire University, built entirely from a blank Unreal Engine 5 C++ project. No starter content, no framework to borrow from — just a blank slate and the task of constructing a fully working third-person action prototype, architectured around SOLID principles, Anonymous Modular Design (AMD), and a suite of industry-standard design patterns.
The prototype is a fast-paced, ability-driven action game inspired by X-Men Legends II: Rise of Apocalypse — mana-based spell casting, environmental destruction, and flight-style movement bursts — combined with a Giant Ball hazard enemy borrowed from Elden Ring's Silver Sphere. The player wields five distinct spells (Fire, Ice, Electric, Ice Spikes, Lightning Strike), engages Rotating Detection Turrets with a full state machine, and progresses through a world with Invisible Magic Path tiles, breakable objects, collectibles, and checkpoints.
// UE5 C++ — in-engine gameplay screenshot
Version control was maintained via a self-hosted Perforce depot (P4V, VPN over double-NAT on a personal PC) — a deliberate choice to mirror industry pipeline practice. All 146 C++ source files were written across 52 folders, covering audio, animation, components, enemies, gameplay, input, interactables, managers, Niagara VFX, player, save/load, UI, and a secondary TwinStick prototype mode.
Inspirations & Mechanics
⚡ X-Men Legends II: Rise of Apocalypse
The primary inspiration for the combat feel. Flight bursts, mana-driven spellcasting, environmental destruction, and dynamic voice-over feedback. The project aims to replicate the audiovisual power fantasy — spells feel loud, reactive, and satisfying even in a simple test environment.
// Gameplay — mechanics in action
🔮 Spell Combat System
Five distinct spells unlock across the run: Fire, Ice, Electric, Ice Spikes, and Lightning Strike. Each consumes mana via the centralised AttributesComponent. The HUD animates in real time — bar chunks, shake intensity scaled to mana cost, smooth interpolation back to full on regen. Casting with insufficient mana fires OnLowManaAttempt, triggering distinct audio feedback.
// In-engine — system interaction 2
⚫ Giant Ball Hazard (Elden Ring)
Inspired by the Elden Ring Silver Sphere — a comedic, disruptive patrol enemy that pauses when the player is detected, then charges. Spline-following patrol, proximity trigger, rush mode, collision reset. Deliberately a "set-piece" enemy that breaks combat pacing. Iterating from Blueprint revealed: sphere half in ground (fixed by Z offset), unintentional jumpscare delay (fixed in patrol/charge transition logic), and overshoot not resetting (fixed via collision-triggered state reset).
// In-engine — system interaction 3
🏰 World Features
Invisible Magic Path tiles revealed at runtime. Rotating Detection Turrets with cone-of-sight aggro. Breakable environmental objects that drop collectibles. Full checkpoint save/load system persisting player stats and inventory across sessions. Camera scroll-wheel zoom bound via Enhanced Input.
Anonymous Modular Design
The entire project is built around Anonymous Modular Design (AMD) — a pattern where no class holds a direct dependency on another. All inter-system communication flows through a centralised UGameplayEvents bus (a UWorldSubsystem), which acts as the sole message router for the game. This eliminates spaghetti coupling and means any system can be added, removed, or replaced without touching anything else.
// Example: Spell fires mana event — HUD, AudioSystem, and RegenLogic // all respond WITHOUT the spell knowing they exist. Events->BroadcastSpellCastRequest(OwnerActor, ManaCost, bApproved); Events->OnManaUseComplete().AddUObject(this, &UAttributesComponent::OnManaUseCompleteRequest); // → UI updates mana bar → AudioSystem plays SFX → Regen restarts
This approach means a Spell does not know the UI exists. A Breakable does not know the AudioSystem exists. The GiantBall does not know the camera shake system exists. Everything communicates anonymously through events.
Key architectural decisions:
- All gameplay state changes route through UGameplayEvents — no Tick polling, no hard casts
- UAttributesComponent is the single authoritative source for Health, Stamina, and Mana — no stat can be modified except through it
- Blueprint subclasses handle presentation only (Niagara, audio, UI polish) — all core logic stays in C++
- Every system exposes UPROPERTY(EditAnywhere, BlueprintReadWrite) parameters so designers can tune without touching code
- Contingency-driven development: each post has an explicit "what I'll do if inheritance fails" plan, documented in the forum thread
18 C++ Systems
UAttributesComponent
APlayerCharacter_Base
UPlayerHUDWidget
ATPGameMode
UAudioSystem
UNiagaraSubsystem
// In-engine — system interaction 1
ASpellBase
ATurretBase
ARotatingDetectionTurret
AGiantBallHazard
Save / Load System
UInventoryComponent
UCombatTextSubsystem
UVoiceOverComponent
UDebugHelper
MagicPath System
UBreakableObject_Base
TwinStick Prototype
Six Patterns Applied
Each pattern was documented in the forum thread with explicit C++ implementations, designer-friendly exposure, and contingency plans for when the pattern was not appropriate:
Observer
Singleton / Facade
State
Template
Flyweight
Type Object
SOLID Principles Applied
SRP (Single Responsibility)
OCP (Open/Closed)
LSP (Liskov Substitution)
ISP (Interface Segregation)
DIP (Dependency Inversion)
Pipeline & Post-mortem
Self-Hosted Perforce
Rather than using Git, the project was managed with Perforce P4V — the VCS standard across AAA game studios. A personal Perforce
server was self-hosted on a home PC with a VPN over double-NAT to create a secure remote depot. The depot was named
CPPforEngines, workspace NomNom_CPPforEngines_WS, with a .p4ignore file configured to exclude UE5
intermediate build artefacts.
This was a deliberate engineering decision — the forum thread explicitly frames it as industry practice preparation, not just convenience. Self-hosting the server rather than using a service like Helix Teams mirrors the studio environment where teams manage their own P4 infrastructure.
What Went Wrong
Roll / animation breakage — The original skeletal mesh had an extra bone absent from standard UE rigs, making animation retargeting impossible. The roll mechanic became a speedrunner glitch rather than a dodge. Ultimately cut.
HUD desyncs — Consecutive spell casts caused mana bar desync when events fired faster than the lerp completed. Fixed by always lerping toward the latest authoritative target value from AttributesComponent, not a cached local value.
Giant Ball overshoot — The Silver Sphere would continue charging past the player after collision. The charge state did not terminate on collision because the state machine transitioned only on a timer, not on an overlap event. Fixed by binding the reset to an OnCollision event that forcibly sets state to Recover.
HUD shake drift — Applying screen-space shake offsets without resetting the transform first caused incremental drift. Fixed by resetting to BaseTranslation before each shake tick.
Chunks not spawning — HUD chunk widgets were children of a Canvas Panel; grabbing them required the parent canvas reference, not the widget directly. A subtle UMG binding quirk that took considerable time to diagnose.

