GrimDark | Unreal Engine | Solo Project
Overview
This Top-Down Shooter is an arcade-style combat project built in Unreal Engine. My main objective was to create a scalable, wave-based survival loop that combines adaptive enemy AI with responsive player mechanics. I developed an AI controller using Pawn Sensing to enable dynamic tracking and synchronised melee attacks via animation montages. I also designed a cohesive HUD to display real-time game states, including player health and wave progression, and ensured that core systems such as reloading and combat remained stable during intense gameplay. This project strengthened my skills in managing system interactions and state management, giving me a solid foundation for refining enemy behaviours and combat balance.
Technical Implementation
1. State-Based Combat Logic
The Challenge: During development, I identified frequent input conflicts that allowed players to trigger simultaneous actions, such as reloading while dead or spamming animations during combat. These issues compromised the project's responsiveness and led to immersion-breaking bugs.
The Solution: I implemented a state-based logic system using boolean flags (e.g., isAlive, isReloading). By adding conditional checks to core combat inputs such as firing and reloading, I ensured that actions occurred only when the player was in the correct state. This approach eliminated animation overlap and input conflicts, creating a more predictable and polished player experience.
2. Modular Wave-Based AI Spawning
The Challenge: While designing combat encounters, I found that manually configuring each wave was inefficient and challenging to balance. I needed a system to automatically track active enemies and trigger the next phase, eliminating the need to overhaul the level blueprint for each encounter.
The Solution: I developed an array-based spawning system in the Game Mode to track active enemy actors. Using an AI Controller with PawnSensing, enemies dynamically tracked the player while the Game Mode monitored the array size. When the array was empty, the system advanced to the next wave, enabling scalable difficulty via variable adjustments rather than core code changes.
3. Dynamic Player-Controller Initialization
The Challenge: Significant issues occurred with UI and HUD elements failing to load correctly at runtime because the player character or controller was not fully initialised when the HUD widget requested data.
The Solution: To address issues with "Event BeginPlay" firing before references were valid, I implemented a custom polling system. This system introduced a brief, controlled delay after spawning to verify that a valid Player Controller reference was present. By establishing critical links only when the actor was ready, I resolved HUD binding errors and ensured player data accurately reflected health and ammo states from the first frame.
Reflection on Development
Upon reviewing the wave-spawning system, I see a clear opportunity for improvement. Although the current array-based tracking is effective, manually checking the array length to trigger waves becomes increasingly challenging as the number of enemy types increases. In future iterations, I would implement a Data Table-driven system. This approach would centralise wave data, such as enemy counts and types, making the system more modular and easier to balance without altering the core Blueprint logic.
Assets & Credits
Developed as a solo project. Some environmental assets, audio files, and animation packs were sourced from public domain libraries and the Unreal Engine Marketplace. All original gameplay logic and systems architecture were developed by me.