Optionaloptions: CharacterControllerOptionsHeight of the ground plane, in the parent frame's metres (§7a).
The node this component is attached to, or null. Written by the
ComponentRegistry alone (§6a); never assign it.
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.
Static ReadonlytypeComponent key (plan D2) and §79 serialization name.
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.
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.
Forward component of the current move intent, in [−1, 1].
Rightward component of the current move intent, in [−1, 1].
Upward speed CharacterController.jump imparts, in m/s; 0 means a
character that cannot jump.
Terminal downward speed in m/s, or Infinity for none. Read-only.
Vertical velocity in m/s, positive up; 0 while grounded.
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.
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.
Launches the character upwards at CharacterController.jumpSpeed.
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.
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.
whether the pose was written.
Clears the move intent. Vertical motion and heading are untouched.
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.
§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:
+Y, no pitch, no roll)moveSpeed · intent, in the heading's framey, against the ground planeIntent is dimensionless and clamped to the unit disc
setMoveIntent(forward, right)takes the two numbers a stick or a WASD pair produces, each nominally in[−1, 1], and clamps their magnitude to1— so walking diagonally is not√2times 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/motioncore,mathandsceneonly), which is also what makes it replayable from a seeded stream (§33/§34).Frames
Everything is written in the node's parent frame:
yawturns about the parent's+Y,groundHeightis a height in the parent's coordinates, and the write istransform.position/transform.rotationdirectly. 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.Writes are gated, never accrued
A grounded character with no intent and no turn since its last write is idle: CharacterController.active is
false, the system skips it,Transform.versiondoes not move, and — followingKinematicController's rule — a node that writes nothing raises no §42 conflict. A step whose arithmetic would produce a non-finite pose (an already-NaNposition, aNaNfed 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 asLookAtConstraint's degenerate aim is.