Tune the follow camera
Goal
A camera that sits where you want it: a longer or shorter boom, a different pivot height, a pitch range that suits your level, and smooth body turning when the camera moves behind the character.
Files you will edit
src/game.tssrc/systems/locomotion.ts
Steps
Know who owns what. The guest states an intent; the host owns the arm.
ctx.camera.follow(hero, { yaw, pitch, distance, height })writes onecamera-staterecord — the orbit pivot, the direction the player is looking, the boom length — and the engine builds a spring arm from it, probes from the pivot towards where the camera wants to sit, and pulls the boom in when a wall is in the way. You never write the collision.Change the numbers. Everything is a rule in
src/game.ts, and the declarativeplayerblock has to agree with it, because the engine re-states the rig after every user system:tsplayer: { prefab: HeroPrefab, spawn: HERO_SPAWN, camera: 'thirdPerson', distance: 3, // a tighter, more claustrophobic boom height: 0.25, // above the body centre: chest height on the 1.73 m rig sensitivity: 0.0018, }, rules: { cameraDistance: 3, cameraHeight: 0.25, cameraPitch: -0.3, // resting: a little above, looking down cameraMinPitch: -1.0, // how far the camera may rise cameraMaxPitch: 0.45, // how far it may drop and look up },Keep
distance/heightandcameraDistance/cameraHeightthe same number. They are the same boom stated twice, andtests/game.test.tsasserts the result.Tune body turning. The shared SDK controller follows these rules in
src/game.ts:tsidleTurnThreshold: Math.PI / 2, idleTurnRate: 4.5, moveTurnRate: 10, acceleration: 10, deceleration: 14,Small stationary orbits leave the body facing alone. Once the camera passes the threshold, the body completes its turn smoothly. While moving, it faces its camera-relative travel direction. The
aosrig_v0model faces +Z, so its initial visual yaw is pi when the camera starts on +Z.Keep the shared controller.
src/systems/locomotion.tsconstructscreateThirdPersonController()from the SDK. It clamps the camera look accumulator in place and reuses a follow-options object every frame. Keepcontroller.reset(ctx)in game init so restart restores the camera, facing, acceleration and jump state together.
Verify
sh
npm test -w templates/third-person
npm run dev -w templates/third-personWalk the hero into a corner and hold the mouse so the camera swings into the wall: the boom shortens instead of the wall filling the screen. The headless test rides a spring arm behind the hero asserts armLength and offset.y straight out of output.camera, so a mismatched pair fails before you look at it.
See also
- Build your first adventure — the template this tunes
- Add a locomotion state — the other half of the same system
- Read player input — where the mouse delta comes from
- The engine loop — why the camera is interpolated, not snapped