makepad/libs/game/session
Admin 1062bf105c PlayerRig prefab: a playable character in and out of a car in four lines
A game gets walking, jumping, a follow camera, mounting and dismounting without
ever touching a camera itself:

  blocks.player_rigs.insert(PlayerId::LOCAL, PlayerRig::new(character_id));
  blocks.tick_player_rigs(&mut world, &raw_inputs);
  blocks.pre_step(&mut world);
  rig.apply_camera(&mut world);

PlayerRig::tick fills BOTH the walking and driving fields of DriveInput every
tick, so changing seat needs no second code path — whichever block is listening
reads its own fields.

The character craft already existed in character.rs and passed; what was missing
was anything consuming it. The camera side is CameraConfig::on_foot (rotates
faster than it translates, so position lag reads as weight while aim stays
crisp; no recentring — the player aims it and it stays) and ::in_vehicle (lower,
35% speed pullback, recentring after a 0.9s delay so it yields to your hand and
only takes over once you let go). Mounting blends with smoothstep.

FOUR REAL BUGS, three of which no existing test could have caught:
- The camera blend never interpolated: `blend / blend.max(0.0001)` is always
  1.0, so the rig held its old shape for the whole transition and snapped at the
  end — precisely the cut the blend exists to prevent. It needed the blend's
  ORIGINAL length, not the remainder
- The blend timer froze when its subject vanished, because tick bailed on the
  entity lookup before advancing time. A car despawning mid-blend would have
  frozen the camera permanently
- **heading_to_right returned LEFT** — the exact negation of forward x up. Its
  doc said "+X" and its test asserted `r.x < -0.99 || r.x > 0.99`, which accepts
  BOTH SIGNS and so could never fail. A test that cannot fail is worse than no
  test, because it is counted as coverage
- The renderer and the sim use opposite yaw AND pitch signs. Derived by matching
  the two eye-position expressions component-wise rather than guessing; the
  conversion now lives in heading.rs as heading_to_camera_yaw/pitch — ONE named
  boundary, never a negation at a call site. That discipline is why heading.rs
  exists, and this is the same bug class that produced the reversed steering

Two changes from peer review: DriveInput.run is f32 so stick deflection gives a
real walk-to-run continuum instead of snapping at a threshold; and pre_step
gained a modality gate, because a player owning both a character and a car was
driving AND walking simultaneously — the stick steering your car was also
walking the body you left in the seat, invisible until you got out somewhere you
had never been.

108 tests across sim and blocks (from 88), including the full walk-in-drive-out
journey, analog deflection landing within 10% of the true midpoint, a reload
keeping you seated with the camera where it was, a vanished car putting you back
on your feet with working controls, and the affordance prompt agreeing with the
button at every distance on the approach.

Known gaps, reported not hidden: dismount picks a side but doesn't check the
GROUND there (spot_clear tests overlap, not floor), recentring uses velocity
heading so slow reversing can hunt, and PlayerRig assumes one local player
because world.cam_yaw is a single device field — split-screen needs a per-player
write path that doesn't exist yet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 19:15:06 +02:00
..
src PlayerRig prefab: a playable character in and out of a car in four lines 2026-08-03 19:15:06 +02:00
tests Arcade M2b: multiplayer — players in the sim, tiered replication, host/join 2026-08-03 01:21:44 +02:00
Cargo.toml Arcade M2b: multiplayer — players in the sim, tiered replication, host/join 2026-08-03 01:21:44 +02:00