Back
C# SCRIPTING FOR GAMES ENGINE
92% · First Class

What is C# Scripting for Games Engine?

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).

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.

51C# Scripts
5+Enemy types
3Player bars
14Stats tracked
6+Projectile types
  • Full enemy AI system — 5 enemy types each with bespoke state machines: Melee, Mini, Mixed (splits on death), Ninja (teleport + slash + kunai throw), and a ranged Bat projectile variant
  • Three-bar player HUD — Health bar (event-driven lerp), Armour bar (per-frame lerp), dual-fill ghost Stamina bar with drain/regen coroutines and delayed ghost fill
  • Currency shop — pauses time (timeScale=0), sells escalating-cost Health, Stamina, and Armour upgrades with coin-gate logic
  • Save & restore system — PlayerPrefs-backed PlayerData ScriptableObject restored one frame post-scene-load via deferred bootstrapper coroutine
  • Floating damage numbers — world-to-screen singleton manager spawning self-destroying TMP labels (yellow/bold for crits) at enemy positions
  • Post-run stats screen — reads 14 PlayerPrefs keys across General, Combat, and Range stat panels after game end
  • Full options menu — BGM/SFX sliders with live % display, Window Mode dropdown, Screen Resolution picker using Win32 P/Invoke to enumerate all native display modes
  • World-space enemy HP bars — follow enemies each LateUpdate, with fading action text (e.g. "Must be my imagination...") coroutine
  • Audio manager — singleton AudioManager with dynamic BGM/SFX volume control via PlayerPrefs, coin and fireball SFX on demand
  • VFX manager — singleton VFXManager handling flashbang screen-flash coroutine and particle effect spawning

Week by Week

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.

Week 0 — Setup
Unity 6 installation, project creation. Mesh creation fundamentals, 2D sprite setup, basic prefab workflow. Initial tilemap with outdoor nature tiles.
Week 1 — Core Player
TopDownCharacterController built — top-down movement, MonoBehaviour lifecycle. Tile customisation, component architecture. Moving obstacle (Lerp waypoint oscillation) introduced as first dynamic object.
Weeks 2–3 — Projectiles & UI
Player projectile system (BaseProjectile abstract class, PlayerProjectile_Basic). UI canvas with coin display and transient "+N" pickup indicator via event subscriptions. Coin pickup physics (arc launch). Timer (HH:MM:SS count-up with UnityEvent<int> OnSecondUpdated). Loading screen with async scene load progress bar.
Week 3 — Menus & Persistence
Main menu with Play/Options/Leaderboards/Quit. Options menu: BGM/SFX sliders persisted in PlayerPrefs, Window Mode dropdown, Screen Resolution picker. PlayerPrefs-backed save/restore flow (PlayerData ScriptableObject + PlayerDataManager + PlayerRestoreBootstrapper).
Week 4 — Enemy System
EnemyBaseController abstract base — state machine with Idle/Chase/Attack/Dead states, obstacle avoidance, patrol routes. EnemyStats for centralised health, death events, loot drops. EnemyMelee (melee + slow projectile), EnemyMini (fast/weak, spawned on boss death).
Week 5 — Advanced Enemies
EnemyMixed — melee and bat-projectile combined, splits into two MiniEnemy on death. EnemyNinja — most complex enemy: retreat/charge/slash/kunai-throw/teleport state machine. Bat and Kunai projectile classes. World-space enemy HP bars with LateUpdate tracking.
Week 6 — Player Depth
Stamina system with sprint and roll. Dual-fill ghost stamina bar — fast front fill, slow delayed drain fill. Armour system (PickupBase, Pickups_Armour, UI_ArmourBar). Invincibility frames (MakeInvincible). Player inventory (PlayerInventory).
Week 7 — Economy & Shop
ShopUpgrades system pausing time (timeScale=0), offering Health/Stamina/Armour upgrades with escalating costs. Coin pickups with CoinPickupPhysics arc launch. UI_Sounds for coin SFX on collection. Score tracking in UI_Canvas.
Week 8 — Feedback Systems
Floating damage text — UI_FloatingDamageTextManager singleton spawns world-to-screen TMP labels (yellow bold for crits). Pause menu (timeScale toggle, restart, quit). FlashbangEffect screen-flash coroutine for visual feedback. LevelTransition and EndGameTrig for scene flow.
Weeks 9–10 — Polish & Stats
Post-run stats screen reading 14 PlayerPrefs keys (kills, damage dealt/taken, coins, score, run time, etc.) across three panels. VFXManager and AudioManager singletons finalised. Pause menu polished. Windows display mode enumeration via P/Invoke for full resolution support.

Enemies Built

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.

Melee
Base · Template
Attack Melee + Slow projectile
AI Idle → Chase → Attack → Dead
Avoidance Obstacle steering
Pattern Standard aggressor
Mini
Swarm · Spawned
Attack Fast melee rush
Spawned by EnemyMixed on death
Stats Lower HP, faster speed
Pattern Glass cannon
Mixed
Split · Hybrid
Attack Melee + Bat projectile
On death Splits into 2× MiniEnemy
Pattern Tanky mid-tier boss
Threat Post-death swarm risk
Ninja
Elite · Complex AI
States Retreat · Charge · Slash · Throw · Teleport
Projectile Kunai (NinjaKunaiProjectile)
Pattern Most complex enemy — reads player position, retreats before re-engaging
Ability Teleport repositioning
Mushroom
Ranged · Patrol
Attack Bat projectile ranged
AI Patrol → Alert → Chase
Dialogue "Must be my imagination..."
Pattern Ranged skirmisher

Systems Built

Enemy Architecture (Template Method)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.

Projectile Hierarchy (Inheritance)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.

Player SystemsTopDownCharacterController 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 Architecture (Observer pattern)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.

Floating Damage Text (Singleton + World-to-Screen)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.

Save/Load System — A three-layer system: 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.

Screen Resolution (Win32 P/Invoke)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.

51 scripts, one cohesive game The module brief was to implement as many mechanics as possible that fit together flawlessly. Rather than bolting on isolated features, every system feeds into a shared game loop — enemy deaths drop coins, coins buy shop upgrades, upgrades affect player stats, player stats gate how long a run lasts, and run length feeds the post-game stats screen. The architecture is deliberately interconnected.

51 C# Scripts

All scripts were written from scratch in C# for Unity 6. They span six major categories:

Enemy Systems

EnemyBaseController EnemyMelee EnemyMini EnemyMixed EnemyNinja EnemyStats

Projectiles

BaseProjectile BatProjectile NinjaKunaiProjectile SlowProjectile PlayerProjectile PlayerProjectile_Basic ProjectileBehaviourData

Pickups & Economy

PickupBase Pickups_Health Pickups_Stamina Pickups_Armour Pickups_Coin CoinPickupPhysics ShopUpgrades

Player

TopDownCharacterController Player_Stats PlayerData PlayerDataManager PlayerInventory PlayerRestoreBootstrapper MakeInvincible

UI

UI_Canvas UI_HealthBar UI_ArmourBar UI_StaminaBar UI_EnemyHealthBar UI_EnemyHealthText UI_FloatingDamageText UI_FloatingDamageTextManager UI_Timer UI_PauseMenu UI_StatsMenu_Canvas UI_Sounds UI_MainMenuCanvas UI_MainMenu_BGMSlider UI_MainMenu_SFXSlider UI_OptionsMenu_Graphics UI_OptionsMenu_GoBackButton UI_OptionsMenu_WindowModeDropdown UI_OptionsMenuScreenRes

Systems & Platform

AudioManager VFXManager GameManager LevelTransition EndGameTrig MovingObstacle FlashbangEffect WindowsDisplayModes

Challenges & Lessons

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.

What worked well — The Template Method pattern for enemies proved highly effective. Adding a new enemy type required only implementing 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.