@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.
Files changed (78) hide show
  1. package/README.md +8 -2
  2. package/manifest.json +241 -2
  3. package/package.json +1 -1
  4. package/scripts/lib/domains.js +1 -0
  5. package/scripts/lib/recommend.js +4 -0
  6. package/skills/game-design/SKILL.md +170 -0
  7. package/skills/game-design/evals/README.md +13 -0
  8. package/skills/game-design/evals/cases.yaml +47 -0
  9. package/skills/game-design/references/mechanics-and-loops.md +78 -0
  10. package/skills/game-design/references/playtesting-and-scope.md +58 -0
  11. package/skills/game-design/references/progression-and-economy.md +70 -0
  12. package/skills/game-storytelling/SKILL.md +159 -0
  13. package/skills/game-storytelling/evals/README.md +28 -0
  14. package/skills/game-storytelling/evals/cases.yaml +51 -0
  15. package/skills/game-storytelling/references/branching-and-state.md +134 -0
  16. package/skills/game-storytelling/references/dialogue-tools.md +117 -0
  17. package/skills/game-storytelling/references/environmental-storytelling.md +71 -0
  18. package/skills/gamedev-multiplayer/SKILL.md +250 -0
  19. package/skills/gamedev-multiplayer/evals/README.md +12 -0
  20. package/skills/gamedev-multiplayer/evals/cases.yaml +44 -0
  21. package/skills/gamedev-multiplayer/references/godot-high-level-multiplayer.md +133 -0
  22. package/skills/gamedev-multiplayer/references/prediction-and-latency.md +104 -0
  23. package/skills/gamedev-multiplayer/references/unity-and-unreal-netcode.md +166 -0
  24. package/skills/gamedev-pathing/SKILL.md +242 -0
  25. package/skills/gamedev-pathing/evals/README.md +14 -0
  26. package/skills/gamedev-pathing/evals/cases.yaml +49 -0
  27. package/skills/gamedev-pathing/references/navmesh-workflow.md +110 -0
  28. package/skills/gamedev-pathing/references/search-algorithms.md +135 -0
  29. package/skills/gamedev-pathing/references/steering-and-avoidance.md +120 -0
  30. package/skills/gamedev-physics/SKILL.md +225 -0
  31. package/skills/gamedev-physics/evals/README.md +12 -0
  32. package/skills/gamedev-physics/evals/cases.yaml +54 -0
  33. package/skills/gamedev-physics/references/character-controllers.md +120 -0
  34. package/skills/gamedev-physics/references/determinism-and-queries.md +124 -0
  35. package/skills/gamedev-physics/references/layers-and-masks.md +115 -0
  36. package/skills/gamedev-shaders/SKILL.md +252 -0
  37. package/skills/gamedev-shaders/evals/README.md +14 -0
  38. package/skills/gamedev-shaders/evals/cases.yaml +45 -0
  39. package/skills/gamedev-shaders/references/effect-recipes.md +205 -0
  40. package/skills/gamedev-shaders/references/godot-shading-language.md +133 -0
  41. package/skills/gamedev-shaders/references/unity-and-unreal-shaders.md +169 -0
  42. package/skills/gamedev-shipping/SKILL.md +236 -0
  43. package/skills/gamedev-shipping/evals/README.md +9 -0
  44. package/skills/gamedev-shipping/evals/cases.yaml +55 -0
  45. package/skills/gamedev-shipping/references/mobile-and-desktop.md +127 -0
  46. package/skills/gamedev-shipping/references/stores-and-ci.md +162 -0
  47. package/skills/gamedev-shipping/references/web-export-headers.md +140 -0
  48. package/skills/godot/SKILL.md +319 -0
  49. package/skills/godot/evals/README.md +13 -0
  50. package/skills/godot/evals/cases.yaml +50 -0
  51. package/skills/godot/references/export-and-testing.md +102 -0
  52. package/skills/godot/references/gdscript-style.md +84 -0
  53. package/skills/godot/references/godot3-to-4-traps.md +89 -0
  54. package/skills/godot/references/nodes-scenes-resources.md +109 -0
  55. package/skills/level-design/SKILL.md +175 -0
  56. package/skills/level-design/evals/README.md +35 -0
  57. package/skills/level-design/evals/cases.yaml +58 -0
  58. package/skills/level-design/references/blockout-to-art.md +118 -0
  59. package/skills/level-design/references/guidance-and-readability.md +152 -0
  60. package/skills/level-design/references/pacing-and-encounters.md +142 -0
  61. package/skills/roast-me/tools/adapters/__pycache__/__init__.cpython-312.pyc +0 -0
  62. package/skills/roast-me/tools/adapters/__pycache__/base.cpython-312.pyc +0 -0
  63. package/skills/roast-me/tools/adapters/__pycache__/claude.cpython-312.pyc +0 -0
  64. package/skills/roast-me/tools/adapters/__pycache__/codex.cpython-312.pyc +0 -0
  65. package/skills/roast-me/tools/adapters/__pycache__/gemini.cpython-312.pyc +0 -0
  66. package/skills/roast-me/tools/adapters/__pycache__/registry.cpython-312.pyc +0 -0
  67. package/skills/unity/SKILL.md +282 -0
  68. package/skills/unity/evals/README.md +8 -0
  69. package/skills/unity/evals/cases.yaml +43 -0
  70. package/skills/unity/references/build-and-platforms.md +65 -0
  71. package/skills/unity/references/input-system.md +102 -0
  72. package/skills/unity/references/lifecycle-deep-dive.md +75 -0
  73. package/skills/unreal/SKILL.md +270 -0
  74. package/skills/unreal/evals/README.md +12 -0
  75. package/skills/unreal/evals/cases.yaml +45 -0
  76. package/skills/unreal/references/blueprint-cpp-parity.md +109 -0
  77. package/skills/unreal/references/gameplay-framework.md +95 -0
  78. package/skills/unreal/references/packaging.md +83 -0
@@ -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.
@@ -0,0 +1,135 @@
1
+ # Search algorithms & world representations
2
+
3
+ Depth for the "Search algorithms" and "Choosing a representation" sections of SKILL.md. Read this when
4
+ you are hand-rolling a search (a grid game, a custom graph, or a debugging session where the engine's
5
+ navmesh isn't enough).
6
+
7
+ ## Representations in detail
8
+
9
+ ### Uniform grid
10
+
11
+ Cells are nodes; neighbours are the adjacent cells. **4-connected** (N/E/S/W) gives Manhattan movement;
12
+ **8-connected** adds diagonals. On 8-connected grids, forbid *corner-cutting*: only allow a diagonal move
13
+ if both orthogonal neighbours are also walkable, or agents clip wall corners.
14
+
15
+ - Pros: trivial to build/edit, supports destructible/procedural terrain, weighted cells (mud=cost 3).
16
+ - Cons: node count explodes with resolution; raw paths look blocky and hug walls — smooth them (funnel).
17
+
18
+ ### Waypoint graph
19
+
20
+ Hand- or tool-placed nodes with explicit edges. Agents path node-to-node, then steer between.
21
+
22
+ - Pros: tiny node count, full designer control (patrol loops, cover-to-cover, racing lines).
23
+ - Cons: movement is confined to edges; anything off-graph is invisible. Poor for large open spaces —
24
+ an agent can't cut across a room the graph didn't anticipate.
25
+
26
+ ### Navmesh
27
+
28
+ A set of convex polygons covering the walkable surface. Search runs over the polygon adjacency graph
29
+ (few, large nodes), then the **funnel algorithm** pulls a straight path through the polygon corridor.
30
+
31
+ - Pros: compact, natural any-angle paths, handles 3D height/slopes, one representation for a whole level.
32
+ - Cons: needs a bake step; representing fine dynamic change means re-baking or carving.
33
+
34
+ ## A* — the workhorse
35
+
36
+ ```text
37
+ function A*(start, goal, h):
38
+ open = min-heap keyed by f; push start with g=0, f=h(start,goal)
39
+ gScore = { start: 0 }
40
+ came = {}
41
+ while open not empty:
42
+ current = open.pop() # lowest f
43
+ if current == goal: return reconstruct(came, current)
44
+ for (nbr, stepCost) in neighbours(current):
45
+ tentative = gScore[current] + stepCost
46
+ if tentative < gScore.get(nbr, +inf):
47
+ came[nbr] = current
48
+ gScore[nbr] = tentative
49
+ f = tentative + h(nbr, goal)
50
+ open.push_or_decrease(nbr, f)
51
+ return NO_PATH # goal unreachable
52
+ ```
53
+
54
+ `f(n) = g(n) + h(n)`: `g` is known cost from start, `h` is the estimated cost to goal.
55
+
56
+ - Use a **binary heap** (or pairing heap) for the open set — a linear scan is the classic "A* is slow" bug.
57
+ - Track a closed/visited set (or the `gScore` map above) so nodes aren't re-expanded needlessly.
58
+ - `neighbours` returns `(node, stepCost)`; diagonal step cost is `√2` (~1.414), not 1.
59
+
60
+ ### Heuristics & admissibility
61
+
62
+ - **Admissible**: `h(n)` never *over*-estimates the true remaining cost ⇒ A* returns the optimal path.
63
+ - **Consistent (monotone)**: `h(n) ≤ stepCost(n,m) + h(m)` for every edge ⇒ no node is ever re-opened
64
+ (strictly stronger than admissible). Standard grid/navmesh heuristics below are both.
65
+ - Match `h` to the movement:
66
+ - 4-connected grid → **Manhattan** `|dx|+|dy|`.
67
+ - 8-connected grid → **octile** `max(dx,dy) + (√2−1)·min(dx,dy)`.
68
+ - any-angle / navmesh → **Euclidean** `√(dx²+dy²)`.
69
+ - **Tie-breaking**: many nodes share the same `f`, giving ugly wandering paths. Break ties toward lower
70
+ `h`, or nudge `h` up by a tiny factor (`h *= 1.0 + ε`) so the search leans goalward. This trades strict
71
+ optimality for straighter, faster searches — usually worth it.
72
+
73
+ ### Weighted / greedy A*
74
+
75
+ `f = g + w·h` with `w > 1` inflates the heuristic: fewer expansions, path at most `w×` optimal. Good when
76
+ frame budget beats perfection. `w → ∞` degenerates to **greedy best-first** (fast, often ugly, not optimal).
77
+
78
+ ## Dijkstra
79
+
80
+ A* with `h = 0`: expands uniformly outward by cost. Reach for it when:
81
+
82
+ - There is **no single target** — find the nearest of several exits/items in one search.
83
+ - You need the **whole cost field** from a source (the basis of flow fields — see steering reference).
84
+
85
+ Slower than A* to one goal because it explores in all directions. **BFS** is the unweighted special case
86
+ (all step costs equal) — fine for simple grids, but Dijkstra/A* generalize to terrain costs.
87
+
88
+ ## JPS (Jump Point Search)
89
+
90
+ An A* accelerator for **uniform-cost grids only** (every walkable cell same cost). It exploits grid
91
+ symmetry: instead of expanding every cell, it "jumps" in a straight line until it hits a *jump point* (a
92
+ cell with a forced neighbour caused by an obstacle). Often 10×+ fewer node expansions with the **same
93
+ optimal** result, and it stores fewer nodes.
94
+
95
+ - Requires a uniform-cost, 8-connected grid; **not** applicable to weighted terrain or navmeshes.
96
+ - Pairs well with JPS+ / precomputed jump distances for static maps.
97
+
98
+ ## Hierarchical / HPA*
99
+
100
+ For very large maps where a single flat A* misses the frame budget:
101
+
102
+ 1. Partition the map into **clusters** (e.g. 10×10 tiles).
103
+ 2. Precompute **entrances** between adjacent clusters and intra-cluster distances (an abstract graph).
104
+ 3. Search the small abstract graph cluster-to-cluster, then refine each segment with a local A* only
105
+ when/where the agent actually reaches it.
106
+
107
+ Trades a little path optimality for a large speedup and enables **path caching** and streaming. Reach for
108
+ it at thousands-of-tiles scale, or when many agents share long routes.
109
+
110
+ ## Any-angle paths (Theta*)
111
+
112
+ Grid A* paths bend only at 45°/90°. **Theta\*** relaxes this: when expanding, if the parent has
113
+ line-of-sight to the neighbour, it links directly to the parent instead of the current cell — yielding
114
+ straight, any-angle paths without a separate smoothing pass. Cheaper alternative: run grid A* then smooth.
115
+
116
+ ## Path smoothing — the funnel (string-pulling)
117
+
118
+ Raw grid/navmesh paths waste motion. Two fixes:
119
+
120
+ - **Line-of-sight smoothing** (grids): walk the path; if node `i` has clear LoS to node `i+2`, drop `i+1`.
121
+ Repeat. Removes staircase kinks cheaply.
122
+ - **Funnel algorithm** (navmesh): given the polygon corridor, pull a taut string through the shared
123
+ edges (portals), advancing left/right apexes — produces the shortest path inside the corridor. This is
124
+ what quality navmesh systems run after the polygon search; emulate it if you build your own.
125
+
126
+ ## Choosing quickly
127
+
128
+ | Situation | Pick |
129
+ | --- | --- |
130
+ | Free-roaming characters, 3D or open 2D | Navmesh + A* over polys + funnel |
131
+ | Tile-locked / destructible / RTS grid | Grid + A* (or JPS if uniform cost) |
132
+ | Constrained routes, patrols, rails | Waypoint graph + A* |
133
+ | Nearest of many goals / cost field | Dijkstra |
134
+ | Thousands of tiles, long shared routes | HPA* / hierarchical |
135
+ | Many agents → one goal | Flow field (steering reference) |
@@ -0,0 +1,120 @@
1
+ # Steering, local avoidance & flow fields
2
+
3
+ Depth for the "Steering & local avoidance" section of SKILL.md. Steering is the **local** layer: given a
4
+ global path (from A*/navmesh), produce a per-frame velocity that follows it while dodging neighbours. All
5
+ formulas below assume a max speed and a max steering force (acceleration clamp).
6
+
7
+ ## The steering model (Reynolds)
8
+
9
+ Each behaviour returns a **steering force** = `desired_velocity − current_velocity`, clamped to
10
+ `max_force`. Apply it as acceleration, integrate velocity (clamped to `max_speed`), integrate position.
11
+
12
+ ```text
13
+ steering = clamp(desired - velocity, max_force)
14
+ velocity = clamp(velocity + steering, max_speed)
15
+ position = position + velocity * dt
16
+ ```
17
+
18
+ ## Primitives (desired velocity per behaviour)
19
+
20
+ - **Seek**: `desired = normalize(target - pos) * max_speed`.
21
+ - **Flee**: seek negated — `desired = normalize(pos - target) * max_speed`.
22
+ - **Arrive**: seek that ramps down inside a slowing radius, so the agent settles instead of orbiting:
23
+ ```text
24
+ to_target = target - pos; dist = length(to_target)
25
+ speed = (dist < slow_radius) ? max_speed * dist/slow_radius : max_speed
26
+ desired = to_target/dist * speed
27
+ ```
28
+ - **Pursue**: seek the target's **predicted** position: `predict = target_pos + target_vel * T`, where
29
+ `T` scales with distance (`T = dist / max_speed`). **Evade** = flee that prediction.
30
+ - **Wander**: keep a point on a small circle projected ahead of the agent, jitter it slightly each frame,
31
+ seek it — gives smooth, non-robotic idle motion (unlike raw random which twitches).
32
+ - **Obstacle avoidance**: cast a probe (feeler) ahead; if it hits a static obstacle, add a lateral force
33
+ away from the obstacle's center. This is a *steering* dodge, distinct from navmesh carving.
34
+
35
+ ## Combining behaviours
36
+
37
+ - **Weighted sum**: `total = Σ wᵢ · forceᵢ`, then clamp. Simple; can average into indecision (two forces
38
+ cancel and the agent freezes between them).
39
+ - **Priority / arbitration**: evaluate in order (avoidance → separation → path-follow → cohesion) and
40
+ take the first non-trivial force, or accumulate with a running-force budget until clamp is hit. More
41
+ robust for "avoidance must win".
42
+
43
+ ## Path following
44
+
45
+ Do **not** seek the far goal — seek a moving point along the corridor:
46
+
47
+ 1. Find the point on the path polyline nearest the agent.
48
+ 2. Advance a **look-ahead** distance along the path from there → target point.
49
+ 3. **Arrive** at that target; when close to the final waypoint, arrive at the true goal.
50
+
51
+ Look-ahead too short = the agent hugs corners and jitters; too long = it cuts corners and leaves the
52
+ corridor. Tune to roughly one agent radius + a fraction of current speed. Engine agents
53
+ (`NavigationAgent.get_next_path_position`, `NavMeshAgent`) do this internally — you feed the corridor,
54
+ they hand back the next point.
55
+
56
+ ## Flocking (boids)
57
+
58
+ Three neighbour-based forces within a perception radius; sum with weights:
59
+
60
+ - **Separation** — steer away from the average offset to close neighbours (usually the strongest weight;
61
+ prevents overlap).
62
+ - **Alignment** — steer toward the average heading of neighbours (group moves as one).
63
+ - **Cohesion** — steer toward the average position of neighbours (group stays together).
64
+
65
+ Typical starting weights: separation 1.5, alignment 1.0, cohesion 1.0 — then tune. Combine with
66
+ path-following (the flock follows a global path while holding formation).
67
+
68
+ ## Local avoidance: RVO / ORCA
69
+
70
+ Reynolds separation is reactive and oscillates in crowds. **Velocity-obstacle** methods are the modern
71
+ answer:
72
+
73
+ - A **velocity obstacle (VO)** for a neighbour is the set of your velocities that would collide with it
74
+ within a time horizon. **RVO** (Reciprocal VO) assumes *both* agents share the avoidance effort, so
75
+ each takes half the correction — this kills the "two agents dance side-to-side" oscillation.
76
+ - **ORCA** (Optimal Reciprocal Collision Avoidance) formulates each neighbour as a half-plane constraint
77
+ on your velocity and picks, via linear programming, the collision-free velocity nearest your desired
78
+ one. It scales to dense crowds and is what most engine crowd systems implement.
79
+ - Key idea: avoidance adjusts the **intended velocity before you move**. It is *not* collision response
80
+ — the physics solver (see `gamedev-physics`) is the last-resort backstop when agents still overlap
81
+ under pressure. Overlap under crush is expected, not a bug.
82
+
83
+ **Per-engine:** Godot `NavigationAgent` avoidance (`avoidance_enabled` + `set_velocity` →
84
+ `velocity_computed` signal, RVO2-based). Unity `NavMeshAgent.obstacleAvoidanceType` (quality levels,
85
+ RVO). Unreal **Detour Crowd** manager / `DetourCrowdAIController` (RVO/ORCA-style for many pawns);
86
+ `RVOAvoidance` on `CharacterMovementComponent` for a lighter option.
87
+
88
+ ## Flow fields — many agents, one goal
89
+
90
+ When hundreds/thousands of agents share a goal (RTS swarm, tower-defense creeps), one A* *per agent* is
91
+ wasteful. Build the field once, then every agent samples O(1):
92
+
93
+ 1. **Cost field**: per-cell traversal cost (walls = impassable, terrain = weighted).
94
+ 2. **Integration field**: run **Dijkstra/BFS from the goal** outward, storing each cell's cheapest total
95
+ cost to reach the goal. (This is why Dijkstra, not A* — you want the whole field, not one path.)
96
+ 3. **Flow field**: for each cell, store a direction vector pointing to the lowest-cost neighbour.
97
+ 4. **Per agent, per frame**: sample the flow vector at the agent's cell, use it as the desired direction,
98
+ then layer local avoidance (separation/RVO) so they don't stack.
99
+
100
+ Rebuild only when the goal or the cost field changes. Multiple goals = seed the Dijkstra from all goal
101
+ cells at once (agents flow to the nearest). Flow fields also smooth naturally (bilinear-sample the
102
+ vectors across cells).
103
+
104
+ When to choose it: **many agents → few goals**. For a handful of agents to distinct goals, per-agent A*
105
+ or navmesh is simpler and gives better individual paths.
106
+
107
+ ## Combining the two layers (the whole loop)
108
+
109
+ ```text
110
+ on new goal: path = search(start, goal) # global, occasional
111
+ every frame per agent:
112
+ look = lookahead_point(path, pos) # follow the corridor
113
+ desired = arrive(look) # path following
114
+ desired += separation + rvo(neighbours) # local avoidance
115
+ velocity = integrate(clamp(desired)) # steer, don't teleport
116
+ move(velocity); resolve_collisions() # physics backstop
117
+ ```
118
+
119
+ Global path answers *where*; steering + avoidance answer *how to move now*; physics is the final contact
120
+ resolver. Keep them distinct and each stays cheap and debuggable.
@@ -0,0 +1,225 @@
1
+ ---
2
+ name: gamedev-physics
3
+ description: "Use when working with a game engine's physics — rigid bodies, colliders, collision layers/masks, character controllers, joints, forces vs impulses, raycasts/shapecasts, or when physics is unstable. Triggers: \"my character falls through the floor\", \"bullet tunnels through walls at high speed\", \"rigidbody jitters / vibrates / explodes\", \"objects that should collide pass through each other\", \"which collision layer and mask do I set\", \"kinematic vs dynamic body\", \"move_and_slide / CharacterBody\", \"AddForce vs impulse vs setting velocity\", \"physics in FixedUpdate or _physics_process\", \"slope and step detection\", \"trigger / Area overlap not firing\", Godot RigidBody/StaticBody/CharacterBody/Area, Unity Rigidbody/Collider/CharacterController, Unreal Chaos/collision presets. NOT rendering, materials, or visual jitter from shaders (use gamedev-shaders); NOT AI navigation, pathfinding, or navmesh (use gamedev-pathing)."
4
+ tags: [physics, collision, rigidbody, character-controller, colliders]
5
+ recommends: [godot, unity, unreal, gamedev-pathing]
6
+ profiles: [full]
7
+ origin: risco
8
+ ---
9
+
10
+ # Game physics & collision
11
+
12
+ Make bodies collide, move, and stay stable across Godot, Unity, and Unreal. This skill owns the
13
+ mental model (body types, colliders, layers/masks, character controllers, joints, queries,
14
+ determinism) and maps every concept onto each engine's real API. When physics "feels broken" —
15
+ jitter, tunneling, sinking, ghost collisions — the cause is almost always one of: wrong body type,
16
+ a layer/mask mismatch, or moving/integrating outside the fixed physics tick. Check those first.
17
+
18
+ ## Version contract — read first (never emit these)
19
+
20
+ | Engine | Target | Banned / deprecated | Use instead |
21
+ | --- | --- | --- | --- |
22
+ | **Godot** | 4.x | `move_and_slide(velocity, up, ...)` with arguments (that is Godot 3) | Godot 4 `move_and_slide()` takes **no args**; set the `velocity` property first |
23
+ | Godot | 4.x | `KinematicBody2D/3D`, `RayShape`, `WorldMarginShape`, `linear_velocity *= delta` before `move_and_slide()` | `CharacterBody2D/3D`, `SeparationRayShape2D/3D`, `WorldBoundaryShape2D/3D`; `move_and_slide()` is already delta-scaled |
24
+ | **Unity** | Unity 6 (6000.x) | `Rigidbody.velocity` / `Rigidbody2D.velocity`, `rb.drag`, `rb.angularDrag` | `linearVelocity` (+ `linearVelocityX/Y`), `linearDamping`, `angularDamping`; new `AddForceX/Y` |
25
+ | Unity | Unity 6 | moving a dynamic `Rigidbody` from `Update`; `transform.position =` on a physics body | move in `FixedUpdate`; use `MovePosition`/`AddForce`, never write `transform` on a simulated body |
26
+ | **Unreal** | UE5 (Chaos) | any PhysX/APEX API, `bUseAsyncScene`, `NvCloth` | Chaos is the only physics backend in UE5; use `UPrimitiveComponent` physics + `PhysicsConstraintComponent` |
27
+
28
+ If you are unsure a symbol exists in the user's exact version, say so instead of inventing it.
29
+
30
+ ## When to use / When NOT to use
31
+
32
+ **Use when:** designing which body type a thing should be; something falls through, sinks into, or
33
+ passes through geometry; a rigid body jitters, drifts, or launches; setting up collision
34
+ layers/masks/channels; building or fixing a character/player controller; adding joints; casting
35
+ rays/shapes or overlap tests; or physics behaves differently at different frame rates.
36
+
37
+ **When NOT to use (delegate):**
38
+ - Visual-only wobble, shader/vertex animation, material or lighting issues → **gamedev-shaders**.
39
+ - AI moving along a path, navmesh, A*, steering, avoidance → **gamedev-pathing** (physics only
40
+ *executes* the move; pathing decides *where*).
41
+ - Deep, engine-specific project setup / editor / build questions → **godot**, **unity**, **unreal**.
42
+ - Netcode, lockstep sync, server-authoritative movement → **gamedev-multiplayer** (this skill gives
43
+ you the *deterministic fixed-tick* foundation multiplayer builds on).
44
+
45
+ ## 1. Body types — pick before you touch anything
46
+
47
+ Three categories exist in every engine; choosing wrong is the root of most bugs.
48
+
49
+ - **Static** — never moves, infinite mass, cheap. Level geometry, walls, floors. Do **not** move a
50
+ static body every frame; the broadphase caches it. If it must move, it is not static.
51
+ - **Kinematic / character** — moved *by you* (code/animation), pushes dynamics but is not pushed by
52
+ them, ignores forces and gravity unless you add them. Players, moving platforms, doors, elevators.
53
+ - **Dynamic (rigid)** — fully simulated: gravity, forces, impulses, collision response. Crates,
54
+ ragdolls, debris, vehicles. You influence it with forces/impulses, never by writing its transform.
55
+
56
+ **Move each the right way:**
57
+
58
+ | | Static | Kinematic / character | Dynamic |
59
+ | --- | --- | --- | --- |
60
+ | Godot | `StaticBody2D/3D` (leave still) | `CharacterBody2D/3D` + `move_and_slide()`; `AnimatableBody2D/3D` for platforms (set `sync_to_physics`) | `RigidBody2D/3D` (`apply_impulse`, `apply_force`) |
61
+ | Unity | Collider, no Rigidbody (or `Rigidbody2D` bodyType Static) | `CharacterController.Move()`, or `Rigidbody.isKinematic=true` + `MovePosition` in `FixedUpdate` | `Rigidbody` (`AddForce`) |
62
+ | Unreal | `Static`/`Movable` mobility, Simulate Physics off | `Character` + `CharacterMovementComponent`; movable component moved by code | Simulate Physics on (`SetSimulatePhysics(true)`) |
63
+
64
+ A Godot `RigidBody` can be temporarily frozen: set `freeze = true` with `freeze_mode =
65
+ FREEZE_MODE_KINEMATIC` to move it by transform without waking the solver wrongly. Prefer
66
+ `AnimatableBody` for a permanent kinematic mover.
67
+
68
+ ## 2. Colliders / shapes
69
+
70
+ Collider ≠ visual mesh. Give every body a **separate, simpler** collision shape.
71
+
72
+ - **Primitives** (box, sphere/circle, capsule) — cheapest and most stable. Prefer a **capsule** for
73
+ characters (rounded ends slide over steps and seams). Use these whenever possible.
74
+ - **Convex hull** — dynamic bodies can use a convex approximation; a concave prop needs **multiple
75
+ convex pieces** (a compound), not one concave hull.
76
+ - **Concave / trimesh** — exact triangle mesh, but **static-only** in every engine. A moving
77
+ concave-mesh collider is a top cause of tunneling and solver blowups. Never put a trimesh on a
78
+ dynamic body.
79
+ - **Compound** — several child shapes on one body. Godot: multiple `CollisionShape` children. Unity:
80
+ multiple `Collider` components. Unreal: multiple primitives / a body setup.
81
+ - **Triggers / areas** — detect overlap, **no physical response**. Godot `Area2D/3D` (signals
82
+ `body_entered` / `area_entered`); Unity collider `Is Trigger` (`OnTriggerEnter/Stay/Exit`); Unreal
83
+ set response to **Overlap** + `Generate Overlap Events` (`OnComponentBeginOverlap`). Use for
84
+ pickups, damage zones, checkpoints, sensors.
85
+
86
+ ## 3. Collision layers & masks — the #1 confusion
87
+
88
+ **Layer = "what I am." Mask = "what I scan for."** They are separate bit sets.
89
+
90
+ - **Godot:** two bodies interact when **one's `collision_mask` includes the other's
91
+ `collision_layer`** — it is an **OR**, so detection can be asymmetric (A sees B without B seeing
92
+ A). Keep them symmetric unless you deliberately want one-way detection. An `Area`'s `mask` decides
93
+ what it detects; its `layer` decides what detects *it*.
94
+ - **Unity:** GameObject **Layers** + the **Layer Collision Matrix** (Project Settings → Physics)
95
+ decide which layer pairs collide. Raycasts/overlaps take a `LayerMask` argument. Toggle a pair at
96
+ runtime with `Physics.IgnoreLayerCollision`.
97
+ - **Unreal:** **Object Type** (channel) = what I am; per-channel **Response** = Block / Overlap /
98
+ Ignore. Package as a reusable **Collision Preset**. Trace channels (Visibility, Camera) are for
99
+ queries; object channels are for physical collision.
100
+
101
+ Full worked examples (player/enemy/pickup/wall, bit math, one-way platforms) →
102
+ `references/layers-and-masks.md`.
103
+
104
+ ## 4. Character controllers
105
+
106
+ Two philosophies — decide up front, don't mix:
107
+
108
+ - **Kinematic character** (recommended for most players): you compute velocity and move via a
109
+ sweeping helper that resolves collisions and slides. Precise, snappy, no solver fighting. Godot
110
+ `CharacterBody`, Unity `CharacterController`, Unreal `CharacterMovementComponent`.
111
+ - **Dynamic (rigidbody) character**: physical pushing/being-pushed for free, but needs high friction
112
+ or a physics material, angular constraints (freeze rotation), and tuning to stop tipping/sliding.
113
+
114
+ Godot 4 (2D — the 3D version is identical with `Vector3` and `get_gravity()`):
115
+
116
+ ```gdscript
117
+ extends CharacterBody2D
118
+
119
+ const SPEED := 300.0
120
+ const JUMP_VELOCITY := -400.0
121
+
122
+ func _physics_process(delta: float) -> void:
123
+ if not is_on_floor():
124
+ velocity += get_gravity() * delta # accumulate accel: scale by delta
125
+ if Input.is_action_just_pressed("jump") and is_on_floor():
126
+ velocity.y = JUMP_VELOCITY
127
+ var dir := Input.get_axis("move_left", "move_right")
128
+ velocity.x = dir * SPEED if dir else move_toward(velocity.x, 0.0, SPEED)
129
+ move_and_slide() # Godot 4: NO args, uses `velocity`, already delta-scaled
130
+ ```
131
+
132
+ `is_on_floor()` / `is_on_wall()`, `floor_max_angle` (slope limit), `floor_snap_length` (stick to
133
+ ground on ramps/stairs), and `up_direction` handle ground/slope/step behavior. `get_gravity()` is
134
+ Godot 4.3+; on older 4.x read `ProjectSettings` gravity. Full per-engine controllers (Unity
135
+ `CharacterController` + custom gravity, Unreal movement modes, ground detection, slopes, steps,
136
+ moving platforms) → `references/character-controllers.md`.
137
+
138
+ ## 5. Joints/constraints & how to apply motion
139
+
140
+ **Joints** connect two bodies with a constraint. Godot: `PinJoint`, `HingeJoint3D`, `SliderJoint3D`,
141
+ `Generic6DOFJoint3D`, `DampedSpringJoint2D`. Unity: `HingeJoint`, `FixedJoint`, `SpringJoint`,
142
+ `ConfigurableJoint`, `CharacterJoint`. Unreal: `PhysicsConstraintComponent` (one 6-DOF constraint
143
+ covers hinge/slider/ball). Keep connected bodies' **mass ratios close** — a heavy body chained to a
144
+ light one is the classic joint-explosion.
145
+
146
+ **Force vs impulse vs direct velocity** (apply all in the fixed tick):
147
+
148
+ | Want | Use | Godot | Unity | Unreal |
149
+ | --- | --- | --- | --- | --- |
150
+ | Continuous push (thrust, wind), mass-scaled | **Force** | `apply_central_force` | `AddForce(f, Force)` | `AddForce` |
151
+ | Instant kick (jump, explosion, hit), mass-scaled | **Impulse** | `apply_central_impulse` | `AddForce(f, Impulse)` | `AddImpulse` |
152
+ | Instant velocity change, **ignoring mass** | mass-independent impulse | set `linear_velocity` | `AddForce(f, VelocityChange)` | `SetPhysicsLinearVelocity` |
153
+
154
+ Setting velocity directly on a *dynamic* body teleports its momentum and can fight the solver — fine
155
+ for character/kinematic bodies, use sparingly on dynamics (prefer forces/impulses).
156
+
157
+ ## 6. Queries — raycasts, shapecasts, overlaps
158
+
159
+ Run queries from the **fixed physics tick** so results match the simulated state.
160
+
161
+ - **Godot:** get `get_world_2d().direct_space_state` (or 3d), build
162
+ `PhysicsRayQueryParameters2D/3D` / `PhysicsShapeQueryParameters2D/3D`, call `intersect_ray`,
163
+ `intersect_shape`, `cast_motion`. Node helpers: `RayCast2D/3D`, `ShapeCast2D/3D`. Queries respect
164
+ `collision_mask`.
165
+ - **Unity:** `Physics.Raycast`, `SphereCast`/`CapsuleCast`, `OverlapSphere`, `CheckSphere` (2D:
166
+ `Physics2D.*`). Always pass a `LayerMask`; prefer non-allocating `RaycastNonAlloc`/`RaycastAll`.
167
+ - **Unreal:** `LineTraceSingleByChannel`, `SweepSingleByChannel`, `OverlapMultiByChannel` (and
168
+ `...ByObjectType`). Use a **trace channel**, set the query params (`bTraceComplex`).
169
+
170
+ Details, snippets, and the tunneling-safe shapecast pattern → `references/determinism-and-queries.md`.
171
+
172
+ ## 7. Determinism & stability
173
+
174
+ The rules that keep physics from jittering, drifting, or tunneling:
175
+
176
+ 1. **Physics lives in the fixed tick.** Godot `_physics_process(delta)`, Unity `FixedUpdate()`,
177
+ Unreal substepping / async physics tick. Read input in the frame update, *apply* it in the fixed
178
+ tick. Never simulate in the render frame.
179
+ 2. **Scale by delta.** Multiply forces/accelerations/manual position changes by the tick's `delta` /
180
+ `fixedDeltaTime` — but **not** the output of a helper that already integrates time (Godot
181
+ `move_and_slide()`, Unity `CharacterController.Move` when you pass a per-second velocity).
182
+ 3. **CCD for fast/thin things.** Small fast projectiles + thin walls tunnel. Enable Continuous
183
+ Collision Detection: Godot `continuous_cd`, Unity `collisionDetectionMode = Continuous*`, Unreal
184
+ `Use CCD`. Enable only on the fast bodies (it costs). Or use a shapecast/raycast-then-move.
185
+ 4. **Keep mass ratios sane.** Stacks and joints between wildly different masses (100:1+) blow up.
186
+ 5. **Never scale a collider at runtime**, and never write `transform`/`position` on a dynamic body —
187
+ both corrupt the broadphase and cause explosions or ghost collisions. Move via the API.
188
+ 6. **Interpolate for smooth visuals** at high frame rates: Godot physics interpolation, Unity
189
+ `Rigidbody.interpolation = Interpolate`. This is visual only — it never changes the simulation.
190
+
191
+ Deeper: fixed-timestep math, substepping, sleeping, why `transform` writes break things →
192
+ `references/determinism-and-queries.md`.
193
+
194
+ ## Guardrails / gotchas
195
+
196
+ - Falling through a thin floor at speed → CCD off, or moving in the render frame. Fix both.
197
+ - A moving platform doesn't carry the player → use a kinematic mover (Godot `AnimatableBody` with
198
+ `sync_to_physics`, Unity kinematic Rigidbody `MovePosition`), not a static body.
199
+ - "Ghost" collisions / catching on seams between tiles → merge colliders or use a capsule character.
200
+ - Trigger never fires → in Unity a trigger pair needs at least one **Rigidbody**; in Godot the
201
+ Area's `mask` must include the body's `layer`; in Unreal enable Generate Overlap Events on **both**.
202
+ - Rigidbody vibrates against the ground → mass ratio, too-soft solver iterations, or you're also
203
+ writing its transform. Stop writing the transform.
204
+ - Character launches off ramps → enable floor snapping (`floor_snap_length` / stop-on-slope).
205
+
206
+ ## Related skills
207
+
208
+ - **godot** / **unity** / **unreal** — engine setup, editor, language, and build specifics.
209
+ - **gamedev-pathing** — decides *where* to move; this skill *executes* the move and collisions.
210
+ - **gamedev-multiplayer** — builds on the deterministic fixed-tick foundation here.
211
+ - **gamedev-shaders** — for visual/material issues that only look like physics.
212
+
213
+ ## Checklist
214
+
215
+ - [ ] Every moving thing has the right **body type** (static / kinematic / dynamic).
216
+ - [ ] Colliders are **primitives/capsules**; no trimesh on a dynamic body; compound = many convex.
217
+ - [ ] Layers/masks (or channels/presets) set so exactly the intended pairs collide; triggers use
218
+ overlap/Area, not solid response.
219
+ - [ ] All physics motion and queries run in the **fixed tick**; deltas scaled correctly (helper
220
+ output not double-scaled).
221
+ - [ ] Forces/impulses/velocity chosen deliberately; no `transform`/`position` writes on dynamics; no
222
+ runtime collider scaling.
223
+ - [ ] Fast/thin bodies have **CCD**; character has floor/slope/step handling; mass ratios are sane.
224
+ - [ ] Correct API for the stated engine **version** (Godot 4 `move_and_slide()` no args; Unity 6
225
+ `linearVelocity`; UE5 Chaos — no PhysX).
@@ -0,0 +1,12 @@
1
+ # Eval harness — `gamedev-physics` skill
2
+
3
+ These cases are read by the rsc skill-eval harness (a Claude agent with the full catalog loaded), or
4
+ by hand. They check two things. **Triggering:** the description fires on real physics symptoms —
5
+ tunneling, jitter, layer/mask filtering, forces-vs-impulses, character controllers — and stays quiet
6
+ on near-misses, routing shader shimmer to `gamedev-shaders`, navmesh/pathfinding to
7
+ `gamedev-pathing`, editor/export setup to `godot`, netcode to `gamedev-multiplayer`, and gameplay
8
+ design to `game-design`. **Capability:** with `SKILL.md` and its references loaded, the answer uses
9
+ the version-correct API (Godot 4 `move_and_slide()` with no args, Unity 6 `linearVelocity`, UE5
10
+ Chaos), puts simulation in the fixed tick, scales delta correctly, and picks the right body type /
11
+ collider / layer-mask / CCD fix. Routing is non-deterministic — run 3–5 trials per prompt and pass
12
+ on a majority; capability is judged by reading the output against the `must_include` bullets.