CHARACTER_CONTROLLER_SERIALIZER's split, applied to a class whose ground is
geometry rather than a plane — the asymmetry is the same one and it is worth
restating: vertical motion is scene state and the move intent is not. A
character saved mid-jump is at a height the document already carries, moving
at a speed nothing else can reconstruct; its move intent is this frame's
input, and §79 documents do not carry the player's thumb. So
verticalVelocity and grounded round-trip and the intent restores at
(0, 0).
What this class adds to that split, and why each lands where it does:
state
round-trips?
the capsule (radius, halfHeight) and every resolution parameter
yes — they are readonly, so the reader must construct with them (ORBIT_RIG_SERIALIZER's rule)
yaw, moveSpeed, gravity, jumpSpeed
yes
maxFallSpeed
yes, by omission when infinite — Infinity is not JSON and absence already means "unbounded" on both sides
verticalVelocity, grounded
yes — scene state
the move intent
no — this frame's input
world
no — a live object reference, re-bound by the application exactly as a reloaded RigidBody is registered (registerPhysicsSerializers's "reloading a scene is not restoring a simulation")
groundBody
no — it is derived: the next step's probe recomputes it, and a saved reference to another node's component is precisely what the rig serializers drop
Reading follows this module's own rule rather than @fourjs/motion's total
one for the two fields that have no defensible default: radius and
halfHeight are required constructor parameters (§24 refuses a capsule
without them), so a document missing them throws naming the field rather than
restoring a character of invented size.
The §79 serializer for SweptCharacterController (§12, §30,
PH-11b, 2026-08-21).CHARACTER_CONTROLLER_SERIALIZER's split, applied to a class whose ground is geometry rather than a plane — the asymmetry is the same one and it is worth restating: vertical motion is scene state and the move intent is not. A character saved mid-jump is at a height the document already carries, moving at a speed nothing else can reconstruct; its move intent is this frame's input, and §79 documents do not carry the player's thumb. SoverticalVelocityandgroundedround-trip and the intent restores at(0, 0).What this class adds to that split, and why each lands where it does:
radius,halfHeight) and every resolution parameterreadonly, so the reader must construct with them (ORBIT_RIG_SERIALIZER's rule)yaw,moveSpeed,gravity,jumpSpeedmaxFallSpeedInfinityis not JSON and absence already means "unbounded" on both sidesverticalVelocity,groundedworldRigidBodyis registered (registerPhysicsSerializers's "reloading a scene is not restoring a simulation")groundBodyskippedSteps,slideCount,stepUpCount,budgetExhaustedStepsReading follows this module's own rule rather than
@fourjs/motion's total one for the two fields that have no defensible default:radiusandhalfHeightare required constructor parameters (§24 refuses a capsule without them), so a document missing them throws naming the field rather than restoring a character of invented size.