The numbers that matter.

Performance observations gathered during development with Visual Studio diagnostics. The fixed-timestep loop runs at 60Hz. NPC count and behaviour type are the primary variables affecting CPU load during gameplay.

60 Hz Fixed timestep

All entity updates run at a fixed 60 updates per second. The game loop decouples update from render, ensuring consistent behaviour tick rates regardless of frame time variation.

O(1) Behaviour swap cost

Swapping NPC behaviour via setBehaviour(unique_ptr) is a pointer assignment. No iteration, no allocation during runtime swap. Ownership transfer is immediate via move semantics.

0 Memory leaks

Exclusive use of std::unique_ptr throughout - NPCBehaviour, SpecialEntity, Player, Menu, PhaseManager. Automatic cleanup on all ownership paths. No raw owning pointers.

What would need to change at scale.

The current implementation is well-suited to the game's NPC population. The two areas that would not scale are the proximity checks and the texture-swap animation system.

Proximity Checks

checkNPCsNearHatWearers() and checkNPCsNearSpecialEntity() both iterate all NPCs each frame. At current counts this is fine. At 500+ NPCs a spatial grid partitioning approach would be required to maintain performance.

Current: O(n²) · Scalable fix: spatial hash grid

Texture Swap Animation

Visual state changes (default → hat → scared → angry → screaming) are handled by reloading a texture from disk and reinitialising the Animation object. Acceptable at current NPC count, but a single sprite sheet with row selection would eliminate disk reads entirely.

Current: disk reload per state change · Fix: multi-row sprite sheet

Game Class Size

Phase-specific logic like applyHatsToNPCs() currently lives in Game.cpp. As noted in the project report, this logic should be delegated to the PhaseManager or a dedicated phase handler to keep the game class clean regardless of how many phases are added.

Current: in Game.cpp · Fix: delegate to PhaseManager