Documentation
    Preparing search index...

    Class CharacterController

    §12's character controller: parameter-driven locomotion for a node, under §42's "kinematic" authority, advanced by KinematicSystem.

    The controller owns three things and nothing else:

    state written by read as
    heading CharacterController.turn / CharacterController.yaw the node's rotation (yaw about +Y, no pitch, no roll)
    planar intent CharacterController.setMoveIntent a velocity of moveSpeed · intent, in the heading's frame
    vertical motion gravity and CharacterController.jump the node's y, against the ground plane

    setMoveIntent(forward, right) takes the two numbers a stick or a WASD pair produces, each nominally in [−1, 1], and clamps their magnitude to 1 — so walking diagonally is not √2 times faster than walking forwards, the oldest bug in the genre. The mapping from a device to those two numbers is application policy: like every rig in this package the controller reads no @fourjs/input (§3.1 gives @fourjs/motion core, math and scene only), which is also what makes it replayable from a seeded stream (§33/§34).

    Everything is written in the node's parent frame: yaw turns about the parent's +Y, groundHeight is a height in the parent's coordinates, and the write is transform.position/transform.rotation directly. For the usual character — a child of the scene root — that is the world frame. A character parented under a moving platform therefore rides it for free, and a character under a rotated parent walks in its parent's idea of north, which is the meaning that composes.

    A grounded character with no intent and no turn since its last write is idle: CharacterController.active is false, the system skips it, Transform.version does not move, and — following KinematicController's rule — a node that writes nothing raises no §42 conflict. A step whose arithmetic would produce a non-finite pose (an already-NaN position, a NaN fed in from elsewhere) writes nothing and bumps CharacterController.skippedSteps: §85's refusals govern authoring, and a value that only goes bad mid-step is a transient the simulation must survive, exactly as LookAtConstraint's degenerate aim is.

    Implements

    Index
    groundHeight: number

    Height of the ground plane, in the parent frame's metres (§7a).

    host: ComponentHost | null

    The node this component is attached to, or null. Written by the ComponentRegistry alone (§6a); never assign it.

    skippedSteps: number

    Steps on which the controller declined to write — see the class note. Not bumped by an idle step and not by a §42 refusal, which the system reports through warnAuthorityConflict instead.

    typeName: "character-controller"

    Component key (plan D2) and §79 serialization name.

    • get active(): boolean

      Whether the next step would write anything — the gate KinematicSystem applies before the §42 authority check, so an idle character neither bumps Transform.version nor reports a conflict on a node it does not own.

      true when the character is airborne, has a non-zero move intent, or has turned since its last successful write.

      Returns boolean

    • get gravity(): number

      Vertical acceleration in m/s², applied on steps the character is airborne.

      Returns number

      RangeError if the value is not finite (§85).

    • set gravity(value: number): void

      Parameters

      • value: number

      Returns void

    • get grounded(): boolean

      Whether the character is standing on the ground plane.

      false until the first step that lands it — a controller placed above groundHeight is falling, which is what a character controller is for. A controller with gravity: 0 placed above the plane hovers and stays ungrounded: it is falling at zero speed, and saying otherwise would make CharacterController.jump succeed in mid-air.

      Returns boolean

    • get intentForward(): number

      Forward component of the current move intent, in [−1, 1].

      Returns number

    • get intentRight(): number

      Rightward component of the current move intent, in [−1, 1].

      Returns number

    • get jumpSpeed(): number

      Upward speed CharacterController.jump imparts, in m/s; 0 means a character that cannot jump.

      Returns number

      RangeError if the value is not a finite number >= 0 (§85).

    • set jumpSpeed(value: number): void

      Parameters

      • value: number

      Returns void

    • get maxFallSpeed(): number

      Terminal downward speed in m/s, or Infinity for none. Read-only.

      Returns number

    • get moveSpeed(): number

      Speed a full move intent produces, in m/s (§7a).

      Returns number

      RangeError if the value is not a finite number >= 0 (§85).

    • set moveSpeed(value: number): void

      Parameters

      • value: number

      Returns void

    • get verticalVelocity(): number

      Vertical velocity in m/s, positive up; 0 while grounded.

      Returns number

    • get yaw(): number

      The character's heading: rotation about the parent frame's +Y, in radians (§7a), measured from +Z towards +X — OrbitRig.yaw's convention, so a rig and a character agree on what an angle means.

      Unbounded: a character may be spun any number of turns, and wrapping would make CharacterController.turn discontinuous at the seam.

      Returns number

      RangeError if the value is not finite (§85).

    • set yaw(value: number): void

      Parameters

      • value: number

      Returns void

    • Puts the character on the ground plane immediately, clearing its vertical velocity — what a teleport or a respawn needs, so the character does not resume the fall it was in when it was moved.

      Returns void

    • Launches the character upwards at CharacterController.jumpSpeed.

      Returns boolean

      whether the jump was taken. false — with nothing changed — for a character that is airborne or whose jumpSpeed is 0. A boolean rather than a throw or a warning: "can I jump right now?" is a question the application asks sixty times a second, and the answer is data.

    • Sets the planar move intent: forward along the heading, right across it, each nominally in [−1, 1]. A magnitude above 1 is scaled back to the unit disc, so diagonal movement is not faster than axial movement.

      The intent persists until it is set again — it is a state, not an impulse — so a controller stops when the application says setMoveIntent(0, 0) (or CharacterController.stop), not when it stops calling.

      Parameters

      • forward: number
      • right: number

      Returns void

      RangeError if either component is not finite (§85).

    • Advances the character by deltaSeconds and writes the result into transform.

      Called by KinematicSystem once per fixed step. Calling it directly is supported (a controller driven without a system, or a unit test) but performs no §42 authority check — that belongs to the system, which knows the node; this method only knows a transform.

      Both writes happen or neither does: the whole pose is computed into locals first, and a non-finite result leaves the transform untouched and bumps CharacterController.skippedSteps.

      Parameters

      Returns boolean

      whether the pose was written.

    • Clears the move intent. Vertical motion and heading are untouched.

      Returns void

    • Adds delta radians to CharacterController.yaw — the input surface an application maps a pointer's horizontal motion onto.

      Goes through the property setter, so a non-finite delta is refused rather than stored.

      Parameters

      • delta: number

      Returns void