Selected Works
Projects
My collection of projects exploring gameplay systems, engine fundamentals, and scalable architecture through hands-on implementation.
Overview
Building interactive experiences through design, code, and iteration.
Each project below highlights a specific skill set—from game mechanics and UX flow to visual composition and prototyping.
Tank Game
The TANK project marked my re-entry into Unity development, focusing on interconnected gameplay systems in a full workflow, inspired by my National Service experience with armoured vehicles.
Mechanics Overview
The mechanics were implemented through focused C# scripts, reinforcing core Unity gameplay patterns. Logic was split into movement, shooting, and health components to maintain clear, reusable responsibilities.
Tank mechanics use physics-safe Rigidbody movement, charge-based shooting with strict state control, and centralized health handling for clean round resets.
TankMovement
TankMovement.cs handles physics-safe Rigidbody movement in the physics loop for consistent behaviour, with dynamically generated input axes to support multiple players. It also switches engine audio between idle and driving states based on movement input.
TankShooting
TankShooting.cs uses an event-driven, charge-based firing system with time-based force accumulation and UI feedback. Projectiles are spawned on release or max charge, with state flags preventing multiple shots per input cycle.
TankHealth
TankHealth.cs centralises health and damage handling, resets state on re-enable for round-based gameplay, and triggers feedback before deactivation to allow clean respawn and flow control.
Managers
To support round-based gameplay, Manager scripts coordinate round-based match flow, handling spawning, player control gating, and state progression, while keeping game flow separate from individual tank logic.
GameManager
GameManager.cs orchestrates TANKS’ round-based loop, handling setup, UI, and camera wiring, then advancing gameplay through explicit round states via a coroutine to ensure clean resets and predictable state transitions.
TankManager
TankManager.cs manages each player tank as a single unit, exposing a clean interface for spawning, resetting, player-specific setup, and control gating. This keeps match flow separate from entity lifecycle, improving scalability and extensibility.
Refelctions
This project helped me move from writing isolated gameplay scripts to thinking in terms of system structure and responsibility boundaries. I learned how to separate low-level mechanics from high-level flow control, and how manager scripts can coordinate state, timing, and player control without tightly coupling systems together. Working with enable/disable lifecycles, coroutines, and physics-safe updates improved my understanding of how Unity expects gameplay code to be structured over time. Overall, TANKS strengthened my ability to reason about real-time game architecture rather than just individual mechanics.
SNAKE REMAKE
This Snake game served as an early entry point into Unity, focused on learning core engine concepts rather than complexity. It introduced C# game loops, real-time input, grid-based movement, and collision logic, forming a foundation for more advanced Unity projects.
Snake Movement
Snake movement is step-based with enforced directional constraints, using input-driven state changes to prevent invalid reversals and a fixed update interval for consistent pacing.
Growth Logic
Snake growth is implemented by instantiating body segments and tracking them in an ordered list, with each segment following the previous one. This reinforced object lifecycle management, ordered state, and multi-object coordination.
Collectibles
A collectibles system uses collision callbacks to trigger growth, scoring, and temporary effects, reinforcing clean separation between movement and gameplay state changes.
CollectiblesController
CollectiblesController centralises collision handling for all collectibles, avoiding duplicated logic while allowing item-specific effects to remain modular.
FoodController
FoodController handles food collection, triggering growth and scoring while delegating state changes to the player controller to keep movement logic decoupled.
PowerUpController
PowerUpController applies temporary, time-based effects, introducing transient game states that modify player behaviour without changing core mechanics.
Game Flow & Level Control
Controller scripts manage start, reset, and game-over states, keeping gameplay systems in sync and highlighting the value of centralised flow control.
Refelctions
As an early Unity project, this built confidence but also exposed issues with tight coupling and overlapping responsibilities, highlighting the need for clearer separation of concerns as complexity increases.
Debugging movement, collisions, and resets highlighted the importance of execution order and shared state, shaping a stronger focus on system boundaries and scalable game flow in later projects.
Street Fighter
A 2D fighting game built in Pygame to explore core systems—game loop, input, and animation—outside of Unity, using a Street Fighter–style format to focus on real-time input and state-driven behaviour.
Sprite Animation System
Character animation is implemented using a sprite-based system driven by sequential frame updates rather than engine-provided tools. Each character state (idle, movement, attack, hit) maps to a set of sprite frames cycled over time, with animation timing controlled explicitly in code to remain frame-rate independent.
Building the animation system highlighted clean state transitions and reinforced animation as a core gameplay system.
Player Input & Combat Logic
Player input is handled directly through Pygame’s event system, translating keyboard events into real-time movement and combat actions. This required explicit management of key states, timing, and action triggers rather than relying on engine abstractions. Attacks are initiated based on input conditions and processed through simple combat rules that determine action availability and interactions with opponents.
State Management
The game uses a manually implemented loop with explicit control over input handling, state updates, and rendering each frame. Player behaviour is governed by clear states (idle, moving, attacking, hit) that drive both gameplay logic and animation, preventing conflicting actions and ensuring consistent behaviour during fast-paced combat.
Reflections
Building this project in Pygame exposed assumptions I previously relied on when using other game engines. Underestimating the complexity of player state management and animation timing led to issues like overlapping actions and desynchronised animations. Resolving these problems required more explicit state transitions and tighter control over execution order within the game loop.
Tightly coupling input and combat logic caused timing issues, reinforcing the need for clear state separation and clean control flow.