Gameplay

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.

Eye entity[package]Body entity[package]Body Transform (yaw only)KinematicPosition capsuleEye Transform (full 6DOF)CameraInput or AI intentFirstPersonPlayerControllerSystemFirstPersonSensorsSystem

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.stategrounded, speed, verticalSpeed, airborneTime, exertion, breathPhase, stridePhase, and more, for HUDs and AI.
  • controller.signalsonFootStep, 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.