🟡 PAC-MAN Royale

A fully procedural, AI-built battle royale game — from zero to deployed in 2 weeks.

▶ Play Now 📋 Status Board

01 What Is PAC-MAN Royale?

PAC-MAN Royale is a browser-playable battle royale based on the PAC-MAN BATTLE ROYALE CHOMPIONSHIP arcade cabinet by Bandai Namco. Up to 4 players compete on a single shared maze. Eat ghosts to earn escalating combo scores. Last player alive wins.

0
Lines of Code
0
Source Files
0
AI Agents
0
Tasks Completed
0
Automated Tests

🕹️ Classic Mechanics

Grid-locked movement with input buffering, 4 ghost AI personalities (Blinky, Pinky, Inky, Clyde), power pellets, and progressive speed increases.

⚔️ Battle Royale Twist

Eat ghosts to build combos (200→400→800→1600 pts). Chain ghost eats during a single power pellet for maximum score!

🌐 WebGL + WebSocket

Runs entirely in the browser. Online multiplayer uses FishNet + Bayou (WebSocket transport) for real-time server-authoritative gameplay.

🤖 100% AI-Built

Every line of code, every design decision, and every deploy script was produced by GitHub Copilot agents directed by a single human "Business Owner."

Tech Stack

LayerTechnologyVersion
Game EngineUnity LTS (C#)6000.3.9f1
RenderingUniversal Render Pipeline17.0.3
NetworkingFishNet + Bayou (WebSocket)4.x
Local InputUnity Input System1.11.2
UI TextTextMeshPro3.2.0
Build TargetWebGL (Brotli compressed)
ServerLinux headless Docker containerUbuntu 22.04
HostingTrueNAS SCALE (self-hosted)25.04
Audio100% procedurally synthesized (no audio files)

02 Local Multiplayer Mode

Up to 4 players on one keyboard. The game uses a "tabletop arcade cabinet" layout — Player 1 & 2 view from the bottom, Player 3 & 4 view from the top (180° rotated input).

How It Works — Game Loop

LocalMultiPlayerLoop.cs — Orchestration Flow
Start()
  • Create PlayerState[] for each active player (1–4)
  • Register with EliminationTracker
  • Wire PelletManager events (OnPelletEaten, OnPowerPelletEaten)
  • Wire AttackSystem + TargetingSystem per player
  • Start background music: RandomGameTrack()
  • Release ghosts after 3-second delay
every frame ~60 FPS
CheckGhostCollisions()
  • For each player × each ghost
  • Frightened ghost → EatGhost() → award score
  • Normal ghost → HitByGhost() → lose life
CheckPacmanVsPacman()
  • Powered Pac eats un-powered Pac
  • Update power-up timers (8s duration)
  • Update invincibility timers (2.5s)
  • Speed ramp: +3% every 30s, cap 150%
on ghost collision (normal)
HitByGhost(playerIndex)
  • Play explosion SFX 💥
  • EliminationTracker.RecordDeath() → deducts life
  • PacmanController.Die() → death particle effect
  • If lives ≤ 0 → CheckForGameOver()
  • Else → RespawnAfterDelay(2s) with 2.5s invincibility
Dependency Injection — No Singletons
provides →
MazeRenderer
📦
GameContext
ScriptableObject Root
PelletManager
GhostStateMachine
PacmanController

Control Schemes

PlayerPositionMovementJoin KeyColor
P1Bottom-leftW A S DF● Yellow
P2Bottom-right↑ ← ↓ →Space● Cyan
P3Top-leftI J K L;● Pink
P4Top-rightNum 8 4 5 6Num 2● Orange

Core Systems

🧠 Ghost AI — State Machine

Each ghost runs a GhostStateMachine with 5 states:

Ghost AI State Transitions
CHASE Target Pac-Man SCATTER Go to corner FRIGHTENED Run away, blue DEAD Eyes → home power pellet timer expires eaten by Pac return home timer timer expires

AI Personalities:

  • Blinky — Targets Pac-Man's current tile (aggressive direct chase)
  • Pinky — Targets 4 tiles ahead of Pac-Man (tries to ambush)
  • Inky — Uses Blinky's position to create a flanking vector
  • Clyde — Chases when far, scatters when close (unpredictable)
⚔️ Attack System — Combos & Scoring

When you eat a ghost during power-up mode, your combo counter increments. Higher combos award more points:

Combo StepPoints
1st ghost200
2nd ghost400
3rd ghost800
4th ghost1,600

Combo resets when your power pellet expires. The TargetingSystem picks which opponent to target based on the selected mode: KO (lowest lives), Attacker (last person who hit you), Random, or Counter (strongest player).

💀 Elimination & Win Condition

Each player starts with 3 lives. When hit by a ghost, you lose a life. At 0 lives, you're eliminated. In solo mode, 0 lives = Game Over. In multiplayer, last player standing wins.

After dying, Pac-Man respawns after a 2-second delay with 2.5 seconds of invincibility (flashing effect).

03 Online Multiplayer Mode

Online mode uses a server-authoritative architecture — the server runs the full game simulation, and clients are "dumb renderers" that display the server's state.

Network Architecture

Online Multiplayer — Client ↔ Server Flow
🌐 CLIENT (Browser — WebGL)
OnlineLobbyScreen.cs
Show lobby UI · Enter player name · Send JoinBroadcast · Wait for GameStart
PlayerInputBroadcast
WASD → direction enum · Sent on every input change
OnlineClientRenderer.cs
Receive GameStateBroadcast (every frame)
Dead-reckon Pac-Man positions
Interpolate ghost movement
Detect state transitions → FX + SFX
Sync pellets (4 eaten per frame)
Play SFX locally (no server audio)
WebSocket
wss://pmbackend
.thatportal.com
:7777
🖥️ SERVER (Linux Docker — headless Unity)
NetworkGameManager.cs
Listen for JoinBroadcast · Assign slot (0–3) · Track player names · Handle disconnects
OnlineServerGameLoop.cs
Full game simulation (same code as local!)
All collision detection on server
All ghost AI on server
All scoring on server
Speed ramp: +3% every 30s
GameStateBroadcast
Packed every frame (~60 Hz):
PacNetState[4] · GhostNetState[8+]
Eaten pellet coords (4) · RepopVersion
Pos, dir, alive, powered, score, lives

Why Server-Authoritative?

🛡️ Anti-Cheat

Clients only send input direction. The server decides all collisions, scoring, and game state. No client can fake their position or score.

🎯 Consistency

All players see the same game state because it comes from a single authoritative source. No desyncs or "I hit you first" disputes.

📡 Bandwidth Efficient

FishNet Broadcast API sends compact structs — no heavy NetworkObject spawning. Ghosts and pellets are never networked objects; they're state arrays.

Network Data Flow

📤 Client → Server Messages
BroadcastDataWhen Sent
JoinBroadcastPlayer name (string)Once when connecting
PlayerInputBroadcastDirection enum (Up/Down/Left/Right)Every input change
ReadyBroadcastReady flag (bool)Toggle in lobby
📥 Server → Client Messages
BroadcastDataFrequency
PlayerCountBroadcastCount, slot assignment, namesOn join/leave
GameStartBroadcastStart signalOnce
GameStateBroadcastAll Pac-Man + ghost positions, scores, lives, pellets, power-upsEvery frame (~60 Hz)
GameOverBroadcastWinner slotOnce
🏎️ Dead-Reckoning & Interpolation

Because network packets arrive at variable intervals, the client uses dead-reckoning to smoothly animate entities between snapshots:

// On each snapshot arrival:
velocity = (newPosition - oldPosition) / timeDelta;

// On each render frame:
displayPosition += velocity * Time.deltaTime;

// Correction: drift back toward latest snapshot
displayPosition = Vector3.Lerp(displayPosition, snapshotPosition, correctionRate);

// Tunnel wrap detection: if distance > half maze width, snap instantly
if (IsTunnelWrap(oldPos, newPos)) snap();

This produces smooth 60 FPS visuals even with variable network latency.

04 Code Architecture

The project follows a clean separation into 5 script namespaces, plus a shared dependency injection root (GameContext).

Script Architecture — Assets/Scripts/
📁 Assets/Scripts/
Core/ — 29 files · 5,644 lines · Foundation systems
GameContext.cs DI container
MazeRenderer.cs SO maze → Tilemap
GridMover.cs Base grid movement
PacmanController.cs Input → move/die/respawn
PelletManager.cs Pellets, repop
AudioManager.cs BGM + SFX control
ProceduralMusic.cs 10 synthesized tracks
ProceduralSFX.cs 6 synthesized effects
PacmanDeathEffect.cs Particle burst
GhostStateMachine.cs 5-state AI FSM
GhostPersonality.cs Blinky/Pinky/Inky/Clyde
BattleRoyale/ — 5 files · 649 lines · Competitive layer
AttackSystem.cs Combo tracking, scoring
TargetingSystem.cs KO/Attacker/Random/Counter
EliminationTracker.cs Lives, death events
PlayerState.cs Score, lives, name
Networking/ — 5 files · 1,276 lines · Online multiplayer
NetworkGameManager.cs Lobby, slots, lifecycle
Broadcasts.cs All FishNet structs
BayouTransport WebSocket port 7777
Game/ — 7 files · 3,412 lines · Mode orchestrators
GameLoop.cs Single-player
LocalMultiPlayerLoop.cs 1–4P local
LocalTwoPlayerLoop.cs 2P optimized
OnlineServerGameLoop.cs Server simulation
OnlineClientRenderer.cs Client display
UI/ — 20 files · 5,058 lines · All player-facing screens
HudController.cs Lives, score, timer, combo
EliminationScreenUI.cs ELIMINATED / YOU WIN
LobbyController.cs Local lobby
OnlineLobbyScreen.cs Online lobby
MainMenuController.cs Title screen

05 Hosting & Deployment Pipeline

One PowerShell command builds everything and deploys to a self-hosted TrueNAS server. The entire pipeline takes about 5 minutes.

Build-ServerImage.ps1 — Full Pipeline
Developer Machine (Windows)
1
Unity Build
WebGL Client (WASM + HTML) + Linux Server (headless x86_64)
2
Docker Build
Ubuntu 22.04 image with server binary
3
docker save
.tar.gz archive ~68 MB compressed
SSH + HTTPS upload ➜
TrueNAS SCALE Server
4
docker load
Load pacman-server:local image
5
Deploy Script
Upload WebGL files, generate nginx.conf, COOP/COEP headers
6
Create App
waystar-pacman with 2 containers
🌐
nginx:alpine
Port 6767 → HTTP
Serves WebGL files
🎮
pacman-server:local
Port 7777 → WSS
Headless Unity Game Server
Public Access — How Players Connect
🖥️
Browser
🔒
Nginx Proxy Mgr
Port 443 (HTTPS)
pm.thatportal.com
→ TrueNAS:6767 (WebGL)
pmbackend.thatportal.com
→ TrueNAS:7777 (WSS)

Key Infrastructure Decisions

🏠 Self-Hosted on TrueNAS

No cloud costs. The game runs on a home TrueNAS SCALE server with Docker support. The entire deploy is automated through the TrueNAS REST API.

🔒 COOP/COEP Headers

Unity WebGL requires SharedArrayBuffer, which needs Cross-Origin isolation headers. The nginx config adds these automatically.

📦 No Docker Hub

The server image is built locally, saved as a tar.gz, uploaded via the TrueNAS filesystem API, and loaded directly into Docker — no registry needed.

06 Procedural Audio — Zero Audio Files

Every sound in the game — all 10 music tracks and 6 sound effects — is generated mathematically at runtime using AudioClip.Create(). No .wav, .mp3, or .ogg files exist in the project.

Why Procedural?

Music Track Roster

#Track NameBPMKeyCharacter
1Industrial Techno138E minAcid bass, multi-arp, evolving filter, climax melody
2Dark Minimal126A minSparse→dense build, deep sub, dark stabs, tension pad
3Acid House132D min303 squelch, filter sweeps, groove variation, cowbell
4Breakbeat Fury140G minSyncopated breaks, ghost snares, stab chords
5Synthwave Chase128C minLush pads, dual melodies, reverb snare, portamento
6Trance Energy142F minGated pads, supersaw leads, noise riser, euphoria
7Dark Electro135Bb minDistorted bass, glitch drums, noise sweep, tritone
8Progressive House124Ab majWarm pads, funky bass, shaker groove, pluck arps
9Jungle Pressure145D minAmen-style breaks, reese bass, ragga stabs
10Cyber Noir130E minWalking bass, detuned leads, noir atmosphere

Synthesis Primitives

🔊 How the Audio Engine Works

All audio is generated as float[] sample buffers at 44,100 Hz mono, then wrapped in AudioClip.Create().

// Example: Kick drum synthesis
static float Kick(float t) {
    if (t > 0.30f) return 0f;
    float phase = 45f * t + (115f / 30f) * (1f - Exp(-t * 30f));
    float body  = Sin(TAU * phase);                    // pitch-dropping sine
    float click = (t < 0.005f) ? WhiteNoise(t) : 0f;  // transient noise
    float env   = Exp(-t * 12f);                       // exponential decay
    return (body + click * 0.6f) * env;
}

// Each track: 16 bars × 4 beats × (60/BPM) seconds × 44100 samples
// = ~300,000 to 400,000 samples per track

Building blocks: Kick(), Snare(), HiHat(), Bass(), SynthArp(), FilteredSaw(), WhiteNoise(), SoftClip()

Each track has 4 sections (A/B/A'/C) where instruments enter and exit, creating dynamic arrangement. Drum fills occur on every 4th bar.

07 The AI Agent Team

This entire game was built by a team of 6 specialized AI agents, each with their own instruction file, responsibilities, and constraints. A single human Business Owner directed the team through natural language prompts.

🎯 Project Orchestrator

The coordinator. Reads SHARED_CONTEXT.md at every session start. Breaks down requests into tasks for other agents. Identifies dependencies. Escalates conflicts to the Business Owner. Never writes code directly.

⚙️ Full Stack Engineer

Owns all C# code: Core systems, networking, build pipeline. Uses GameContext for DI (no singletons). Creates NUnit test stubs for every new class. Must compile before completing.

🎨 UI/UX & Gameplay

Owns all player-facing UI and "game feel." Designs HUD, screens, animations. WCAG AA contrast. TextMeshPro only. Animations ≤2s. Priority: Players > Attacks > Lives > Score.

🧪 QA Engineer

Owns test plans and NUnit suites. Coverage targets: Core 90%, BR 85%, Network 70%, UI 60%. Writes regression tests for every bug fix. Must sign off before any feature is "done."

📋 Product Manager

Owns the backlog, user stories, and acceptance criteria. Uses MoSCoW prioritization. Protects scope — no feature creep without removing something. All work tracked in PRODUCT_BACKLOG.md.

🎮 Gameplay Tester

Reviews screenshots and logs, scores game feel on 5 dimensions (Responsiveness, Clarity, Fairness, Tension, Juice). Files structured issue reports. Cannot execute the game directly.

How Agents Are Configured

Each agent is defined by a .instructions.md file in .github/instructions/. These files use YAML frontmatter with an applyTo glob pattern that tells VS Code Copilot when to load each agent's rules:

# File: .github/instructions/fullstack-engineer.instructions.md
---
applyTo: "Assets/Scripts/Core/**,Assets/Scripts/BattleRoyale/**,Assets/Scripts/Networking/**"
---

# Agent: Full Stack Engineer

## Identity
You are the Full Stack Engineer for Pac-Man Battle Royale...

## Constraints
- Never use FindObjectOfType (use GameContext injection)
- All public API must have XML doc comments
- Must create stub NUnit test for every new class
...

The workspace-level .github/copilot-instructions.md file ties everything together, defining the team structure and how to invoke specific agents.

Agent Interaction Model

Agent Communication Flow
👤 BUSINESS OWNER Human User — Final Decisions natural language 🎯 PROJECT ORCHESTRATOR Reads SHARED_CONTEXT.md · Routes Tasks ⚙️ Engineer C# / Systems 🎨 UI/UX Screens / Feel 🧪 QA Tests / Sign-off 📋 PM Backlog / Scope 🎮 Tester Feel / Feedback Each agent works within its applyTo scope · All changes update SHARED_CONTEXT.md

08 The Shared Context — Project Memory

SHARED_CONTEXT.md is the single source of truth for the entire project. It's a living document (~350 lines) that every agent reads at the start of every work session.

What's In It

📊 System Status Table

159+ rows tracking every system: who owns it, what status it's in (Done/In Progress/Not Started), and when it was completed.

🏗️ Architecture Decisions

Tech stack, design rules (grid-locked movement, input buffering, no singletons), and the ScriptableObject maze pattern.

📝 Session Log

Chronological record of every work session: what was built, bugs found, decisions made. This is how agents "remember" across conversations.

🎯 Milestone Tracker

6 milestones from single-player core to polish & ship, with dates and completion status.

Why It Works

AI agents don't have persistent memory between conversations. SHARED_CONTEXT.md solves this by acting as an external memory bank:

  1. Session start: Orchestrator reads the file. Knows what's done, what's blocked, what's next.
  2. During work: Agents reference it for architecture decisions, coding conventions, and status.
  3. Session end: Orchestrator updates the file with new completions, blockers, and decisions.

This creates a continuous thread of context that survives across dozens of separate AI conversations.

09 How We Prompted — Real Examples

Here are actual prompts the Business Owner sent to build this game. Notice how they're high-level and outcome-focused — the agents figure out the implementation details.

Feb 21, 2026 — Session 1
"Act as the Project Orchestrator. Open today's session."
The very first prompt. The orchestrator read SHARED_CONTEXT.md, reported milestone status, and proposed the first work items.
Feb 22 — Core Gameplay
"Build the maze renderer, Pac-Man movement with input buffering, and ghost AI with 4 personalities."
One prompt produced the entire single-player core: MazeRenderer, GridMover, PacmanController, GhostStateMachine, PelletManager.
Feb 25 — Battle Royale
"Add the battle royale layer: attack system with combo chains, targeting modes, and elimination tracking."
AttackSystem, TargetingSystem, and EliminationTracker were built and wired. Combo scoring (200→400→800→1600) was implemented.
Feb 28 — Local 4P
"Make it work for 4 players on one keyboard with different control schemes."
LocalMultiPlayerLoop created with rotated input for top-side players. HUDs per player. Join system with per-key activation.
Mar 1 — Online
"Add online multiplayer using FishNet and Bayou WebSocket. I want a lobby and server-authoritative gameplay."
NetworkGameManager, OnlineServerGameLoop, OnlineClientRenderer, all broadcast structs. Deployed to TrueNAS Docker.
Mar 5 — Polish
"Make the game feel alive. Add ghost trails, maze glow effects, pellet twinkle, death particles, and score eruptions."
15+ visual effects added in one session. Shard bursts, gradient trails, floating score popups, pulsing power pellets, crown effect for the leader.
Mar 6 — Audio
"Create procedural background music and sound effects. No audio files — everything synthesized from math."
ProceduralMusic.cs (5 tracks initially, later expanded to 10) and ProceduralSFX.cs (6 effects including explosive power pellet sound).
Mar 7 — Docs
"Create documentation on a web page that describes how this game works... I want rich diagrams... explain how someone could replicate what we did."
This very page you're reading now. Built by the same AI agent team. 🎉

10 How to Replicate This — Step by Step

Want to build your own AI-agent-driven project? Here's exactly how to set it up.

Step 1: Set Up The Agent Instructions

Create .github/instructions/ in your project root. Add one .instructions.md file per agent role:

.github/
├── copilot-instructions.md          ← workspace-level (always loaded)
└── instructions/
    ├── project-orchestrator.instructions.md  (applyTo: "*")
    ├── fullstack-engineer.instructions.md    (applyTo: "src/**")
    ├── qa-engineer.instructions.md           (applyTo: "tests/**")
    ├── product-manager.instructions.md       (applyTo: "*.md")
    └── uiux-gameplay.instructions.md         (applyTo: "ui/**")

Step 2: Create SHARED_CONTEXT.md

This is your project's persistent memory. Include:

Step 3: Define the Orchestrator's Rules

The orchestrator is the most important agent. Its instruction file should specify:

Step 4: Work in Sessions

1. "Act as the Project Orchestrator. Open today's session."
   → Reads SHARED_CONTEXT.md, reports status, proposes agenda

2. "Build [feature X]."
   → Orchestrator routes to the right agent(s)
   → Agent(s) implement with awareness of project conventions

3. "Run the tests."
   → QA agent executes test suites, reports results

4. "Deploy."
   → Full Stack runs the deploy script

5. Session ends → Update SHARED_CONTEXT.md

Step 5: Key Principles

🔄 Context is Everything

SHARED_CONTEXT.md is the secret sauce. Without it, each AI conversation starts from zero. With it, agents build on weeks of prior work seamlessly.

📏 Constraints > Freedom

Well-defined constraints (use GameContext, no singletons, XML docs) produce better code than vague instructions. Agents thrive with clear guardrails.

🎯 Outcome-Focused Prompts

Say what you want, not how to do it. "Add online multiplayer" beats "Create a NetworkBehaviour class with a FixedUpdateNetwork method."

✅ Definition of Done

Every feature needs clear acceptance criteria. The PM agent ensures nothing is marked "done" without meeting its checklist.

11 How To Do This Better & Faster

Reflecting on what worked, what didn't, and how engineering teams can leverage AI agents to ship even faster.

What Could Have Been More Efficient

1. Parallel Agent Execution

We ran agents sequentially — one conversation at a time. With tools like multiple Copilot Chat windows or Copilot Workspace branches, you could run the Full Stack Engineer and UI/UX agent simultaneously on different parts of the codebase, merging their outputs.

2. Pre-Built ScriptableObject Templates

The maze data, ghost personalities, and game settings could have been defined as ScriptableObject templates upfront, reducing the back-and-forth between agents about data shapes. A "data architecture" phase before coding would have saved time.

3. Continuous Integration from Day 1

We set up the deploy pipeline late (after Milestone 4). Setting up GitHub Actions → build → test → deploy from the first commit would have caught issues earlier and made each session's deploy trivial.

4. More Granular SHARED_CONTEXT Updates

Sometimes the context file got stale between sessions. An automated post-commit hook that appends changed files and test results to SHARED_CONTEXT.md would keep it perpetually current.

5. Better Test-Driven Development

We wrote tests after features. If the QA agent wrote test stubs first (with expected behavior), the Full Stack agent could fill in implementations that pass tests — classic TDD, but with AI agents.

Alternative Tools That Could Accelerate This

🔧 Cursor + Multi-File Editing

Cursor IDE excels at multi-file refactors with its Composer feature. For large structural changes (like the networking rewrite), being able to edit 10 files simultaneously with full project awareness would have been faster.

🔧 Windsurf / Cline / Aider

These tools offer different approaches to AI-assisted coding. Aider in particular works well for git-based workflows where every AI change is a separate commit — perfect for the session-based model we used.

🔧 Claude Projects / ChatGPT Custom GPTs

For the planning and architecture phases, using a dedicated AI with the full SHARED_CONTEXT.md as permanent context (Claude Projects or Custom GPTs) could provide higher-quality architectural decisions without re-reading the file each time.

🔧 GitHub Copilot Workspace

Copilot Workspace provides a plan → implement → review → merge flow that maps perfectly to our agent model. It could replace the manual "open session → route to agent → close session" cycle.

🔧 MCP Servers (Model Context Protocol)

MCP servers could give agents access to live game state (debug data, player counts, crash logs) during development — enabling the Gameplay Tester agent to provide real-time feedback instead of reviewing static screenshots.

🔧 Unity Muse / DALL-E for Assets

We used procedural graphics and audio. For a production game, AI-generated sprites, textures, and music (via Muse, Midjourney, Suno, etc.) could dramatically improve visual quality while maintaining the AI-first workflow.

How Engineering Teams Can Leverage This

The 10x Multiplier Model

The real power isn't replacing engineers — it's giving each engineer a team of AI specialists. One senior engineer + 6 AI agents accomplished in 2 weeks what would traditionally take a small team months.

Recommended Team Structure

Human RoleAI AgentsResponsibility Split
Tech LeadOrchestrator + EngineerHuman sets architecture; AI implements and iterates
DesignerUI/UX AgentHuman wireframes; AI builds responsive UIs
QA LeadQA + Tester AgentsHuman defines test strategy; AI writes and runs suites
PMPM AgentHuman provides priorities; AI maintains backlog

Key Success Factors

  1. Invest in instructions upfront. The time spent writing agent instruction files pays back 100x in consistent output quality.
  2. SHARED_CONTEXT is non-negotiable. Without persistent context, you're throwing away agent knowledge every session.
  3. Define constraints, not procedures. Tell agents what NOT to do, and let them figure out the best way to do what's left.
  4. Review, don't rewrite. Treat AI output like a junior engineer's PR — review and iterate, don't start over.
  5. Automate the boring parts. Deploy scripts, test runners, context updates — automate everything that isn't creative decision-making.

The Bottom Line

This project proves that a single person with well-configured AI agents can ship a real-time multiplayer WebGL game with procedural audio, 37,000+ lines across 130 source files, 447 automated tests, and a full deploy pipeline — with no team, no budget, and no prior Unity experience. The implications for engineering productivity are enormous.

12 Project By The Numbers

0
Lines of Code
0
Source Files
0
AI Agents
0
Tasks Completed
0
Automated Tests
0
Milestones Done
0
Max Players

Lines of Code — Full Breakdown

0
Total Lines of Code

By Language / Category

C# Game Scripts
20,829
79 files
C# Test Suites
6,837
32 files
HTML / Web
5,527
5 files
C# Editor Scripts
2,013
3 files
Markdown / Docs
1,600
5 files
PowerShell
951
5 files
Dockerfile
34
1 file

C# Game Scripts — By Namespace

20,829 Lines of C#
UI
7,852 lines · 28 files
Core
6,594 lines · 31 files
Game
4,584 lines · 11 files
Networking
1,258 lines · 4 files
BattleRoyale
541 lines · 5 files

Additional Metrics

226
Tasks Tracked & Completed
in SHARED_CONTEXT.md
447
Automated Tests
294 EditMode + 76 PlayMode
7
Milestones Complete
all 7 shipped
6,837
Lines of Test Code
35 NUnit test suites

Development Timeline

Feb 21
Project kickoff — agent setup, SHARED_CONTEXT, instruction files
Feb 22
Milestone 1 complete — single-player core (maze, movement, ghosts, pellets)
Feb 23-25
Milestone 2 — Battle Royale layer (attacks, combos, targeting, elimination)
Feb 26-28
Milestone 3 — Local 4-player multiplayer with rotated controls
Mar 1-3
Milestone 4 — Online multiplayer (FishNet + Bayou WebSocket)
Mar 4-6
Milestone 5 — Server-auth rewrite, visual polish, procedural audio (10 tracks + 6 SFX)
Mar 7-17
Milestone 6 — Automated play testing (bot AI, headless runner, CI pipeline)
Mar 7-17
Milestone 7 — Next-Gen UI: neon arcade theme, 128px sprites, attract mode, victory celebration
Mar 21
Full QA pass — 447 automated tests confirmed green (0 failures)

13 Testing & Quality Assurance

The project has 447 automated tests across 35 NUnit test files (6,837 lines of test code). Every system, from maze generation to online networking, has dedicated test coverage. Tests run in Unity's batchmode test runner and gate the deploy pipeline.

0
EditMode Tests
0
PlayMode Tests
0
Failures
0
Test Files
0
Lines of Test Code

How Tests Are Built

🧪 EditMode Tests (294 tests, 21 files)

Run without starting the Unity game loop. Test pure logic, data structures, math, and configuration. Use [Test] attributes and mock dependencies. Instantiate components via new GameObject().AddComponent<T>() with manual wiring — no scene required. Fast, deterministic, no frame waits.

🎮 PlayMode Tests (76 tests, 11 files)

Run inside the Unity runtime with a full game loop. Use [UnityTest] coroutines that yield frames (yield return null) to test real component lifecycles, Start()/Awake() execution, physics, rendering, and time-based behavior. Used for integration tests and visual effects validation.

🤖 Automated Play Testing (M6)

A full headless game session runner (PlayTestRunner) boots 1–4 AI bot players (BotPacmanController with BFS pathfinding + ghost avoidance), runs configurable rounds, and outputs structured JSON results. PlayTestInvariantValidator checks 6 game invariants. Run-PlayTests.ps1 gates the deploy pipeline.

🔧 Test Framework & Tooling

All tests use NUnit 3.x via Unity Test Framework. Two assembly definitions: PacmanRoyale.Tests.EditMode and PacmanRoyale.Tests.PlayMode. Tests run in Unity batchmode (-runTests -testPlatform EditMode|PlayMode). Results output as NUnit XML. CI script parses XML and JSON for pass/fail gating.

What Tests Cover

SystemTest FileTypeTestsWhat It Validates
Maze GenerationMazeLayoutTests.csEditMode~12Tile access, bounds, wall detection, passability queries
Classic Maze DataClassicMazeDataTests.csEditMode~1428×31 grid integrity, dead-end regression guard, pellet/wall counts
Pellet SystemPelletManagerTests.csEditMode~16Collection, repopulation, power pellet events, Reinitialise()
Ghost AIGhostNavigationTests.csEditMode~15Direction choice, Blinky/Pinky/Inky/Clyde targeting, ghost-house blocking
Ghost State MachineGhostStateMachineTests.csPlayMode14State transitions, frightened/dead speed, sprite hide/show, house exit bugs
Ghost Release ConfigGhostReleaseConfigTests.csEditMode6Spawn tiles inside house, exit tile correctness, no spawn==exit
Player StatePlayerStateTests.csEditMode~8Score add, lives decrement, death tracking
Elimination TrackerEliminationTrackerTests.csEditMode~14Registration, death recording, alive count, winner detection
Attack & ComboAttackSystemTests.csEditMode~12Combo step escalation (200/400/800/1600), reset on power expire
Targeting SystemTargetingSystemTests.csEditMode~18Mode cycling (KO/Attacker/Random/Manual), OnModeChanged events
Input ProfilesInputPlayerProfileTests.csEditMode~9Control scheme assignment, rotated input for P3/P4
Local MultiplayerLocalMultiPlayerLoopTests.csEditMode14BeginGame bitmask, game-over detection (1p/multi/draw), OnGameOver events, reset
Grid MovementGridMoverTests.csPlayMode~10Tile-to-tile movement, input buffering, frozen state, tunnel wrap
Pac-Man ControllerPacmanControllerTests.csPlayMode4Die hides sprite, IsAlive false, respawn re-enables sprite, IsAlive true
Audio SystemAudioManagerTests.cs (Edit) + PlayModeBoth20Init, BGM play/pause, SFX trigger, volume prefs persistence
Victory FanfareVictoryFanfareTests.csEditMode10Clip generation, sample rate, mono, duration, non-silent, idempotent
Visual EffectsVisualEffectsTests.csPlayMode7Death effect spawn/destroy, pellet effect normal/power, self-destruct timing
Visual PolishVisualPolishTests.csPlayMode11Maze glow, border FX brackets, combo popup, flash effect, players-remaining HUD
Visual Features (S2)VisualFeaturesTests.csEditMode30+Season-2 effects, bug fix regressions (HUD hide, spawn tiles, pellet reinit)
Power Pellet EffectsPowerPelletEffectsTests.csPlayMode7Timer HUD show/hide/color, PowerActivateEffect spawn/destroy/ring children
HUD & Screen ShakeHudAndShakeTests.csPlayMode4Targeting label update, ScreenShake activation
Polish SystemsPolishSystemTests.csPlayMode1PelletPulse sine wave modulation
Online LobbyOnlineLobbyDefaultsTests.cs + OnlineLobbyScreenTests.csBoth15Default server/room values, scheme stripping, Awake prefill behavior
Online MultiplayerOnlineMultiplayerTests.cs + OnlineNameTests.csEditMode25+Frozen state, NetworkControlled, spawn tiles, ForceRemoveTile, visual states
Network SessionNetworkSessionStubTests.csEditMode~7INetworkSession stub contract, options, session lifecycle
M7 Neon UIM7VisualTests.csEditMode38NeonAmbientBg, NeonScreenTransition, HighResSprites, ArcadeHudChrome, AttractMode, CabinetBorder, EnhancedParticles, PowerUpSprites
Bot AIBotPacmanControllerTests.csEditMode14BFS pathfinding, ghost avoidance, aggression levels, null safety
Play Test InvariantsPlayTestInvariantTests.csEditMode18Validator logic: no exceptions, scores valid, lives valid, game-over reached
Play Test RunnerPlayTestRunnerTests.csPlayMode7Scene build, maze integrity, bot instantiation, session completion

What Tests Don’t Cover

The test suite is comprehensive for game logic but has known gaps in the following areas:

🌐 End-to-End Online Networking

No tests exercise a full FishNet server + WebSocket client connection. Online tests validate data structures, state flags, and broadcast payloads in isolation — but not actual packet send/receive over a real transport. Verifying online play requires manual testing with the deployed server.

🖥️ WebGL-Specific Behavior

Tests run in the Unity Editor (Windows). WebGL-specific issues (IL2CPP stripping, browser JS interop via pacman-webgl.jslib, Brotli decompression, mixed-content HTTPS restrictions) are not exercised by the test suite and require manual browser testing.

📐 Visual Pixel-Accuracy

Tests confirm that visual-effect GameObjects are created and self-destruct on schedule, but do not perform screenshot comparison or pixel-level rendering validation. Visual regressions (glitched sprites, wrong colors, z-fighting) require human review or a future screenshot-diff pipeline.

⏱️ Timing-Sensitive Edge Cases

Two tests are inconclusive due to frame-timing sensitivity: DeathEffect_SelfDestructs_AfterDuration (PlayMode) and ChooseDirection_BlocksGhostHouseWhenFlagFalse (EditMode). These pass intermittently but depend on exact frame timing that varies by machine load.

📱 Mobile / Touch Input

No tests cover mobile swipe controls or touch input. US-20 (mobile swipe controls) is in the backlog but not started. All input tests assume keyboard schemes (WASD, Arrows, IJKL, Numpad).

🔊 Audio Output Verification

Audio tests validate that AudioManager creates clips, sets volumes, and persists preferences. They do not verify that sound actually plays through speakers. Procedural audio waveform correctness is tested (non-silent, valid AudioClip) but not perceptual quality.