@ericrisco/rsc 0.1.34 → 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 +28 -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/agent-eval/SKILL.md +1 -1
- package/skills/analyze/SKILL.md +1 -1
- package/skills/brand-voice/SKILL.md +1 -1
- package/skills/brand-voice/references/voice-guide-template.md +14 -0
- package/skills/building-agents/SKILL.md +2 -2
- package/skills/competitor-watch/SKILL.md +9 -4
- package/skills/competitor-watch/references/tracker-schema.md +19 -0
- package/skills/constitution/references/constitution-template.md +10 -0
- package/skills/content-engine/SKILL.md +1 -1
- package/skills/content-engine/references/brief-and-pipeline.md +15 -1
- package/skills/course-builder/references/grounding-and-scoping.md +23 -0
- package/skills/course-storytelling/references/learner-grounding.md +22 -2
- package/skills/decision-records/SKILL.md +14 -1
- package/skills/decision-records/references/templates.md +20 -0
- package/skills/design/references/brand-grounding.md +11 -1
- 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/harness/SKILL.md +3 -3
- package/skills/harness/references/daily-curation-automation.md +8 -6
- package/skills/harness/references/obsidian-scaffolding.md +16 -9
- package/skills/harness/references/wiki-article-template.md +31 -14
- package/skills/harness/references/wiki-index-template.md +7 -3
- package/skills/harness/references/wiki-protocol.md +120 -54
- package/skills/harness/references/wiki-worklog-template.md +5 -1
- package/skills/instagram-api/SKILL.md +8 -1
- package/skills/instagram-api/references/insights-metrics.md +8 -1
- package/skills/knowledge-ops/SKILL.md +9 -9
- package/skills/knowledge-ops/evals/cases.yaml +1 -1
- package/skills/knowledge-ops/references/gardening-playbook.md +18 -8
- 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/linkedin-api/SKILL.md +10 -1
- package/skills/linkedin-strategy/SKILL.md +12 -0
- package/skills/linkedin-strategy/references/wiki-records.md +27 -1
- package/skills/marketing/references/brand-grounding.md +22 -2
- package/skills/medium-strategy/SKILL.md +12 -0
- package/skills/medium-strategy/references/wiki-records.md +39 -1
- package/skills/medium-writing/SKILL.md +19 -0
- package/skills/plan/references/plan-template.md +12 -1
- 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/shortform-packaging/SKILL.md +7 -2
- package/skills/shortform-packaging/references/package-templates.md +13 -2
- package/skills/shortform-strategy/SKILL.md +30 -6
- package/skills/shortform-strategy/references/learning-loop-template.md +9 -1
- package/skills/specify/references/spec-template.md +12 -1
- package/skills/tiktok-api/SKILL.md +6 -2
- package/skills/tiktok-api/references/wiki-schema.md +38 -4
- 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
- package/skills/verify/SKILL.md +11 -1
- package/skills/youtube-api/SKILL.md +7 -2
- package/skills/youtube-api/references/wiki-schema.md +25 -4
- package/skills/youtube-packaging/SKILL.md +10 -2
- package/skills/youtube-packaging/references/description-and-chapters.md +70 -2
- package/skills/youtube-strategy/SKILL.md +9 -2
- package/skills/youtube-strategy/references/wiki-records.md +39 -6
- package/skills/youtube-strategy/scripts/verify.sh +4 -0
- package/skills/youtube-thumbnails/SKILL.md +10 -5
- package/skills/youtube-thumbnails/references/experiment-log-format.md +28 -2
|
@@ -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"
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Navmesh workflow — baking, links, dynamic obstacles, per-engine
|
|
2
|
+
|
|
3
|
+
Depth for the "Navmesh workflow" section of SKILL.md. A navmesh is the walkable surface expressed as
|
|
4
|
+
convex polygons; search runs over polygon adjacency, and movement follows the corridor via the funnel
|
|
5
|
+
algorithm (see `search-algorithms.md`).
|
|
6
|
+
|
|
7
|
+
## Bake parameters (the knobs that matter)
|
|
8
|
+
|
|
9
|
+
Every engine bakes by voxelizing collision geometry and reconstructing walkable polys (Recast under the
|
|
10
|
+
hood in Unity and Unreal; Godot has its own baker). The parameters map closely across engines:
|
|
11
|
+
|
|
12
|
+
| Parameter | What it does | Getting it wrong |
|
|
13
|
+
| --- | --- | --- |
|
|
14
|
+
| **Agent radius** | Shrinks the mesh away from walls by this radius | Too small → agents clip walls / stick in doorways; too large → valid gaps vanish |
|
|
15
|
+
| **Agent height** | Min vertical clearance a poly must have | Agents path under geometry they can't fit beneath |
|
|
16
|
+
| **Max step / climb** | Tallest ledge treated as walkable (stairs, curbs) | Stairs become walls, or agents "climb" impossible ledges |
|
|
17
|
+
| **Max slope** | Steepest walkable incline (degrees) | Ramps drop out, or agents walk up cliffs |
|
|
18
|
+
| **Cell size / voxel size** | Bake resolution | Coarse → missing thin walkways; fine → slow bake, huge data |
|
|
19
|
+
| **Min region area** | Discards tiny isolated islands | Noise polys, or losing small legit platforms |
|
|
20
|
+
|
|
21
|
+
**One bake per agent size.** A large enemy and a small one need different radius/height; share a mesh
|
|
22
|
+
only if their footprints truly match. Unity/Unreal support multiple **agent types/profiles**; Godot uses
|
|
23
|
+
separate `NavigationMesh` resources or navigation layers.
|
|
24
|
+
|
|
25
|
+
## Regions, areas & costs
|
|
26
|
+
|
|
27
|
+
Tag surfaces with an **area type** carrying a traversal **cost multiplier** so paths *prefer* or *avoid*
|
|
28
|
+
terrain instead of it being all-or-nothing:
|
|
29
|
+
|
|
30
|
+
- Road = 0.5, grass = 1, mud = 4, water = 8 → agents naturally favour roads, cross mud only if shorter.
|
|
31
|
+
- Set an area's cost to *very high* rather than deleting it when you want "avoid unless no alternative".
|
|
32
|
+
- **Navigation layers / area masks** let different agents see different areas (e.g. only amphibious units
|
|
33
|
+
path through water) by filtering which polygons a query considers.
|
|
34
|
+
|
|
35
|
+
## Off-mesh / nav links
|
|
36
|
+
|
|
37
|
+
Explicit connections where an agent can move but no polygon adjacency exists: jump-downs, ladders,
|
|
38
|
+
teleporters, ziplines, doorways between disconnected meshes.
|
|
39
|
+
|
|
40
|
+
- **Directional**: define both directions if the agent can traverse both ways (a fall-down link is often
|
|
41
|
+
one-way). Give it a cost so pathing weighs it against walking around.
|
|
42
|
+
- The link only tells pathfinding the two ends connect — you still play the **traversal animation/motion**
|
|
43
|
+
yourself when the agent reaches the link (jump arc, climb, teleport).
|
|
44
|
+
- Godot `NavigationLink2D/3D` · Unity `NavMeshLink` · Unreal `NavLinkProxy` (with smart-link events).
|
|
45
|
+
|
|
46
|
+
## Dynamic obstacles: three different tools
|
|
47
|
+
|
|
48
|
+
Do not confuse these — each solves a different problem:
|
|
49
|
+
|
|
50
|
+
1. **Carving obstacle** — cuts a hole in the navmesh so *global* paths route around a placed object
|
|
51
|
+
(a dropped crate, a closed gate). Global and correct, but re-carving on every move is costly → use for
|
|
52
|
+
**stationary / rarely-moving** props. Unity `NavMeshObstacle` (Carve on) · Godot `NavigationObstacle`
|
|
53
|
+
(with vertices) · Unreal `NavModifierComponent` / `NavModifierVolume`.
|
|
54
|
+
2. **Local avoidance (RVO/ORCA)** — agents steer around each other and moving obstacles *without*
|
|
55
|
+
touching the mesh, every frame. For **other agents** and small moving things. See `steering-and-avoidance.md`.
|
|
56
|
+
3. **Re-bake** — regenerate the mesh (or affected tiles) when the level geometry itself changes
|
|
57
|
+
(destruction, procedural build). Prefer **tile/partial** rebake over full.
|
|
58
|
+
|
|
59
|
+
Rule of thumb: **moving agents → RVO; placed static blockers → carving; geometry changed → rebake.**
|
|
60
|
+
|
|
61
|
+
## Re-baking strategy
|
|
62
|
+
|
|
63
|
+
- **Static levels**: bake once at author time; ship the baked data. No runtime cost.
|
|
64
|
+
- **Occasional changes**: rebake the affected **tiles/region** only, ideally async/off the main thread.
|
|
65
|
+
- **Constant small changes** (a patrolling door): a carving obstacle beats rebaking.
|
|
66
|
+
- Full-map rebake every frame is never the answer — it is the "navmesh not updating, so I rebake
|
|
67
|
+
constantly" anti-pattern.
|
|
68
|
+
|
|
69
|
+
## Per-engine baking specifics
|
|
70
|
+
|
|
71
|
+
### Godot 4.x
|
|
72
|
+
|
|
73
|
+
- `NavigationRegion2D/3D` holds a `NavigationMesh` (3D) / `NavigationPolygon` (2D). Bake in-editor, or at
|
|
74
|
+
runtime with `region.bake_navigation_mesh()`.
|
|
75
|
+
- Source geometry: configure the `NavigationMesh` geometry parsing (parsed geometry type, collision mask,
|
|
76
|
+
source group) so the baker knows which nodes to voxelize.
|
|
77
|
+
- Advanced/procedural: build a `NavigationMeshSourceGeometryData3D` and call
|
|
78
|
+
`NavigationServer3D.bake_from_source_geometry_data(...)`, then assign to the region.
|
|
79
|
+
- Query directly: `NavigationServer3D.map_get_path(map, from, to, optimize, navigation_layers)`.
|
|
80
|
+
- Debug: enable **Visible Navigation** (Debug menu) to draw the mesh in-game.
|
|
81
|
+
|
|
82
|
+
### Unity (AI Navigation package, `com.unity.ai.navigation`)
|
|
83
|
+
|
|
84
|
+
- The legacy Navigation window / `Navigation Static` flag is deprecated. Add a **`NavMeshSurface`**
|
|
85
|
+
component to a root object; set its **Collect Objects** (All / Children / by layer) and **Agent Type**,
|
|
86
|
+
then **Bake**.
|
|
87
|
+
- Runtime: `surface.BuildNavMesh()` rebakes; combine with a data source for large worlds.
|
|
88
|
+
- `NavMeshModifier` overrides area/inclusion per-object; `NavMeshModifierVolume` does it by volume.
|
|
89
|
+
- Multiple agent sizes = multiple Agent Types (Navigation → Agents) + one surface each.
|
|
90
|
+
- Query without moving: `NavMesh.CalculatePath(from, to, areaMask, NavMeshPath)`; sample the mesh with
|
|
91
|
+
`NavMesh.SamplePosition` to snap a point onto it.
|
|
92
|
+
|
|
93
|
+
### Unreal (UE5, `RecastNavMesh`)
|
|
94
|
+
|
|
95
|
+
- A **`NavMeshBoundsVolume`** defines where navigation is generated; a `RecastNavMesh-Default` actor
|
|
96
|
+
appears (tune agent radius/height/step/slope in Project Settings → Navigation System / its details).
|
|
97
|
+
- **Runtime Generation** modes: **Static** (fastest, no runtime change), **Dynamic Modifiers Only**
|
|
98
|
+
(can only *remove* nav via modifiers at runtime), **Dynamic** (any change, rebuilds affected tiles).
|
|
99
|
+
- The mesh is **tiled**; runtime edits rebuild only overlapping tiles. Spawned geometry must have
|
|
100
|
+
collision + **Can Ever Affect Navigation** to influence it.
|
|
101
|
+
- `NavModifierVolume` + `NavArea` subclasses set cost/exclusion. Debug with the **`P`** key (Show
|
|
102
|
+
Navigation) in PIE.
|
|
103
|
+
|
|
104
|
+
## Common failure modes
|
|
105
|
+
|
|
106
|
+
- **Empty path**: start or end is off the mesh, or the two are on disconnected islands. Snap both to the
|
|
107
|
+
nearest polygon and test reachability before trusting the result.
|
|
108
|
+
- **Agents hug walls / stick in doors**: agent radius in the bake ≠ real collider radius.
|
|
109
|
+
- **Spawned wall ignored**: static bake — carve it, add a link, or switch to a dynamic/rebake mode.
|
|
110
|
+
- **Path exists but agent won't take a jump**: missing or wrong-direction off-mesh link.
|