Nine agent skills for building Makepad/Rust games, ported from majidmanzarpour/threejs-game-skills. Same director-routed workflow and premium bar; runtime rewritten for this fork's game crates. - makepad-game-director entrypoint, routing, continuity, asset probe - makepad-gameplay-systems loop, movers vs rigid bodies, input, camera, netplay - makepad-aaa-graphics-builder lighting, shaders, budget, visual scorecard - makepad-game-ui-designer HUD, menus, touch and XR UI - makepad-debug-profiler defect bisection and profiling - makepad-qa-release verification ladder, evidence, packaging - makepad-3d-generator CC0 model search, casts, procedural geometry - makepad-image-generator texgen textures, palettes, sky, icons - makepad-audio-generator sample bank, mixer, material impacts, 3D audio Written against the real APIs in libs/game/*, libs/sim and apps/arcade: the game.* verb table, GameRenderer adaptive quality, the packed 6-float GameMeshVertex layout, script_mod! splash styling, makepad-test driving, and the BUDGETS.md numbers. No paid generation API is required - the CC0 library plus seeded makepad-game-gen replaces them. Includes install.sh (Codex/Claude), validate-skills.sh and a repo-aware probe_assets.sh; both scripts verified against this checkout.
110 lines
7.3 KiB
Markdown
110 lines
7.3 KiB
Markdown
---
|
|
name: makepad-gameplay-systems
|
|
description: "Design and implement Makepad game loops: entities, movers, rigid bodies, terrain, input, camera, physics, AI brains, racing, scoring, netplay and feel. Use for core loop, level design, controls, collision, character/vehicle handling, NPC behaviour, multiplayer session, and game-feel work on makepad-game-sim, makepad-game-blocks, makepad-game-script and makepad-game-session."
|
|
---
|
|
|
|
# Makepad Gameplay Systems
|
|
|
|
Build the loop that makes it a game: something the player does, a reason to do it, and a state that ends.
|
|
|
|
## Before implementing
|
|
|
|
Write the contract down. Four lines, in `artifacts/game-progress.md`:
|
|
|
|
- **Core loop** — one sentence: what the player repeatedly does and why it escalates.
|
|
- **Win / lose** — both reachable, both testable, both observable in a log line.
|
|
- **Input map** — every action, on every target device you claim to support.
|
|
- **Camera scale** — how tall is the player in world units, how far is the camera. This decides asset scale, terrain scale, and readability, and it is expensive to change later.
|
|
|
|
Pick the route (script verbs vs Rust crate) per `makepad-game-director/references/architecture.md` before writing code.
|
|
|
|
## The simulation model
|
|
|
|
`makepad-game-sim` is Cx-free. **Every gameplay rule you write belongs in a Cx-free crate and gets a unit test.** If a rule needs a `Cx` to test, it is in the wrong place.
|
|
|
|
`GameWorld` holds entities and steps at a fixed `TICK_DT`. Never advance by a variable frame delta — it breaks netplay, replay, and determinism.
|
|
|
|
Movement comes in three kinds, and picking wrong is the most common design error here:
|
|
|
|
| Kind | Use for | API |
|
|
| --- | --- | --- |
|
|
| **Mover** (kinematic capsule) | players, NPCs, anything that should feel controlled | `capsule_mover_step`, `apply_mover_contacts` |
|
|
| **Rigid body** | debris, crates, ragdolls, anything that should feel thrown | `RigidDynamics`, `step_dynamics`, `rigid_impulse`, `rigid_spin` |
|
|
| **Scripted transform** | platforms, doors, lifts on rails | set position directly per tick |
|
|
|
|
A player on a rigid body feels floaty and catches on geometry. A crate on a mover feels weightless. Mixed control — a mover that is sometimes rigid — is how ragdoll works (`activate_ragdoll`, `ragdoll_active`, `ragdoll_body_poses`), and that transition must be explicit, not emergent.
|
|
|
|
Ground and collision helpers: `ground_y`, `ground_normal`, `deck_floor_under`, `box_floor_under`, `cast_ray`, `projectile_ccd`, `raycast_filter()`, `projectile_filter()`. Use `projectile_ccd` for anything fast — a bullet stepped discretely tunnels through walls, and this is not a bug you will find by playing.
|
|
|
|
Also in sim: `terrain`, `landform`, `water` (`WaterVolume`, `WaterWave`, buoyancy), `voxel`, `nav` (`NavMap`, `NavAgent`, `FlowField`), `decal` (`BulletDecal`), `particles`, `hud`, `player`, `sense`, `queries`.
|
|
|
|
Entity lookup is O(log n) binary search over a sorted-id `Vec`. Do not build a parallel `HashMap` index beside it.
|
|
|
|
## Building blocks — do not reimplement these
|
|
|
|
`makepad-game-blocks` already solved the hard handling problems. Reaching for raw physics when a block exists is the second most common error here.
|
|
|
|
- `Car` / `CarConfig` — vehicle handling, suspension, wheels
|
|
- `Character` / `CharacterConfig` / `CharacterPose` — humanoid locomotion and pose
|
|
- `Plane` / `PlaneConfig` — flight
|
|
- `Npc` / `NpcConfig` / `Personality` / `Activity` / `Poi` / `PoiSet` / `DoorUse` / `DAY_SECONDS` — daily-routine NPCs with points of interest
|
|
- `Brain` / `BrainKind` — wander, chase, patrol behaviour
|
|
- `RaceKit` / `Checkpoint` / `SpawnPoint` / `Standing` — laps, checkpoints, standings
|
|
- `DriveInput`, `RawInput`, `ControlSource`, `PlayerRig`, `Seat`, `Blocks`
|
|
|
|
Tune a `*Config` before writing new physics. If the config genuinely cannot express the feel, extend the block in `libs/game/blocks` — with a unit test — rather than bypassing it in the app.
|
|
|
|
## Input
|
|
|
|
`RawInput` and `ControlSource` abstract the source; `GameInputState` / `GamepadState` come from `makepad_platform::event::game_input`. Support keyboard, gamepad, and touch through the same action enum — branch at the mapping layer, never inside gameplay code.
|
|
|
|
Two things that get missed and are always caught in review:
|
|
|
|
- **Touch**: no hover, no right-click, fat fingers. Every action needs a touch path or the mobile claim is false.
|
|
- **XR**: `apps/arcade/src/xr_input.rs` exists. If the target includes Quest, controller input is not the same shape as gamepad input.
|
|
|
|
Deadzone analog sticks, normalise diagonal keyboard movement (or diagonals are faster — the classic bug), and buffer the jump input a few frames before ground contact.
|
|
|
|
## Feel
|
|
|
|
Feel is mostly timing, and timing is cheap. In rough order of return:
|
|
|
|
1. **Coyote time** — jump remains valid ~100 ms after leaving ground.
|
|
2. **Input buffering** — an action pressed just before it becomes legal still fires.
|
|
3. **Acceleration curves** — never set velocity directly from input for a character.
|
|
4. **Camera lag and lookahead** — the camera trails and leads; `CameraRig` in `makepad-game-render` owns this.
|
|
5. **Hit feedback** — `cam_shake`, a particle `burst`, an `sfx`, and a HUD flash on the same frame as the hit. All four, or the hit reads as nothing.
|
|
6. **Recovery frames** — a brief committed window after an action makes it feel weighty.
|
|
|
|
## Script route specifics
|
|
|
|
Verbs are listed in `architecture.md`; `makepad_game_script::api_text()` is authoritative. Constraints that shape design:
|
|
|
|
- **One cumulative 500k-instruction pool per tick**, shared by `on_tick` + timers + touch events, target ≤ 2 ms. Do not write a per-entity `on_tick`; iterate a batch inside one handler.
|
|
- **Options keys are typo-guarded.** A rejected key is a wrong key.
|
|
- **Use `rand` / `rand_range`**, the seeded verbs — never host RNG — or netplay desyncs.
|
|
- Untrusted scripts pass through `strip_capabilities` with a `Trust` level first. AI-authored code is untrusted.
|
|
|
|
## Multiplayer
|
|
|
|
`makepad-game-net` is **host-authoritative** LAN transport; `makepad-game-session` gives `HostSession`, `ClientSession`, `Session`, `SessionEvent`, replication, `MAX_PLAYERS`, and `drive_input_for(world, player)`.
|
|
|
|
Design rules: clients send *input*, never state. The host steps the sim and replicates. Anything derived from `std` float math or unseeded RNG will desync — use `makepad-game-math` and seeded `rng`. Content replicates as `(preset, seed, position)`, not vertex data.
|
|
|
|
`makepad-game-coedit` handles collaborative *editing* (leases, diff3, `Transaction`, `Refusal`) — a different problem from gameplay replication. Do not mix them.
|
|
|
|
## Verification
|
|
|
|
```bash
|
|
cargo test -p makepad-game-sim
|
|
cargo test -p makepad-game-blocks
|
|
cargo check -p <your app crate>
|
|
```
|
|
|
|
Assert on behaviour, not on internals: the mover clears a 0.5 m step and does not clear a 1.5 m wall; the car's body does not sink into the road; the lap counts once per crossing and not twice; the NPC reaches its POI within N ticks. Test the *composed* system when the claim is about composition — a walker entering a real generated room is one test, not two half-tests that both pass while the walker floats.
|
|
|
|
## Required reading
|
|
|
|
- `references/core-loop.md` — briefs, level structure, difficulty, state machine.
|
|
- `references/physics-and-movement.md` — mover vs rigid, collision, tuning tables.
|
|
- `references/input-and-camera.md` — action mapping, devices, camera rigs.
|