Melee
Base · TemplateAI Idle → Chase → Attack → Dead
Avoidance Obstacle steering
Pattern Standard aggressor
A 2D top-down pixel-art adventure built as one cohesive game framework — 51 C# scripts covering enemy AI, player bars, a coin shop, save/restore and a full options menu.
C# Scripting for Games Engine (GDEV40008) was a solo module at Staffordshire University focused on implementing as many well-integrated game mechanics as possible into a single cohesive Unity 6 project. The deliverable is a 2D top-down pixel-art adventure game with no formal title — the goal was breadth and quality of systems, not narrative.
Starting from scratch with MonoBehaviours, the project grew week by week into a complete game framework spanning 51 C# scripts across enemies, player systems, UI, audio, visual effects, economy, and platform utilities. Progress was documented weekly on the Digital Academy Forum in a structured development thread (Namaan Hussain, H018122M).
// Top-down gameplay — fireball vs Mushroom enemy
The game features a Pokémon-inspired top-down pixel-art aesthetic — outdoor tilesets, tree obstacles, coin pickups, and a cast of distinct enemy characters including Mushroom creatures, a Ninja Bear, and an armoured Bat. Every system was coded in C# using object-oriented principles: abstract base classes, ScriptableObjects, singletons, observers, and state machines.
// Level map — outdoor tilemap at full scale
Each week added or refined one or more systems. Progress was logged in the GDEV40008 Digital Academy Forum thread, covering analysis, implementation, bugs fixed, and reflections.
Five enemy types were implemented, each inheriting from EnemyBaseController and overriding a template-method attack pattern. Enemies
share a centralised EnemyStats component for health, death, and loot.
EnemyBaseController is an abstract MonoBehaviour base class implementing the shared state machine (Idle/Chase/Attack/Dead), obstacle
avoidance steering, and patrol route logic. Each concrete subclass (Melee, Mini, Mixed, Ninja) overrides a single
PerformAttack() template method, keeping the shared AI loop intact while allowing radically different attack behaviour.BaseProjectile provides lifecycle, movement, and collision. Concrete classes (BatProjectile,
NinjaKunaiProjectile, SlowProjectile, PlayerProjectile_Basic) inherit from it and override impact
behaviour. ProjectileBehaviourData is a ScriptableObject used as a data container for projectile parameters, keeping configuration
out of the MonoBehaviour.TopDownCharacterController handles movement, sprint, and roll. Player_Stats manages health, stamina, armour, and coin
values with event callbacks. PlayerData is a ScriptableObject acting as a DTO between scenes. PlayerDataManager persists
it to PlayerPrefs. PlayerRestoreBootstrapper defers the restore to the frame after scene load, avoiding initialisation order
issues.
UI_Canvas subscribes to coin pickup events to display transient "+N" text without polling. UI_HealthBar and
UI_StaminaBar respond to damage events via coroutine lerps, producing smooth bar animations. UI_StaminaBar implements a
dual-fill ghost bar: the front fill tracks stamina instantly, while the back fill drains slowly to give a visual lag effect inspired by modern RPG
stamina systems.UI_FloatingDamageTextManager is a singleton that spawns self-destroying UI_FloatingDamageText instances at enemy
positions converted to screen space. Each label floats upward and fades out over its lifetime, with yellow bold formatting for critical hits.PlayerData ScriptableObject holds runtime values, PlayerDataManager serialises/deserialises via
PlayerPrefs across 14 tracked stats (kills, damage dealt, damage taken, coins collected, score, run time, and more), and
PlayerRestoreBootstrapper triggers on scene load with a one-frame coroutine delay to ensure all MonoBehaviours are Awake before data
is injected.
WindowsDisplayModes is a static C# class that uses [DllImport("user32.dll")] P/Invoke to call
EnumDisplaySettings and retrieve all native display modes from Windows. This is fed into UI_OptionsMenuScreenRes's
dropdown, giving the player access to every resolution their monitor supports — rather than the limited set Unity exposes by default.All scripts were written from scratch in C# for Unity 6. They span six major categories:
Scope management — The brief explicitly encouraged implementing as many mechanics as possible. The risk of this is scope creep and shallow implementations. The approach taken was to prioritise systems that directly connect — an enemy that drops coins that fund shop upgrades that affect player bars is more valuable than three disconnected demo mechanics. This interconnection is what the 92% mark reflects.
Initialisation order — The PlayerRestoreBootstrapper was introduced to solve a recurring bug where player data restored on scene load would be overwritten by MonoBehaviour Awake calls that ran after it. The solution — a one-frame yield before injecting restored state — is a common Unity pattern but required debugging to identify. It now handles all cross-scene persistence reliably.
Ghost stamina bar — The dual-fill approach for the stamina bar (UI_StaminaBar) required two separate coroutines: one for the fast front fill that tracks stamina immediately, and one for the slow trailing drain fill that represents "what you just spent". Coordinating the drain delay without the two fills fighting each other required careful coroutine cancellation logic.
Win32 P/Invoke for screen resolutions — Unity's Screen.resolutions returns a limited and sometimes incorrect list on
certain monitors and Windows configurations. The decision to use EnumDisplaySettings via P/Invoke (WindowsDisplayModes) gives the
player the full native resolution list — a technically involved but user-facing quality-of-life improvement that reflects the module's emphasis on
going beyond the defaults.
PerformAttack() — all pathfinding, state transitions, health, and death logic were inherited. The Ninja enemy's complexity
(five distinct states) was achievable because the base class handled everything else. The modular pickup system (PickupBase → concrete pickups)
followed the same principle.