@ericrisco/rsc 0.1.35 → 0.1.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/manifest.json +241 -2
- package/package.json +1 -1
- package/scripts/lib/domains.js +1 -0
- package/scripts/lib/recommend.js +4 -0
- package/skills/game-design/SKILL.md +170 -0
- package/skills/game-design/evals/README.md +13 -0
- package/skills/game-design/evals/cases.yaml +47 -0
- package/skills/game-design/references/mechanics-and-loops.md +78 -0
- package/skills/game-design/references/playtesting-and-scope.md +58 -0
- package/skills/game-design/references/progression-and-economy.md +70 -0
- package/skills/game-storytelling/SKILL.md +159 -0
- package/skills/game-storytelling/evals/README.md +28 -0
- package/skills/game-storytelling/evals/cases.yaml +51 -0
- package/skills/game-storytelling/references/branching-and-state.md +134 -0
- package/skills/game-storytelling/references/dialogue-tools.md +117 -0
- package/skills/game-storytelling/references/environmental-storytelling.md +71 -0
- package/skills/gamedev-multiplayer/SKILL.md +250 -0
- package/skills/gamedev-multiplayer/evals/README.md +12 -0
- package/skills/gamedev-multiplayer/evals/cases.yaml +44 -0
- package/skills/gamedev-multiplayer/references/godot-high-level-multiplayer.md +133 -0
- package/skills/gamedev-multiplayer/references/prediction-and-latency.md +104 -0
- package/skills/gamedev-multiplayer/references/unity-and-unreal-netcode.md +166 -0
- package/skills/gamedev-pathing/SKILL.md +242 -0
- package/skills/gamedev-pathing/evals/README.md +14 -0
- package/skills/gamedev-pathing/evals/cases.yaml +49 -0
- package/skills/gamedev-pathing/references/navmesh-workflow.md +110 -0
- package/skills/gamedev-pathing/references/search-algorithms.md +135 -0
- package/skills/gamedev-pathing/references/steering-and-avoidance.md +120 -0
- package/skills/gamedev-physics/SKILL.md +225 -0
- package/skills/gamedev-physics/evals/README.md +12 -0
- package/skills/gamedev-physics/evals/cases.yaml +54 -0
- package/skills/gamedev-physics/references/character-controllers.md +120 -0
- package/skills/gamedev-physics/references/determinism-and-queries.md +124 -0
- package/skills/gamedev-physics/references/layers-and-masks.md +115 -0
- package/skills/gamedev-shaders/SKILL.md +252 -0
- package/skills/gamedev-shaders/evals/README.md +14 -0
- package/skills/gamedev-shaders/evals/cases.yaml +45 -0
- package/skills/gamedev-shaders/references/effect-recipes.md +205 -0
- package/skills/gamedev-shaders/references/godot-shading-language.md +133 -0
- package/skills/gamedev-shaders/references/unity-and-unreal-shaders.md +169 -0
- package/skills/gamedev-shipping/SKILL.md +236 -0
- package/skills/gamedev-shipping/evals/README.md +9 -0
- package/skills/gamedev-shipping/evals/cases.yaml +55 -0
- package/skills/gamedev-shipping/references/mobile-and-desktop.md +127 -0
- package/skills/gamedev-shipping/references/stores-and-ci.md +162 -0
- package/skills/gamedev-shipping/references/web-export-headers.md +140 -0
- package/skills/godot/SKILL.md +319 -0
- package/skills/godot/evals/README.md +13 -0
- package/skills/godot/evals/cases.yaml +50 -0
- package/skills/godot/references/export-and-testing.md +102 -0
- package/skills/godot/references/gdscript-style.md +84 -0
- package/skills/godot/references/godot3-to-4-traps.md +89 -0
- package/skills/godot/references/nodes-scenes-resources.md +109 -0
- package/skills/level-design/SKILL.md +175 -0
- package/skills/level-design/evals/README.md +35 -0
- package/skills/level-design/evals/cases.yaml +58 -0
- package/skills/level-design/references/blockout-to-art.md +118 -0
- package/skills/level-design/references/guidance-and-readability.md +152 -0
- package/skills/level-design/references/pacing-and-encounters.md +142 -0
- package/skills/roast-me/tools/adapters/__pycache__/__init__.cpython-312.pyc +0 -0
- package/skills/roast-me/tools/adapters/__pycache__/base.cpython-312.pyc +0 -0
- package/skills/roast-me/tools/adapters/__pycache__/claude.cpython-312.pyc +0 -0
- package/skills/roast-me/tools/adapters/__pycache__/codex.cpython-312.pyc +0 -0
- package/skills/roast-me/tools/adapters/__pycache__/gemini.cpython-312.pyc +0 -0
- package/skills/roast-me/tools/adapters/__pycache__/registry.cpython-312.pyc +0 -0
- package/skills/unity/SKILL.md +282 -0
- package/skills/unity/evals/README.md +8 -0
- package/skills/unity/evals/cases.yaml +43 -0
- package/skills/unity/references/build-and-platforms.md +65 -0
- package/skills/unity/references/input-system.md +102 -0
- package/skills/unity/references/lifecycle-deep-dive.md +75 -0
- package/skills/unreal/SKILL.md +270 -0
- package/skills/unreal/evals/README.md +12 -0
- package/skills/unreal/evals/cases.yaml +45 -0
- package/skills/unreal/references/blueprint-cpp-parity.md +109 -0
- package/skills/unreal/references/gameplay-framework.md +95 -0
- package/skills/unreal/references/packaging.md +83 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Prediction, reconciliation & latency hiding
|
|
2
|
+
|
|
3
|
+
The whole reason netcode is hard: the speed of light. A round trip (RTT) of 60–150 ms is normal, and if the
|
|
4
|
+
owning client had to wait a full RTT to see its own movement, the game would feel broken. The techniques
|
|
5
|
+
below keep it responsive **without** giving up server authority. This is engine-agnostic theory; the
|
|
6
|
+
per-engine references show where the hooks live.
|
|
7
|
+
|
|
8
|
+
## The authoritative loop (mental model)
|
|
9
|
+
|
|
10
|
+
1. Client samples input each tick, tags it with a **sequence number**, sends it to the server, and **applies
|
|
11
|
+
it locally immediately** (prediction).
|
|
12
|
+
2. Server receives inputs, simulates them on its fixed tick, and produces authoritative state. It stamps each
|
|
13
|
+
state snapshot with the last input sequence number it processed for that client.
|
|
14
|
+
3. Client receives the authoritative snapshot, **reconciles**: snap to the server state, then re-apply every
|
|
15
|
+
input *after* the acknowledged sequence number (they haven't reached the server yet).
|
|
16
|
+
4. Other (remote) entities are rendered from buffered snapshots, **interpolated** slightly in the past.
|
|
17
|
+
|
|
18
|
+
Server is always the source of truth. The client only ever *guesses ahead* and gets corrected.
|
|
19
|
+
|
|
20
|
+
## 1. Client-side prediction
|
|
21
|
+
|
|
22
|
+
The owning client runs the same movement code the server will run, on the same fixed timestep, the instant it
|
|
23
|
+
presses a key — no waiting. Requirements:
|
|
24
|
+
|
|
25
|
+
- **Deterministic** movement/simulation for the predicted entity (same input + same state → same result).
|
|
26
|
+
This is why `gamedev-physics` determinism matters here.
|
|
27
|
+
- A **ring buffer** of unacknowledged inputs (`{seq, dt, input}`), kept until the server acknowledges them.
|
|
28
|
+
- Predict only what the client owns (its pawn). Never predict other players — interpolate them instead.
|
|
29
|
+
|
|
30
|
+
## 2. Server reconciliation
|
|
31
|
+
|
|
32
|
+
When an authoritative snapshot arrives tagged "processed up to input N":
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
clientState = serverState # trust the server
|
|
36
|
+
discard buffered inputs with seq <= N # server already applied these
|
|
37
|
+
for each remaining buffered input (seq > N):
|
|
38
|
+
clientState = simulate(clientState, input) # replay the unacknowledged ones
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
If the replayed result matches what the client already had, the correction is invisible. If it diverges (the
|
|
42
|
+
server disagreed — lag, a missed collision, a rejected move), the entity snaps: the visible **rubber-band**.
|
|
43
|
+
Goal is to *minimize* divergence, not to suppress the correction — hiding a legitimate correction is how you
|
|
44
|
+
get players walking through walls on their screen.
|
|
45
|
+
|
|
46
|
+
- **Smoothing**: for small corrections, blend toward the corrected position over a few frames instead of
|
|
47
|
+
hard-snapping, so a 2 cm disagreement isn't a visible pop. Hard-snap large ones.
|
|
48
|
+
|
|
49
|
+
## 3. Snapshot interpolation (remote entities)
|
|
50
|
+
|
|
51
|
+
You receive remote entities' state only every send-tick (e.g. every 50 ms) — rendering the latest packet
|
|
52
|
+
directly makes them teleport. Instead, keep a small buffer and render them ~1–2 snapshots **in the past**:
|
|
53
|
+
|
|
54
|
+
- **Interpolation delay** ≈ 100 ms (2× send interval + jitter margin). Render remote entities at
|
|
55
|
+
`now − delay`, interpolating between the two snapshots that straddle that time.
|
|
56
|
+
- Trade-off: everyone else is shown slightly in the past. That is fine and standard.
|
|
57
|
+
- **Extrapolation** (dead reckoning) predicts a remote entity forward when a packet is late — cheaper on
|
|
58
|
+
bandwidth but wrong on direction changes; prefer interpolation and only extrapolate briefly to cover a gap.
|
|
59
|
+
|
|
60
|
+
So on one client, three clocks coexist: **its own pawn** = present (predicted), **remote entities** = past
|
|
61
|
+
(interpolated), **the server** = the arbiter reconciling the two.
|
|
62
|
+
|
|
63
|
+
## 4. Lag compensation (server-side rewind)
|
|
64
|
+
|
|
65
|
+
For instant/hitscan hits, the shooter aimed at where they *saw* the target — which is `interpolation delay +
|
|
66
|
+
½ RTT` in the past. If the server tests the hit against the target's *present* position, the shooter misses
|
|
67
|
+
despite a perfect aim. Fix: the server keeps a short **history** of every entity's recent positions and, when
|
|
68
|
+
processing a shot, **rewinds** the world to the shooter's view time before testing the hit.
|
|
69
|
+
|
|
70
|
+
- Store ~1 second of position history per entity (a ring of transforms per tick).
|
|
71
|
+
- On a shot: reconstruct positions at `clientRenderTime`, run the trace, then restore.
|
|
72
|
+
- Trade-off: the victim can be hit just after ducking behind cover ("shot behind the wall"). This is an
|
|
73
|
+
accepted fairness choice favoring the shooter; cap how far back you rewind to bound the unfairness.
|
|
74
|
+
|
|
75
|
+
## 5. Tick rate, send rate, netrate
|
|
76
|
+
|
|
77
|
+
- **Simulation tick** — fixed Hz the server steps the world (20 / 30 / 60+; higher = more precise + more CPU).
|
|
78
|
+
- **Send rate / snapshot rate** — how often state goes on the wire; often lower than sim tick, and independent
|
|
79
|
+
of client render FPS. This is the "netrate/updaterate/cmdrate" family in shooters.
|
|
80
|
+
- **Client render FPS** — decoupled from both; render via interpolation between fixed-step states.
|
|
81
|
+
- Bandwidth ≈ replicated-bytes × send-rate × relevant-peers. Shrink it with relevancy/visibility scoping,
|
|
82
|
+
quantized/delta-compressed fields, and lower send rates for distant or unimportant actors.
|
|
83
|
+
|
|
84
|
+
**Never run gameplay in a variable `_process`/`Update`/`Tick` on a networked game** — use a fixed timestep
|
|
85
|
+
(`_physics_process`, `NetworkUpdate`/`FixedUpdate`, Unreal's fixed sim) so prediction and reconciliation
|
|
86
|
+
replay identically on client and server.
|
|
87
|
+
|
|
88
|
+
## Alternative model: deterministic lockstep / rollback
|
|
89
|
+
|
|
90
|
+
For fighting games and some RTS: no per-frame state replication at all — every peer runs the identical
|
|
91
|
+
deterministic simulation and only **inputs** are exchanged. **Lockstep** waits for all inputs before advancing
|
|
92
|
+
(one slow peer stalls everyone). **Rollback** (GGPO-style) predicts absent inputs, then rolls back and
|
|
93
|
+
re-simulates when the real inputs arrive — excellent feel, but demands *bit-perfect determinism* (watch
|
|
94
|
+
floats, iteration order, RNG) and bounded state you can snapshot/restore cheaply. Choose it only when you can
|
|
95
|
+
guarantee that determinism; otherwise the client-server model above is the safer default.
|
|
96
|
+
|
|
97
|
+
## Checklist for a responsive authoritative game
|
|
98
|
+
|
|
99
|
+
- [ ] Owning pawn predicted locally on a fixed timestep; inputs buffered with sequence numbers.
|
|
100
|
+
- [ ] Server acks the last processed input seq in each snapshot; client replays unacknowledged inputs.
|
|
101
|
+
- [ ] Small corrections smoothed, large ones snapped; corrections never fully suppressed.
|
|
102
|
+
- [ ] Remote entities interpolated ~100 ms in the past from a snapshot buffer.
|
|
103
|
+
- [ ] Hitscan uses server-side rewind bounded to ~1 s of history.
|
|
104
|
+
- [ ] Sim tick / send rate / render FPS all decoupled; bandwidth scoped by relevancy.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Unity & Unreal netcode
|
|
2
|
+
|
|
3
|
+
Per-engine deep dive for the two C#/C++ engines. Godot lives in its own reference.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Unity — Netcode for GameObjects (NGO)
|
|
8
|
+
|
|
9
|
+
NGO is Unity's first-party high-level netcode. **UNet / HLAPI is removed** — never emit `NetworkIdentity`,
|
|
10
|
+
`[Command]`, `[SyncVar]`, `NetworkServer`, or anything under `UnityEngine.Networking`. (Mirror is a
|
|
11
|
+
community fork with a UNet-like API; if a repo uses it, name it, but this reference targets NGO.)
|
|
12
|
+
|
|
13
|
+
### Setup & session
|
|
14
|
+
|
|
15
|
+
- One `NetworkManager` in the scene, with a chosen transport (Unity Transport / UTP).
|
|
16
|
+
- Start a session: `NetworkManager.Singleton.StartHost()` (server + local client), `.StartServer()`
|
|
17
|
+
(dedicated/headless), or `.StartClient()`.
|
|
18
|
+
- Networked prefabs need a `NetworkObject` and must be registered in the NetworkManager prefab list;
|
|
19
|
+
the **server** spawns them: `Instantiate(...).GetComponent<NetworkObject>().Spawn()`.
|
|
20
|
+
|
|
21
|
+
### NetworkBehaviour, roles, lifecycle
|
|
22
|
+
|
|
23
|
+
Networked scripts derive from `NetworkBehaviour`. Use `OnNetworkSpawn()` / `OnNetworkDespawn()` instead of
|
|
24
|
+
`Start` for network-aware init. Role/ownership properties: `IsServer`, `IsClient`, `IsHost`, `IsOwner`,
|
|
25
|
+
`OwnerClientId`, `NetworkObjectId`.
|
|
26
|
+
|
|
27
|
+
### NetworkVariable — replicated state
|
|
28
|
+
|
|
29
|
+
```csharp
|
|
30
|
+
public class PlayerState : NetworkBehaviour {
|
|
31
|
+
// Default: read Everyone, write Server → authoritative health.
|
|
32
|
+
public NetworkVariable<int> Health = new(100);
|
|
33
|
+
|
|
34
|
+
// Client convenience state: owner writes, everyone reads.
|
|
35
|
+
public NetworkVariable<int> SkinIndex =
|
|
36
|
+
new(0, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
|
37
|
+
|
|
38
|
+
// Hidden-from-others: owner reads, owner writes.
|
|
39
|
+
public NetworkVariable<int> Ammo =
|
|
40
|
+
new(default, NetworkVariableReadPermission.Owner, NetworkVariableWritePermission.Owner);
|
|
41
|
+
|
|
42
|
+
public override void OnNetworkSpawn() {
|
|
43
|
+
Health.OnValueChanged += (prev, cur) => UpdateHealthBar(cur);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`NetworkVariableReadPermission` is `Everyone` | `Owner`; `NetworkVariableWritePermission` is `Server` |
|
|
49
|
+
`Owner`. Default (no perms arg) = read Everyone, write Server — the right default for authoritative gameplay
|
|
50
|
+
values. Only the permitted writer may assign `.Value`; a client writing a server-write variable is ignored.
|
|
51
|
+
|
|
52
|
+
### RPCs
|
|
53
|
+
|
|
54
|
+
NGO 2.x's unified attribute is preferred:
|
|
55
|
+
|
|
56
|
+
```csharp
|
|
57
|
+
[Rpc(SendTo.Server)] // client → server
|
|
58
|
+
void FireRpc(Vector3 target) { /* runs on server; validate + apply */ }
|
|
59
|
+
|
|
60
|
+
[Rpc(SendTo.ClientsAndHost)] // server → all clients (+ host)
|
|
61
|
+
void PlayFxRpc(Vector3 at) { /* cosmetic */ }
|
|
62
|
+
|
|
63
|
+
[Rpc(SendTo.Owner)] // server → the owning client only
|
|
64
|
+
void YouWereHitRpc(int dmg) { }
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`SendTo` options include `Server`, `ClientsAndHost`, `Everyone`, `Owner`, `NotServer`, `SpecifiedInParams`.
|
|
68
|
+
The **legacy** attributes still compile and enforce method-name suffixes:
|
|
69
|
+
|
|
70
|
+
```csharp
|
|
71
|
+
[ServerRpc(RequireOwnership = true)] void FireServerRpc(Vector3 t) { } // name MUST end in ServerRpc
|
|
72
|
+
[ClientRpc] void PlayFxClientRpc(Vector3 a) { } // name MUST end in ClientRpc
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Prefer `[Rpc(SendTo.X)]` on new code; recognize the legacy pair in existing code. RPCs are unreliable by
|
|
76
|
+
default for some configs — mark reliability via the attribute's `Delivery` where consistency matters.
|
|
77
|
+
|
|
78
|
+
### Transforms & movement
|
|
79
|
+
|
|
80
|
+
`NetworkTransform` replicates position/rotation/scale (server-authoritative by default; an
|
|
81
|
+
`OwnerNetworkTransform`/authority setting allows client-authoritative). For server-authoritative movement
|
|
82
|
+
with responsiveness you add prediction yourself (NGO does not ship full prediction; see
|
|
83
|
+
prediction-and-latency.md). `NetworkAnimator` syncs Animator state.
|
|
84
|
+
|
|
85
|
+
### Gotchas
|
|
86
|
+
|
|
87
|
+
- Only the **server** may `Spawn()`/`Despawn()` NetworkObjects.
|
|
88
|
+
- Writing a `NetworkVariable` you don't have write permission for silently no-ops — check perms first.
|
|
89
|
+
- `IsOwner` ≠ `IsServer`; on a listen host both can be true. Gate authoritative logic on `IsServer`.
|
|
90
|
+
- Legacy RPC name-suffix rule is a compile error if violated; the unified `[Rpc]` has no such rule.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Unreal Engine 5.x — Actor replication
|
|
95
|
+
|
|
96
|
+
Replication is per-`AActor`. Turn it on (`bReplicates = true` in the constructor or `SetReplicates(true)`),
|
|
97
|
+
mark properties, and register them. RPCs are `UFUNCTION`s with network specifiers.
|
|
98
|
+
|
|
99
|
+
### Replicated properties
|
|
100
|
+
|
|
101
|
+
```cpp
|
|
102
|
+
// Header
|
|
103
|
+
UPROPERTY(Replicated)
|
|
104
|
+
int32 Score = 0;
|
|
105
|
+
|
|
106
|
+
UPROPERTY(ReplicatedUsing = OnRep_Health) // RepNotify: OnRep_Health() runs on clients on change
|
|
107
|
+
float Health = 100.f;
|
|
108
|
+
|
|
109
|
+
UFUNCTION() void OnRep_Health();
|
|
110
|
+
|
|
111
|
+
// Cpp — every replicated prop MUST be registered here
|
|
112
|
+
void AMyPawn::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& Out) const {
|
|
113
|
+
Super::GetLifetimeReplicatedProps(Out);
|
|
114
|
+
DOREPLIFETIME(AMyPawn, Score);
|
|
115
|
+
DOREPLIFETIME(AMyPawn, Health);
|
|
116
|
+
// DOREPLIFETIME_CONDITION(AMyPawn, Ammo, COND_OwnerOnly); // owner-only relevancy
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`ReplicatedUsing` (RepNotify) is how you react to a change — use it instead of polling the value in `Tick`.
|
|
121
|
+
The **push model** (`SetReplicatedByCondition` / `MARK_PROPERTY_DIRTY`) avoids per-frame comparison for
|
|
122
|
+
high-count actors.
|
|
123
|
+
|
|
124
|
+
### RPCs
|
|
125
|
+
|
|
126
|
+
```cpp
|
|
127
|
+
UFUNCTION(Server, Reliable, WithValidation) // client → server (validated)
|
|
128
|
+
void ServerFire(FVector Target);
|
|
129
|
+
bool ServerFire_Validate(FVector Target) { return Target.SizeSquared() < 1e8f; }
|
|
130
|
+
void ServerFire_Implementation(FVector Target) { /* server applies */ }
|
|
131
|
+
|
|
132
|
+
UFUNCTION(Client, Reliable) // server → owning client
|
|
133
|
+
void ClientNotifyHit(int32 Dmg);
|
|
134
|
+
|
|
135
|
+
UFUNCTION(NetMulticast, Unreliable) // server → all relevant clients
|
|
136
|
+
void MulticastMuzzleFx(FVector At);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
- `Server` RPCs run on the server, callable by the owning client; add `WithValidation` for gameplay-critical
|
|
140
|
+
ones and reject cheating inputs in `_Validate`.
|
|
141
|
+
- `Client` RPCs run on the actor's owning client only.
|
|
142
|
+
- `NetMulticast` runs on the server and all relevant clients — cosmetic events; keep unreliable.
|
|
143
|
+
- `Reliable` vs `Unreliable`: reliable is guaranteed+ordered but can saturate; reserve for gameplay.
|
|
144
|
+
|
|
145
|
+
### Roles, authority, relevancy
|
|
146
|
+
|
|
147
|
+
- `HasAuthority()` → true on the server (or standalone). Guard server logic with it.
|
|
148
|
+
- `GetLocalRole()`: `ROLE_Authority` (server), `ROLE_AutonomousProxy` (the owning client's predicted pawn),
|
|
149
|
+
`ROLE_SimulatedProxy` (an observed remote actor). Prediction lives on autonomous proxies.
|
|
150
|
+
- **Relevancy** controls what each client receives: `NetCullDistanceSquared`, `bAlwaysRelevant`,
|
|
151
|
+
`bOnlyRelevantToOwner`, override `IsNetRelevantFor`, and **dormancy** (`SetNetDormancy(DORM_DormantAll)`)
|
|
152
|
+
to stop replicating quiescent actors. This is the main bandwidth/anti-wallhack lever.
|
|
153
|
+
|
|
154
|
+
### Movement & prediction
|
|
155
|
+
|
|
156
|
+
`UCharacterMovementComponent` already implements client-side prediction + server reconciliation for
|
|
157
|
+
character movement — use `ACharacter` and let it do the work rather than hand-rolling. For abilities, the
|
|
158
|
+
Gameplay Ability System (GAS) has prediction; the Network Prediction plugin generalizes it (experimental).
|
|
159
|
+
|
|
160
|
+
### Gotchas
|
|
161
|
+
|
|
162
|
+
- Forgetting `DOREPLIFETIME` = the property silently never replicates.
|
|
163
|
+
- RPC specifiers are directional: calling a `Server` RPC from the server, or a `Client` RPC from a client,
|
|
164
|
+
does nothing useful — respect the flow (client→Server, server→Client/Multicast).
|
|
165
|
+
- Actors only replicate if `bReplicates` is true *and* the actor is net-relevant to that client.
|
|
166
|
+
- On a listen server the host is authority + local player; `HasAuthority()` is your gate.
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gamedev-pathing
|
|
3
|
+
description: "Use when implementing AI navigation or pathfinding in a game - moving enemies/NPCs/units toward goals, \"how do enemies navigate the level\", A*/Dijkstra/JPS/HPA* search, choosing grid vs waypoint-graph vs navmesh, baking a navmesh and tuning agent radius/height, off-mesh/nav links, dynamic obstacles and re-baking, steering (seek/flee/arrive/pursue/wander/path-following/flocking), local avoidance (RVO/ORCA, crowds), and flow fields for many agents to one goal. Per-engine: Godot 4.x (NavigationServer2D/3D, NavigationRegion, NavigationAgent, get_next_path_position), Unity (NavMeshAgent + NavMeshObstacle + AI Navigation baking), Unreal (RecastNavMesh + AIController + Behavior Trees/EQS). Triggers: \"my enemies get stuck\", \"units clump together\", \"navmesh not updating\", \"agentes se atascan\". NOT physics/rigidbody collision response or character-controller tuning (gamedev-physics), and NOT high-level enemy AI behaviour design like aggro/states/difficulty (game-design)."
|
|
4
|
+
tags: [pathfinding, navmesh, navigation, steering, a-star]
|
|
5
|
+
recommends: [godot, unity, unreal, gamedev-physics]
|
|
6
|
+
profiles: [full]
|
|
7
|
+
origin: risco
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Game AI navigation & pathfinding
|
|
11
|
+
|
|
12
|
+
Move agents through a level intelligently and per-engine-correctly: pick a world representation,
|
|
13
|
+
run the right search, then follow the result with steering + local avoidance. This skill owns the
|
|
14
|
+
**navigation stack**; it stops where physics collision response and high-level behaviour design begin.
|
|
15
|
+
|
|
16
|
+
## Version targets & the API ban-list (read first)
|
|
17
|
+
|
|
18
|
+
Navigation APIs were renamed hard between engine generations. Emitting an old name compiles into
|
|
19
|
+
nothing or silent breakage. Target these versions; never emit the banned column.
|
|
20
|
+
|
|
21
|
+
| Engine (target) | NEVER emit (deprecated / removed) | Use instead |
|
|
22
|
+
| --- | --- | --- |
|
|
23
|
+
| **Godot 4.x** | `Navigation` / `NavigationMeshInstance` / `Navigation2D` nodes | `NavigationServer2D/3D` + `NavigationRegion2D/3D` |
|
|
24
|
+
| Godot 4.x | `agent.get_next_location()` | `agent.get_next_path_position()` |
|
|
25
|
+
| Godot 4.x | `NavigationAgent.set_target_location()` | set the `target_position` property |
|
|
26
|
+
| Godot 4.x | reading `velocity` directly when avoidance is on | feed `set_velocity()`, read the `velocity_computed(safe)` signal |
|
|
27
|
+
| **Unity (AI Navigation pkg)** | the legacy **Navigation window** static bake, `Navigation Static` flag, built-in `OffMeshLink` component | `NavMeshSurface` (`com.unity.ai.navigation`), `NavMeshLink`, `NavMeshModifier` |
|
|
28
|
+
| Unity | `agent.destination = p` then reading a path same frame | `SetDestination(p)`, then gate on `!pathPending && remainingDistance <= stoppingDistance` |
|
|
29
|
+
| **Unreal (UE5)** | hand-rolling A* over your own grid for pawns | `AAIController::MoveTo*` over `RecastNavMesh`; BT `MoveTo` task |
|
|
30
|
+
| Unreal | expecting a **Static** navmesh to react to spawned geometry | set RecastNavMesh **Runtime Generation = Dynamic** (or Dynamic Modifiers Only) |
|
|
31
|
+
|
|
32
|
+
## The two-layer mental model (center of gravity)
|
|
33
|
+
|
|
34
|
+
The single most common navigation bug is collapsing two jobs into one. Keep them separate:
|
|
35
|
+
|
|
36
|
+
1. **Global pathfinding — *where* to go.** A discrete search (A* / navmesh query) over a static-ish
|
|
37
|
+
representation, run *occasionally* (on new goal, or throttled), returns a **corridor of waypoints**.
|
|
38
|
+
2. **Local steering + avoidance — *how* to move this frame.** A cheap per-frame vector: follow the
|
|
39
|
+
corridor, dodge other agents and dynamic obstacles, respect acceleration. Runs *every frame*.
|
|
40
|
+
|
|
41
|
+
Path for the map, steering for the moment. Symptoms of merging them: re-running A* every frame (CPU
|
|
42
|
+
melts), or agents that walk the path but pile into each other (no local avoidance). Every engine's
|
|
43
|
+
`NavMeshAgent` / `NavigationAgent` bundles both — know which layer you are configuring.
|
|
44
|
+
|
|
45
|
+
## Choosing a representation
|
|
46
|
+
|
|
47
|
+
| Representation | Fits | Cost / caveat |
|
|
48
|
+
| --- | --- | --- |
|
|
49
|
+
| **Uniform grid** | tile/2D games, RTS, roguelikes, destructible terrain | many nodes; needs path smoothing to avoid staircase paths; JPS accelerates it |
|
|
50
|
+
| **Waypoint graph** | sparse hand-placed routes, patrol nets, rails, racing lines | cheap; agents snap to nodes, off-graph space is invisible — brittle for open areas |
|
|
51
|
+
| **Navmesh** | 3D and most open 2D worlds; the default for character movement | bake step; represents *walkable surface* as convex polys — fewer nodes, natural paths |
|
|
52
|
+
|
|
53
|
+
Rule: **navmesh for free-roaming characters, grid for tile-locked/destructible worlds, waypoint graph
|
|
54
|
+
only for constrained routes.** Detail & path-smoothing (funnel algorithm) → `references/search-algorithms.md`.
|
|
55
|
+
|
|
56
|
+
## Search algorithms (essentials)
|
|
57
|
+
|
|
58
|
+
- **A\*** — the default. Dijkstra + a heuristic `h(n)` that estimates remaining cost. Admissible `h`
|
|
59
|
+
(never *over*-estimates) ⇒ optimal path; use **octile** distance on 8-connected grids, **Euclidean**
|
|
60
|
+
on navmesh/any-angle. `h` must also stay ≤ true edge costs (consistency) to skip re-expansions.
|
|
61
|
+
- **Dijkstra** — A* with `h=0`. Use when there is **no single goal** (nearest of many exits) or you need
|
|
62
|
+
the full cost field (see flow fields). Slower than A* to one target.
|
|
63
|
+
- **Weighted / greedy** — inflate `h` (`f = g + w·h`, `w>1`) for faster, slightly suboptimal paths when
|
|
64
|
+
frame budget beats optimality.
|
|
65
|
+
- **JPS (Jump Point Search)** — A* speedup for **uniform-cost grids only**; skips symmetric paths, often
|
|
66
|
+
10×+ fewer expansions. Not for weighted terrain or navmeshes.
|
|
67
|
+
- **Hierarchical / HPA\*** — for **huge maps**: partition into clusters, path cluster-to-cluster, refine
|
|
68
|
+
locally. Reach for it when a single flat A* blows the frame budget or you have thousands of tiles.
|
|
69
|
+
|
|
70
|
+
Always run search off a **binary-heap open set** and a closed set. Pseudocode, tie-breaking, any-angle
|
|
71
|
+
(Theta*), and the funnel string-pull → `references/search-algorithms.md`.
|
|
72
|
+
|
|
73
|
+
## Navmesh workflow (essentials)
|
|
74
|
+
|
|
75
|
+
1. **Bake** the walkable surface from level geometry. Key params: **agent radius** (how far the mesh is
|
|
76
|
+
shrunk from walls — the #1 knob), **agent height**, **max step/climb**, **max slope**, cell size.
|
|
77
|
+
2. **One navmesh per agent size.** A tank and a rat need different bakes; do not share one mesh and hope.
|
|
78
|
+
3. **Regions / areas + costs** — tag surfaces (water, mud, road) with a traversal cost so paths prefer
|
|
79
|
+
roads and avoid hazards, rather than deleting the area outright.
|
|
80
|
+
4. **Off-mesh / nav links** — explicit edges for jumps, ladders, teleporters, doors: places agents move
|
|
81
|
+
but no polygon connects. Godot `NavigationLink`, Unity `NavMeshLink`, Unreal `NavLinkProxy`.
|
|
82
|
+
5. **Dynamic obstacles** — two tools, do not confuse them:
|
|
83
|
+
- **Carving obstacle** (Unity `NavMeshObstacle` carving, Godot `NavigationObstacle`, Unreal
|
|
84
|
+
`NavModifier`): punches a hole so *global* paths route around a placed prop. Costs a re-carve on move.
|
|
85
|
+
- **Local avoidance** (RVO/ORCA): agents dodge *without* touching the mesh — for other moving agents.
|
|
86
|
+
6. **Re-baking** — full rebake is expensive; prefer **tile/partial** rebake or carving for runtime
|
|
87
|
+
changes. Bake offline for static levels. Full param table + per-engine baking → `references/navmesh-workflow.md`.
|
|
88
|
+
|
|
89
|
+
## Steering & local avoidance (essentials)
|
|
90
|
+
|
|
91
|
+
Steering = a desired-velocity vector combined and clamped to max force/speed. Primitives:
|
|
92
|
+
|
|
93
|
+
- **Seek / Flee** — accelerate toward / away from a target point.
|
|
94
|
+
- **Arrive** — seek that ramps speed down inside a slowing radius (no overshoot/jitter at the goal).
|
|
95
|
+
- **Pursue / Evade** — seek/flee the target's *predicted future* position, not its current one.
|
|
96
|
+
- **Wander** — smoothed random heading for idle/ambient motion.
|
|
97
|
+
- **Path following** — steer toward a look-ahead point along the A*/navmesh corridor, not the far goal.
|
|
98
|
+
- **Flocking** = separation + alignment + cohesion (Reynolds boids) for groups.
|
|
99
|
+
|
|
100
|
+
Combine either as a **weighted sum** (simple) or **priority/arbitration** (avoidance wins over cohesion).
|
|
101
|
+
|
|
102
|
+
**Local avoidance (RVO / ORCA):** each agent picks a velocity that is collision-free assuming neighbours
|
|
103
|
+
share the burden (*reciprocal* — hence no oscillating "dance"). This is avoidance, **not** collision
|
|
104
|
+
*response*: it changes intended velocity *before* moving; the physics/collision solver is a separate,
|
|
105
|
+
last-resort backstop (→ `gamedev-physics`). For dense crowds use the engine's crowd/avoidance system.
|
|
106
|
+
|
|
107
|
+
**Flow fields** — for **many agents → one (or few) goals** (RTS swarm, tower-defense creeps): run one
|
|
108
|
+
**Dijkstra from the goal** over the grid to build a cost/integration field, derive a per-cell direction
|
|
109
|
+
vector once, then every agent just samples its cell. O(1) per agent vs one A* each. Full derivation,
|
|
110
|
+
boids weights, and RVO intuition → `references/steering-and-avoidance.md`.
|
|
111
|
+
|
|
112
|
+
## Per-engine mapping
|
|
113
|
+
|
|
114
|
+
### Godot 4.x (`NavigationAgent3D` — 2D is identical with `2D` suffix)
|
|
115
|
+
|
|
116
|
+
```gdscript
|
|
117
|
+
extends CharacterBody3D
|
|
118
|
+
@onready var agent: NavigationAgent3D = $NavigationAgent3D
|
|
119
|
+
@export var speed := 4.0
|
|
120
|
+
|
|
121
|
+
func _ready() -> void:
|
|
122
|
+
agent.avoidance_enabled = true # RVO local avoidance
|
|
123
|
+
agent.velocity_computed.connect(_on_velocity_computed)
|
|
124
|
+
|
|
125
|
+
func set_goal(p: Vector3) -> void:
|
|
126
|
+
agent.target_position = p # property, NOT set_target_location()
|
|
127
|
+
|
|
128
|
+
func _physics_process(_delta: float) -> void:
|
|
129
|
+
if agent.is_navigation_finished():
|
|
130
|
+
return
|
|
131
|
+
var next := agent.get_next_path_position() # 4.x name (was get_next_location)
|
|
132
|
+
var desired := global_position.direction_to(next) * speed
|
|
133
|
+
agent.set_velocity(desired) # feed RVO; result via signal
|
|
134
|
+
|
|
135
|
+
func _on_velocity_computed(safe: Vector3) -> void: # only fires while avoidance enabled
|
|
136
|
+
velocity = safe
|
|
137
|
+
move_and_slide()
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
One-off query without an agent: `NavigationServer3D.map_get_path(map_rid, from, to, true)`. Rebake a
|
|
141
|
+
region at runtime: `$NavigationRegion3D.bake_navigation_mesh()`. Off-mesh: `NavigationLink3D`. If
|
|
142
|
+
avoidance is **off**, skip the signal and `move_and_slide()` with `desired` directly.
|
|
143
|
+
|
|
144
|
+
### Unity (AI Navigation package + `NavMeshAgent`)
|
|
145
|
+
|
|
146
|
+
```csharp
|
|
147
|
+
using UnityEngine;
|
|
148
|
+
using UnityEngine.AI;
|
|
149
|
+
|
|
150
|
+
[RequireComponent(typeof(NavMeshAgent))]
|
|
151
|
+
public class Chaser : MonoBehaviour {
|
|
152
|
+
NavMeshAgent agent;
|
|
153
|
+
void Awake() => agent = GetComponent<NavMeshAgent>();
|
|
154
|
+
|
|
155
|
+
public void Chase(Transform target) => agent.SetDestination(target.position);
|
|
156
|
+
|
|
157
|
+
public bool ReachedGoal() =>
|
|
158
|
+
!agent.pathPending && agent.remainingDistance <= agent.stoppingDistance
|
|
159
|
+
&& (!agent.hasPath || agent.velocity.sqrMagnitude < 0.01f);
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Bake: add a **`NavMeshSurface`** to a scene root and Bake (the legacy Navigation window is gone).
|
|
164
|
+
Runtime rebake: `surface.BuildNavMesh()`. Dynamic blockers: **`NavMeshObstacle`** (enable *Carving* for
|
|
165
|
+
stationary props, leave off for moving agents so RVO handles them). Off-mesh: **`NavMeshLink`**. Local
|
|
166
|
+
avoidance quality: `agent.obstacleAvoidanceType`. Area costs: `agent.SetAreaCost(area, cost)`.
|
|
167
|
+
|
|
168
|
+
### Unreal (UE5 — `RecastNavMesh` + `AIController` + Behavior Tree / EQS)
|
|
169
|
+
|
|
170
|
+
1. Drop a **`NavMeshBoundsVolume`** around playable space → a `RecastNavMesh` auto-generates. For
|
|
171
|
+
runtime changes set its **Runtime Generation = Dynamic** (or *Dynamic Modifiers Only*); spawned
|
|
172
|
+
geometry must have collision + **Can Ever Affect Navigation**.
|
|
173
|
+
2. Possess the pawn with an **`AAIController`**. Movement:
|
|
174
|
+
|
|
175
|
+
```cpp
|
|
176
|
+
AAIController* AICon = Cast<AAIController>(GetController());
|
|
177
|
+
AICon->MoveToActor(TargetActor, /*AcceptanceRadius*/ 50.f); // or MoveToLocation(FVector)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
3. **Behavior Tree + Blackboard** for decisions; the **`MoveTo`** task walks the navmesh. **EQS**
|
|
181
|
+
(Environment Query System) picks *where* to go (cover, flank, nearest item) via scored queries.
|
|
182
|
+
4. Costs/holes: **`NavModifierVolume`** + `NavArea` classes. Off-mesh: **`NavLinkProxy`**. Crowd
|
|
183
|
+
avoidance: enable the **Detour Crowd** manager (or `DetourCrowdAIController`) for RVO on many agents.
|
|
184
|
+
|
|
185
|
+
> High-level BT *design* (states, aggro, difficulty) is `game-design`; this skill wires the BT's
|
|
186
|
+
> movement/EQS tasks to navigation, not the decision tree's semantics.
|
|
187
|
+
|
|
188
|
+
## Guardrails / gotchas
|
|
189
|
+
|
|
190
|
+
- **Agent radius mismatch** is the top "stuck in doorways / clipping walls" cause — bake with the real radius.
|
|
191
|
+
- Bake covers **static** geometry; runtime-spawned meshes need a rebake, a carving obstacle, or a nav link.
|
|
192
|
+
- Path returns empty when start/end are **off the navmesh** or in disconnected islands — snap to nearest poly and check reachability first.
|
|
193
|
+
- Avoidance (RVO) ≠ collision (physics). Agents can still overlap under pressure; that is expected, not a physics bug.
|
|
194
|
+
- Off-mesh links are **directional and manual** — a jump-down link does not imply a jump-up link.
|
|
195
|
+
- Smooth grid paths (funnel / string-pull) or agents walk visible zig-zag staircases.
|
|
196
|
+
|
|
197
|
+
## Anti-patterns / rationalizations → STOP
|
|
198
|
+
|
|
199
|
+
| Rationalization | Reality / do instead |
|
|
200
|
+
| --- | --- |
|
|
201
|
+
| "Just A* every frame, it's fine" | Melts CPU at scale; path on goal-change, steer between frames. |
|
|
202
|
+
| "One navmesh bake works for every unit" | Radius/height differ; bake per agent size or use agent-type profiles. |
|
|
203
|
+
| "Agents clip through each other — physics bug" | Missing **local avoidance**; enable RVO/crowd, that's the navigation layer. |
|
|
204
|
+
| "Navmesh doesn't see my spawned wall" | Static bake; set Dynamic/rebake, or add a carving obstacle/nav link. |
|
|
205
|
+
| "I'll grid-search my own A* in Unity/Unreal" | Reinvents the built-in navmesh; use `NavMeshAgent` / `AAIController::MoveTo`. |
|
|
206
|
+
| "500 zombies each run A* to the player" | That's a **flow field**: one Dijkstra from the goal, agents sample cells. |
|
|
207
|
+
| "Set `agent.destination` and read the path same frame" | Path is async (`pathPending`); gate on it before trusting `remainingDistance`. |
|
|
208
|
+
| "Bigger heuristic = smarter" | Over-estimating `h` breaks A* optimality; keep it admissible (or weight it *knowingly*). |
|
|
209
|
+
|
|
210
|
+
## Related skills
|
|
211
|
+
|
|
212
|
+
- [`godot`](../godot/SKILL.md) — GDScript/scene specifics; this skill owns the navigation subsystem it plugs into.
|
|
213
|
+
- [`unity`](../unity/SKILL.md) — Unity/C# project setup; here for the NavMesh + AI Navigation package details.
|
|
214
|
+
- [`unreal`](../unreal/SKILL.md) — UE5/Blueprint/C++; here for RecastNavMesh + AIController/BT/EQS wiring.
|
|
215
|
+
- [`gamedev-physics`](../gamedev-physics/SKILL.md) — collision *response*, rigidbodies, character controllers; steering decides intended velocity, physics resolves the contact.
|
|
216
|
+
- `game-design` — high-level enemy behaviour, aggro, encounter/difficulty design (what the AI *decides*, not how it *moves*).
|
|
217
|
+
|
|
218
|
+
## Checklist
|
|
219
|
+
|
|
220
|
+
- [ ] Representation chosen deliberately (navmesh / grid / waypoint) and justified for the world type.
|
|
221
|
+
- [ ] Global path and local steering are **separate layers**; A* is not per-frame.
|
|
222
|
+
- [ ] Navmesh baked with the correct **agent radius/height/step/slope** (per agent size if they differ).
|
|
223
|
+
- [ ] Area costs set for hazards/terrain instead of hard-deleting walkable space.
|
|
224
|
+
- [ ] Off-mesh/nav links placed for every jump/ladder/teleport gap (correct direction).
|
|
225
|
+
- [ ] Dynamic obstacles handled by carving *or* rebake *or* local avoidance — the right one for each case.
|
|
226
|
+
- [ ] Local avoidance / crowd enabled if multiple agents share space; flow field used for many→one-goal.
|
|
227
|
+
- [ ] Correct **current** engine APIs (no banned names from the version table); paths validated for reachability.
|
|
228
|
+
- [ ] Grid paths smoothed (funnel) so movement isn't staircased.
|
|
229
|
+
|
|
230
|
+
## Project grounding (02-DOCS + CLAUDE.md)
|
|
231
|
+
|
|
232
|
+
When this runs in a project with a `02-DOCS/` layer (the [`harness`](../harness/SKILL.md) wiki), record
|
|
233
|
+
the project's navigation decisions in `02-DOCS/wiki/stack/gamedev-pathing.md` (indexed from
|
|
234
|
+
`02-DOCS/wiki/index.md`): engine + version, chosen representation, bake settings (agent sizes, cell
|
|
235
|
+
size), avoidance/crowd choice, custom links/areas. Read it first on every use; bump its `Updated` date
|
|
236
|
+
when a convention changes. No `02-DOCS/` layer? Skip silently — conventions are *recorded, not gated*.
|
|
237
|
+
|
|
238
|
+
## See Also — local references (read when)
|
|
239
|
+
|
|
240
|
+
- `references/search-algorithms.md` — representations, A*/Dijkstra/JPS/HPA* pseudocode, heuristics & admissibility, tie-breaking, any-angle, funnel smoothing.
|
|
241
|
+
- `references/navmesh-workflow.md` — bake-parameter table, areas/costs, off-mesh links, obstacles vs carving vs partial rebake, per-engine baking, debugging.
|
|
242
|
+
- `references/steering-and-avoidance.md` — steering primitives, path following, boids weights, RVO/ORCA intuition, crowds, flow-field construction.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Eval harness — `gamedev-pathing` skill
|
|
2
|
+
|
|
3
|
+
`cases.yaml` is a human/CI-readable trigger-and-capability spec, not an automated runner. It asserts two
|
|
4
|
+
things. **Triggering**: the skill fires on real navigation asks — engine-specific ("enemies stuck in
|
|
5
|
+
Godot 4", "Unreal navmesh won't see a spawned crate"), representation/algorithm theory (A* heuristics,
|
|
6
|
+
grid vs navmesh), crowd/avoidance ("units dance and pile up"), and a Catalan off-mesh-link phrasing — and
|
|
7
|
+
stays quiet on near-misses that route to `gamedev-physics` (collision response), `game-design` (what the
|
|
8
|
+
AI decides), `unity` (lightmap baking — the "bake" false friend), `godot` (animation state machine), and
|
|
9
|
+
`gamedev-multiplayer` (netcode). **Capability**: with `SKILL.md` + references loaded, the answer uses
|
|
10
|
+
the current Godot 4.x API (`target_position`, `get_next_path_position`, `velocity_computed` — never the
|
|
11
|
+
Godot-3 names), separates global pathfinding from per-frame steering, and reaches for a flow field for
|
|
12
|
+
many-agents-to-one-goal. Run 3–5 trials per prompt (LLM routing is non-deterministic); a prompt passes on
|
|
13
|
+
a majority. Judge `capability` by reading the output against `must_include`; the with-skill answer should
|
|
14
|
+
clearly beat a no-skill baseline.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
skill: gamedev-pathing
|
|
2
|
+
|
|
3
|
+
should_trigger:
|
|
4
|
+
- prompt: "My enemies in Godot 4 keep getting stuck on walls and never reach the player. How do I get them to actually path around the level?"
|
|
5
|
+
why: "Core trigger: AI navigation in Godot 4.x. Should produce a NavigationAgent3D loop with target_position + get_next_path_position, and flag agent-radius baking as the stuck-on-walls cause."
|
|
6
|
+
- prompt: "I've got 400 zombies all chasing the player in my Unity horde game and the framerate tanks because each one runs its own pathfinding. What's the right approach?"
|
|
7
|
+
why: "Many-agents-to-one-goal is the flow-field trigger; should recommend a Dijkstra-from-goal flow field instead of per-agent A*, plus local avoidance."
|
|
8
|
+
- prompt: "In Unreal my AI won't walk around a crate I spawn at runtime — it just walks straight through where it should be. The navmesh doesn't seem to update."
|
|
9
|
+
why: "Dynamic-obstacle / re-bake trigger; should point to RecastNavMesh Runtime Generation = Dynamic (or a NavModifier), Can Ever Affect Navigation, not a static bake."
|
|
10
|
+
- prompt: "What's the difference between using a grid, a waypoint graph, and a navmesh for pathfinding, and when should I pick each?"
|
|
11
|
+
why: "Representation trade-offs are a named core section; should give the grid/waypoint/navmesh table with the 'navmesh for free-roaming characters' rule."
|
|
12
|
+
- prompt: "My squad of units all pile onto the exact same spot and jitter against each other when they reach the destination. How do I make them spread out and stop dancing?"
|
|
13
|
+
why: "Local avoidance / crowd trigger (RVO/ORCA reciprocal avoidance); should distinguish avoidance from collision and enable the engine's crowd/avoidance system, plus arrive-behaviour to stop jitter."
|
|
14
|
+
- prompt: "Explain A* — how the heuristic works, what admissible means, and when I'd use JPS or a hierarchical version instead."
|
|
15
|
+
why: "Search-algorithm core section; should cover f=g+h, admissibility/consistency, octile vs Euclidean h, JPS for uniform grids, HPA* for huge maps."
|
|
16
|
+
- prompt: "Els meus NPCs no saben baixar d'una plataforma a una altra al navmesh, com els connecto?"
|
|
17
|
+
why: "Catalan off-mesh-link trigger; should recommend an explicit directional nav link (NavigationLink / NavMeshLink / NavLinkProxy)."
|
|
18
|
+
|
|
19
|
+
should_not_trigger:
|
|
20
|
+
- prompt: "My character controller sinks into the floor and bounces off walls weirdly — the rigidbody collision response feels wrong."
|
|
21
|
+
route_to: "gamedev-physics"
|
|
22
|
+
why: "Collision response / rigidbody / character-controller tuning is physics, not navigation; steering only sets intended velocity, physics resolves the contact."
|
|
23
|
+
- prompt: "Design my enemy AI: when should the boss enrage, how does aggro/threat work, and how do difficulty tiers change its behaviour?"
|
|
24
|
+
route_to: "game-design"
|
|
25
|
+
why: "High-level behaviour/encounter/difficulty design is what the AI decides, not how it moves through space — belongs to game-design."
|
|
26
|
+
- prompt: "Write a GDScript state machine for my player's idle/run/jump/attack animation states in Godot."
|
|
27
|
+
route_to: "godot"
|
|
28
|
+
why: "General GDScript / animation state machine is engine-language work, not the navigation subsystem."
|
|
29
|
+
- prompt: "Set up a dedicated server so multiple players' positions stay in sync with low latency."
|
|
30
|
+
route_to: "gamedev-multiplayer"
|
|
31
|
+
why: "Netcode / replication / lag compensation is multiplayer networking, unrelated to pathfinding."
|
|
32
|
+
- prompt: "How do I bake lighting and set up baked global illumination for my Unity level?"
|
|
33
|
+
route_to: "unity"
|
|
34
|
+
why: "'Baking' here is lightmaps, not a navmesh; near-miss on the word 'bake' that must route to the engine skill, not navigation."
|
|
35
|
+
|
|
36
|
+
capability:
|
|
37
|
+
- scenario: "In Godot 4.x, make a CharacterBody3D enemy chase the player across a baked navmesh, pathing around static geometry and avoiding other enemies, without getting stuck in doorways."
|
|
38
|
+
must_include:
|
|
39
|
+
- "Uses a NavigationAgent3D with the `target_position` property (NOT the removed set_target_location) to set the goal"
|
|
40
|
+
- "Calls `get_next_path_position()` each physics frame (NOT the Godot-3 `get_next_location()`) and moves toward it"
|
|
41
|
+
- "Enables avoidance (`avoidance_enabled = true`), feeds `set_velocity(desired)`, and reads the safe velocity from the `velocity_computed` signal before move_and_slide()"
|
|
42
|
+
- "Separates global pathfinding (occasional, on new goal) from per-frame steering/movement — does not re-run a full search every frame"
|
|
43
|
+
- "Names agent radius in the navmesh bake as the fix for getting stuck in doorways / clipping walls"
|
|
44
|
+
- "Notes that local avoidance (RVO) is distinct from physics collision response"
|
|
45
|
+
- scenario: "Advise on moving 500+ units to a single rally point in an RTS efficiently."
|
|
46
|
+
must_include:
|
|
47
|
+
- "Recommends a flow field instead of per-agent A*"
|
|
48
|
+
- "Describes building it via Dijkstra/BFS from the goal to make an integration/cost field, then a per-cell direction vector agents sample in O(1)"
|
|
49
|
+
- "Layers local avoidance / separation on top so units don't stack on the goal cell"
|