First-person controller
A kinematic-capsule first-person controller — mouse-look and movement, the procedural camera and locomotion feel on top, an opt-in movement-ability set, and read-only state for HUDs and AI.
FirstPersonPlayerController is a single component (plus its system) that turns an
entity into a first-person character. The visible part is what you’d expect — a
capsule that walks, sprints, crouches, and jumps under mouse-look. Underneath, it
also drives a procedural camera, exposes a clean control surface, and carries an
optional movement-ability vocabulary. Out of the box you get the capsule and the
camera feel; the abilities and the mastery layer are opt-in.
Intent, not input
The controller never touches the keyboard or mouse. It exposes an intent surface that input bindings or an AI script write each frame:
const intent = controller.intent;
intent.move.set(x, y); // forward+ / right+, in body-local space
intent.look._add(dYaw, dPitch);
intent.jump = held; // hold-state — the system edge-detects
intent.sprint = held;
intent.crouch = held;
move, jump, sprint, and crouch are latched hold-states; look accumulates
deltas and is consumed once per fixed step. Edge detection is internal — holding
the jump button produces exactly one jump, and its release drives variable jump
height. The same fields serve mouse-and-keyboard, gamepad, or an AI agent with no
changes to the controller.
Body and eye
The controller drives two transforms. The entity’s own Transform is the
body: ground-projected, yaw only, physics-clean — what the collider integrates.
The system creates a separate eye entity (its own Transform + Camera) on
link, and that’s where the feel lives:
- gait-driven head bob — an under-damped spring kicked at each footfall, not a sine
- breathing whose rate and amplitude scale with an exertion signal that rises while you sprint and jump and decays when you stop
- jump anticipation (a small squash before take-off) and landing recovery (a spring that dips and rings on impact)
- lean roll into lateral acceleration, and an FOV kick at sprint speed
- forgiveness: coyote time, jump buffer, and a faster fall gravity for variable jump height
Splitting the two keeps body kinematics clean for collision and AI line-of-sight while the camera is free to dip and oscillate. Every effect above is a config knob, tunable per entity — a heavy soldier and a light scout are data, not code — and bob, lean, and the FOV kick have explicit off switches for motion sensitivity.
Movement abilities
Abilities are opt-in modules added to controller.abilities. The set is empty by
default; base locomotion is unchanged until you add them:
controller.abilities.add(new Slide());
controller.abilities.add(new Mantle());
controller.abilities.add(new WallRun());
controller.abilities.add(new WallJump());
controller.abilities.add(new LedgeGrab());
At most one is active at a time; they activate by priority and can preempt or queue. They share a sensor layer — a small set of raycast probes (walls to the left/right/front, an obstacle-ahead probe, a ledge probe) computed once per step and reused — so registering the sensors system is all the abilities need to read the world. They engage on their own against the right geometry; there are no ability buttons beyond move, jump, sprint, and crouch.
Mastery (optional)
controller.mastery is a registry of timing evaluators, also empty by default.
Each returns a multiplier at a decision point — jump impulse, ground
acceleration, slide entry, wall-jump — and they compose multiplicatively. The
shipped evaluators are physically motivated (stride phase, foot side, breath
rhythm), small in magnitude, and feed a telemetry score a HUD can read. Add it for
skill-timed depth; leave it empty and nothing changes.
Reading state
The controller publishes read-only outputs every step:
controller.state—grounded,speed,verticalSpeed,airborneTime,exertion,breathPhase,stridePhase, and more, for HUDs and AI.controller.signals—onFootStep,onJumpStart,onLand,onBreathIn/onBreathOut, and more, for audio and FX.controller.pose— head/root channels and a locomotion phase, a stable contract a skeleton or networked rig can drive itself from.
Only config is serialized; everything else is rebuilt on link.
Using it
The player is a KinematicPosition body with a capsule collider — the controller
writes the Transform and a collision mover keeps it out of walls. Register
FirstPersonPlayerControllerSystem (and FirstPersonSensorsSystem if you use
abilities); the system creates the camera for you. Ground is resolved by the
built-in flat baseline, a downward physics raycast you wire in, or the collision
mover when a physics system is present.
The first-person example is a complete, runnable setup — the capsule, the systems, input bindings, a physics-backed ground resolver, and all five abilities across a parkour gym. Start there.