Unity gameplay systems (gameplay systems programmer)

SkillAI & models

Use when building or debugging Unity gameplay systems: enemy AI that patrols, chases and attacks, NavMesh baking and agents (AI Navigation 2), feet sliding on agents, state machines or behavior trees, physics projectiles that tunnel, the physics spiral, layers and the collision matrix, CharacterController vs Rigidbody characters, object pooling, 'hundreds of enemies', DOTS, Burst or Jobs decisions, Netcode for GameObjects multiplayer, audio mixers, snapshots, volume sliders and 3D combat sounds.

Available today. Use it from your connected AI after setup.

Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.

Then ask your AI: use the Unity gameplay systems (gameplay systems programmer) skill

What this skill tells your AI

The instructions your AI receives, as published by scenario-labs/skills in skills/game-engines/unity/scenario-unity-gameplay/SKILL.md and read by ahel’s review.

Expert gameplay work in Unity 6.3 means every system has one owner of its state, runs at a rate someone chose, and is proven by a measurement: steps per frame, a tunneling test at the real speed, a path query through the real door, a timed transition, the worst frame at the real agent count. This skill carries the expert delta from the 6.3 manuals, AI Navigation 2, NGO and shipped games (Survival Kids, Very Very Valet), with jobs and tests run in 6000.3.21f1. If a sibling skill named here is missing from your available skills, ask the user to install it (npx skills add scenario-labs/skills --skill <name>); unattended, proceed from tool schemas and flag the gap.

REQUIRED BACKGROUND: scenario-unity-expert (channels, review loop, 6.3 traps). Install: ut_gameplay.install(P) (core AgentKit + AgentKit.Gameplay jobs + the AgentKit.Gameplay.Runtime assembly).

Stance (the expert delta)

  1. Fix frequency before colliders. Physics cost multiplies with fixed steps per frame; "a call count close to 10 might indicate an issue" (6.3 Manual). Measured at 50 Hz: a 400 ms frame ran 17 catch-up steps with the default 0.333 s Maximum Allowed Timestep; a 0.1 s cap gave 5 steps and a world at 0.25x. The cap trades the snowball for slow motion.
  2. Escalate CCD per object on observed tunneling, then leave rigid bodies. Discrete, Continuous Speculative ("often the best choice"), Continuous, Continuous Dynamic last (6.3 Manual). Measured: every CCD mode held a static 5 cm wall to 300 m/s; against a thin dynamic plate Continuous tunneled from 50 m/s, Continuous Dynamic from 100. A sweep of the projectile's own shape (SweepProjectile) stopped the 300 m/s shot, and a 0.25 m sphere sweep hit a post a center ray missed: match the cast to the collider (LlamAcademy, fJyi7l2tWKo [00:09:54]).
  3. One writer per piece of state. Agent plus non-kinematic Rigidbody is a race, agent plus enabled obstacle avoids itself, agent and animation must flow one way (AI Navigation manual). Measured: a main-thread write to a transform held by a running TransformAccessArray job waited the whole job (22 to 38 ms, vs 0.003 ms outside it) (Survival Kids, ZkvK0mX-id4 [00:26:42]).
  4. A NavMesh is proven by queries and saved as an asset. One NavMesh Surface per agent type (SMWxCpLvrcc [00:06:35]); voxel 3 per agent radius, 4 to 6 indoors (manual). Measured: a door 1.2x the agent diameter was closed at 3 voxels per radius, open at 6; stairs without a HeightMesh read 0.17 to 0.26 m off the treads, 0 with it; a carve reached queries one frame later.
  5. The lightest AI structure, ticked from one place. Unstructured for 1 to 2 behaviors, FSM while transitions stay few (toward N squared), behavior tree for priorities, GOAP for chained goals (LlamAcademy, CZvfuNfdc1M [00:07:13]). Brains tick from a director; mixed object types register with one UpdateHub sorted by type (Survival Kids: same code, mean -12%, worst frame 2.18 to 1.43 ms [00:23:59]; measured here at 1,000 and 10,000 objects: mean 7 to 14% lower).
  6. DOTS is an umbrella; measure before adopting it. Order updates, then Burst jobs over NativeArrays with transforms copied out in a tiny read-only job, then fill main-thread job waits with Burst .Run() (measured: 6 to 10 ms saved of 21 to 26), and only then Entities (Survival Kids [00:08:49], [00:39:15]). Decide ECS early, for large CPU-driven counts, competitive netcode or determinism (Turbo Makes Games, Bz24Jp30nkM [00:21:58]).
  7. NGO 2.x has no full client-side prediction and reconciliation (no rollback-and-replay loop) and no server rewind. Client anticipation (AnticipatedNetworkVariable, AnticipatedNetworkTransform, OnReanticipate) is the building block for hand-built prediction (NGO 2.7 manual; same text in the bundled 2.13.0 docs). Server authority by default, HasAuthority over IsServer, "would a late joiner need it?" decides NetworkVariable vs RPC.
  8. Every sound goes through a mixer group; distance happens at the source. Output None (the default) bypasses the mixer; attenuation and distance low-pass happen at the AudioSource before any group (manual). Measured: Logarithmic rolloff kept attenuating past Max Distance (1/d to 40 m), a slider on a snapshot parameter froze that group's moods until ClearFloat, and 60 uncapped looping weapon sounds erased a quiet music bed at default priority. Cap frequent sounds at 10 to 15 and steal the oldest (git-amend, BgpqoRFCNOs [00:17:56]).

Establish first

Ask once: genre and whether play is competitive; platforms and frame budget (default 16.67 ms desktop, mobile x 0.65, scenario-unity-mobile); peak agents, projectiles and sounds; whether the player pushes and is pushed by physics; animation-driven or agent-driven locomotion; multiplayer (players, host leaving, rollback or hit rewind); existing layers, mixer and scenes; 2D or 3D (scenario-unity-2d). Defaults: 3D URP, 50 Hz, layers Environment 8, Player 9, Enemy 10, Projectile 11 with Projectile x Projectile and Enemy x Enemy off, Humanoid agent type (radius 0.5), code FSM enemies at 10 Hz, one mixer Master > Music, SFX, UI.

Workflow

  1. Project physics. GameplaySetup.ConfigurePhysics (layers, Physics.IgnoreLayerCollision, Time and Physics through SerializedObject). GATE: ReadPhysicsSettings in a second process shows the matrix, 0.02 s and the cap (measured: Time.* setters plus SaveAssets did not persist).
  2. Navigation. GameplayScenes.BuildNavArena pattern: surface layerMask = Environment, voxel = radius/3, BuildNavMesh() then AssetDatabase.CreateAsset(surface.navMeshData, ...) as the Inspector Bake button does, save. Build Height Mesh when agents use stairs; NavMesh Modifier Remove Object on foliage; Collect Objects = Volume or Modifier-only to scope. GATE: CalculatePath PathComplete through every door, SamplePosition on spawns and treads, data present after reopening, scene still text; overlay capture opened.
  3. Characters and locomotion. CharacterController when physics interplay is not in the design (skin >= max(0.01, 10% radius)); FloatingCapsule when it is. Agent locomotion with AgentLocomotionSync: agent drives (root motion off, speed from deltaPosition) or animation drives (updatePosition = false, 0.9 pull beyond the radius). GATE: 0 cc.* audit errors; ControllerTests numbers; drift test.
  4. AI. EnemyBrain + EnemyDirector; perception cheapest first, then one ray on an occluder mask with QueryTriggerInteraction.Ignore, batched in LineOfSightBatch (RaycastCommand with QueryParameters) at scale; agent off before obstacle on death. GATE: EnemyLoopTests times Patrol > Chase > Attack > Search > Patrol > Dead.
  5. Projectiles, queries, pools. Rigid projectiles: CCD by a tunneling test at the real speed and thickness; fast or fat shots: SweepProjectile. NonAlloc through Queries (loop on the count, flag a full buffer, sort when order matters); Physics.SyncTransforms() before querying colliders moved by transform. ProjectilePool with collectionCheck. GATE: CcdLadderMeasured and QueryTests at your numbers; CountActive back to 0.
  6. Audio. Mixer from GameplayAudio.CreateCombatMixer or a template; every source routed; CombatAudio capped emitters; sliders on a parameter no snapshot drives; music priority 0; distance muffling with an AudioLowPassFilter curve. GATE: AudioTests, AudioSpatialTests (music band under spam), 0 audio.* errors; a human listening pass at caps 30, 15, 5 under stress, keeping the smallest that sounds full (BgpqoRFCNOs [00:17:23]).
  7. Scale. BuildCrowdScene at the real count, AgentProfile.PlayModeTimings, AIBudgetMeter, JobWaitMeter; PerceptionBenchmark, ProximityBenchmark, dots_gate and proximity_gate before any DOTS proposal. GATE: p95 AND the worst frame; editor numbers confirmed in a development player with Profile Analyzer and the native device profiler (Survival Kids [00:10:27]; scenario-unity-performance).
  8. Netcode scoping (multiplayer only). ut_gameplay.netcode_fit, authority and tick sheet, prediction proven in Edit Mode with NetSim before NGO wiring. GATE: reconciliation tests green; the design says what must be hand-built.

Numbers

ValueRelative toSource
0.02 s (50 Hz); 1/60 for one step per 60 fps frameFixed TimestepManual; pTz3LMQpvfA [00:01:22]
0.333 s default (about 16 steps at 50 Hz); 0.1 s = 5 stepsMaximum Allowed Timestepmeasured
near 10 calls per frame = problemPhysics.Processing CallsManual
Discrete tunnels above about (thickness + 2r + 0.02) / dt50 Hzdiscrete_tunnel_speed; measured 20 m/s, 5 cm wall
skin >= max(0.01, 0.1 x radius); step 0.1 to 0.4 m; min move 02 m CharacterControllerManual
sag = m x g / k; x2 acceleration on reversalsfloating capsulemeasured; Toyful [00:05:32]
3 voxels per radius; 4-6 interiors; >8 rarely helps; door >= about 1.5x agent diameteragent radiusmanual; measured
drift pull 0.9 x delta when drift > agent radius (measured max 0.41 to 0.45 m at r 0.4, 1.84 m without)animation-driven agentAI Navigation manual
carve seen next frame; Carve Only Stationary re-carves after 0.5 s still (measured 0.50 to 0.61 s)NavMesh Obstaclemanual; measured
AI slice 0.10-0.15 ms (Update) vs about 0.01 ms (10 Hz director)200 agents, Editormeasured
Burst parallel slower than managed at 200 agents, faster from 2,000perception prefiltermeasured
brute force 7-8 ns managed, 1.2-1.5 ns Burst per item-query pair; grid 0.3-1.3 ms vs 14-15 ms at 10,000 x 200proximity queriesmeasured (Survival Kids KD tree: 5 ms to 0.08 ms, 20 queries over 1,000 items)
200 ms noticed; FPS < 100 ms; 30 Hz tick; update rate > fire ratenetcodeNGO manual
32 real / 512 virtual voices; priority 0-256, default 128Audio settingsmeasured; manual
gain = min/d past Max Distance (Logarithmic); 0 at Max Distance (Linear)3D rolloffmeasured
low-pass cutoff curve 22000 to 10 Hz, needs an Audio Low Pass Filterdistance mufflingManual; measured 8 kHz at 8 m: 0.03x
slider 0.0001..1 -> 20 log10(v); 10-15 instances per frequent soundmixer, voicesDU7cgVsU2rM [00:14:30]; BgpqoRFCNOs [00:17:56]

Quality gates

  • Measurable: compile_errors == []; settings read back in a new process; AuditGameplayScene 0 errors (the negative control flags every seeded trap); suites green with GAMEPLAY_METRICS parsed; CCD and sweep tests at the real speed; pools back to baseline; crowd CSV at the real count with p95, worst frame and AI slice; code_audit 0 errors.
  • Visual: NavMesh overlay and crowd captures pass ut_review.image_checks and were opened; controller and locomotion feel judged from frames when feel is the brief.

Common mistakes

MistakeWhat it looks likeFix
Continuous Dynamic on every fast bodyCPU grows, still tunnels a dynamic plateSpeculative first; SweepProjectile
thin ray for a fat projectilemisses what its radius touchessweep the collider's shape (Queries.SweepShape)
NonAlloc loop over the bufferstale or silently dropped hits, unorderedloop on the count; flag count == length; sort
Physics.autoSyncTransforms = truehidden sync before every query, CS0618one Physics.SyncTransforms() before the query
Time.maximumDeltaTime = x in an editor jobback to 0.333 next launchSerializedObject on TimeManager.asset
BuildNavMesh() then save the scenedata embedded, scene file turns binaryAssetDatabase.CreateAsset(surface.navMeshData) first
querying after saving over an existing sceneNavMesh empty, paths invalidGameplayScenes.EnsureLoaded
stairs without a HeightMeshagents glide up a rampBuild Height Mesh, rebake
repath in the frame an obstacle carvesroute through the new bodyquery next frame
agent and animation both move the characterfoot sliding, jitterone way: AgentLocomotionSync
static singleton, domain reload offlast session's hub, double updatesreset in SubsystemRegistration; SafeUnregister
TransformAccessArray from FindObjectsByType<Transform>frame cost grows with the sceneown list, rebuild on change
writing TAA transforms while the job runsmain thread stalls for the jobcopy out read-only; write after Complete()
triggers for "anything near me" at scaletransform sync on every moverown list, Burst, then grid (proximity_gate)
MonoBehaviour in a file with another namesilently absent after a scene saveone MonoBehaviour per file
PlayClipAtPoint for combat soundssliders do nothingpooled emitters routed to a group
low-pass "distance" effect on the SFX groupevery sound muffled alikeAudioLowPassFilter curve per source
trusting Max Distance to silence a Logarithmic sourcestill audible far away (1/d)Linear or custom curve, or cull by distance
"NGO has client prediction" in a planpromised loop does not existanticipation plus hand-built reconciliation, or Netcode for Entities

Handoffs

  • Receives: code structure, services and Input System actions from scenario-unity-architecture; levels on the Environment layer from scenario-unity-world-building; animated characters from scenario-unity-animation; frame budgets from scenario-unity-performance.
  • Delivers: locomotion velocity, state parameters and the chosen coupling to scenario-unity-animation; hit events to scenario-unity-vfx; health and slider hooks to scenario-unity-ui (MixerControl.SetUserVolume); crowd CSVs, AI slice and job waits to scenario-unity-performance; 2D physics to scenario-unity-2d; audio limits to scenario-unity-mobile and scenario-unity-web. Packet: scene, job ids, audit JSON, test metrics, CSV verdicts, Verified and Assumed.

Unity 6.3 notes

  • 3D physics: linearVelocity, linearDamping, PhysicsMaterial, Physics.simulationMode; Physics.autoSyncTransforms deprecated (not just off); RaycastCommand takes QueryParameters; Physics.BakeMesh(int...) obsolete.
  • AI Navigation 2.0.14: bake per NavMesh Surface (no Bake in the Navigation window); OffMesh Link deprecated.
  • Entities 1.4.8 (core from 6.4); 6.5 removes Entities.ForEach; 6.6 makes Burst built in and new projects skip domain reload on Play.
  • NGO 2.13.0 bundled, 1.x deprecated; [Rpc(SendTo.Server)]; override OnUpdate, not NetworkTransform.Update; Multiplayer Services replaces Lobby and Relay.

References

Signals

GitHub stars
681
Forks
82
Last commit
Sep 2026
Advanced
Item type
skill
Key
scenario-unity-gameplay
Source
github.com/scenario-labs/skills