
About
Axiom is a C++ project built in Unreal Engine 5. The goal was simple: stop relying on Blueprints and learn to build gameplay systems in code.
Every system in Axiom was designed with one question in mind: how would a professional build this? Components over monolithic classes. Interfaces over direct dependencies. Clean separation between input, logic, and state.
No game. No story. Just systems, built to a standard.
FULL GAMEPLAY
CODE BREAKDOWN
The architecture is built around three principles:
ā
Components over monolithic classes
Every behaviour lives in its own component, HealthComponent, WeaponComponent, InventoryComponent, CameraLockOnComponent. The character class owns them but doesn't implement them. Swap one out without touching the rest.
ā
Interfaces over direct dependencies
IDamageable means the weapon system never needs to know what it hit. Enemy, player, destructible object. If it implements the interface, it takes damage. Nothing is hardcoded.
ā
The Do___ pattern
Input handlers are thin wrappers. A gamepad press and a Blueprint UI button both call DoComboAttackStart(). The logic lives in one place, the input source doesn't matter.
SYSTEMS
HealthComponent
Manages current and max HP. Broadcasts delegates on damage and death so any system — UI, animation, AI — can react without being coupled to the component. Death is handled internally; external code can't trigger it directly.
.h
.cpp
.cpp
WeaponComponent
Handles weapon equip/unequip, attack montage cycling, and melee sphere traces. Attack availability is gated by AnimNotifies, the animation controls when damage can happen, not the code.
.h
.cpp
InteractComponent
A generic component placed on any actor to make it interactable. Broadcasts a delegate on interact, the door, the pickup, anything else handles the response themselves. The component has no knowledge of what it's attached to.
.h
.cpp
CameraLockOnComponent
Automatically finds and locks onto the nearest valid enemy. Smoothly interpolates the controller rotation toward the target's aim socket each frame. Enemy scanning runs on a throttled timer rather than every frame to avoid iterating all world actors at 60hz
.h
.cpp
CASE STUDY
Why interfaces?
The weapon trace doesn't import AIEnemy.h. It casts to IDamageable and calls ApplyDamage. Adding a new damageable actor type, a destructible crate or a boss, means implementing one interface, nothing else changes. This is the difference between code that scales and code that breaks.
ā
The combo input cache
If you press attack 50ms before the current animation allows it, most implementations ignore the input and the game feels unresponsive. Axiom timestamps every attack input and checks it when the animation window opens and if it's recent enough, the combo continues. One float, one comparison, the game feels tight.
ā
What I'd do differently
The enemy cache in CameraLockOnComponent is currently refreshed on a timer. The correct solution is registering to spawn and destroy delegates so the list updates the moment an enemy appears or dies. That's the next step.
