Metal Gear Mechanics
A recreation of Metal Gear Solid's stealth loop in Unity: guards patrol fixed routes, notice the player through a vision cone confirmed by line of sight, and escalate through scan and search states before returning to patrol.
my role
Solo project. I wrote all of the gameplay code: the AI controller, the item and inventory systems, the manager layer, scene loading, UI, and utilities such as the object pooler. Environment art, particle effects and the PS1-style post-processing are third-party asset packs.
the problem
Proximity detection is the hot path in any stealth game. Every guard has to ask "what is near me" every frame, and Unity's convenience overloads like Physics.OverlapSphere return a freshly allocated array on every call. With N guards running that each frame, allocation scales linearly with agent count and the garbage collector eventually stalls the frame. The goal was detection that stays flat in allocation no matter how many agents are active.
approach
- 01A single static readonly Collider[16] buffer shared across every agent instance, rather than one buffer per agent.
- 02Physics.OverlapSphereNonAlloc writes candidates into that buffer and returns a count, so no array is allocated per query.
- 03A layer mask restricts the query to the controller layer, so the physics engine never tests irrelevant colliders.
- 04Vision cone test via Vector3.Angle against the agent's forward vector, so candidates outside the field of view are rejected before any raycast.
- 05Physics.Raycast confirms line of sight only for candidates that survive the cone test, keeping the expensive check off the common path.
- 06A three-phase state machine (scanRan, searchRan, searchPOI) drives behaviour, with NavMesh.SamplePosition and NavMeshAgent.SetDestination handling pathing between waypoints.
trade-offs
- 01The shared buffer is capped at 16 colliders. Beyond that, additional overlaps in range are silently dropped. For guard-density stealth gameplay that ceiling is never reached, but it is a hard cap rather than a graceful degradation.
- 02Because the buffer is static and shared, detection cannot be moved onto worker threads without reworking it. The zero-allocation property is bought with single-threaded access.
- 03Detection runs from Update on every agent rather than on a staggered timer, so cost scales with agent count every frame rather than being amortised across frames.
measured results
Unity 2022.3 editor, RTX 4080 SUPER / Ryzen 7 9800X3D. Measured from AIController.Update in the Unity Profiler hierarchy. Per-agent cost converges to roughly 0.015 ms and stays flat, so detection scales linearly while allocation stays at exactly zero across a ~97x range. At 584 agents the AI accounted for 21% of a 41.28 ms frame; the bottleneck at that point was skinned mesh rendering, not detection.