Characters
Move CharacterVirtual objects, inspect ground state, crouch and jump, and use the model and clips a new character ships with.
Character movement
Characters use Jolt CharacterVirtual. Set horizontal velocity directly and preserve the current Y component so gravity and jumping continue naturally.
local velocity = gameObject.getCharacterVelocity()
gameObject.setCharacterVelocity(0, velocity.y, 5)
The engine does not move a character on its own: its velocity is whatever your script last set, plus gravity. The inspector's Walk Speed and Run Speed are for your script to read, with gameObject.getWalkSpeed() and gameObject.getRunSpeed() (both in metres per second, and nil on anything but a character). The engine never applies them itself, so a controller that reads them lets you tune each character's speed in the inspector without editing the script. The starter controller reads Run Speed. Jump Speed is the one movement field the engine uses; see below.
A character is not a rigid body. addForce, addImpulse, addTorque, and setLinearVelocity do nothing on it. To knock a character back, add to its velocity with setCharacterVelocity. To turn it, use setRotation or rotate.
A character does report what it touches. Once its script calls gameObject.registerForCollisions(), onCollisionEnter and onCollisionExit fire for the floor it lands on, the walls it walks into, and any body or other character it touches, and the other object's script gets the same events. A pickup a player walks into can therefore detect it from its own onCollisionEnter. See Physics, collisions, and triggers.
Ground state
isOnGround()getGroundNormal()andgetGroundVelocity()getGroundObjectId(),getGroundObject(), andgetGroundInfo()getSlopeAngle()andisSlopeTooSteep()getUp(), the character's up direction
isOnGround() is true only when the character stands on something it can stand on. On a slope steeper than the inspector's Max Slope Angle (45° by default) it is false even though the character is touching the ground, and isSlopeTooSteep() is true. getSlopeAngle() is in degrees and returns 0 when the character touches nothing, so ask isOnGround() rather than reading 0 as a flat floor. getGroundNormal() is nil in the air. getGroundInfo() returns all of it in one table: objectId, objectName, slopeAngle, isTooSteep, velocity, and normal.
Jumping and crouching
jump() uses the character’s configured jump speed. It only works while isOnGround() is true and the character is not already rising, so calling it every frame the button is held gives one jump, not a rocket. It adds Jump Speed (6 by default) to the character's upward velocity rather than replacing it. In water it swims upward instead — see below.
setCrouched(true) swaps to the crouching capsule set by Crouch Radius and Crouch Half Height (1 m tall by default) and returns whether the capsule could change safely; isCrouched() reports the current state. Standing back up fails under a ceiling too low for the standing capsule: setCrouched(false) returns false and the character stays crouched. Call it every frame with the button's state, as below, and the character stands as soon as there is room.
To give the player a crouch control, add an action button with the ID crouch and pass input.crouch straight to setCrouched(): it is true while the button is held, so releasing it stands the character back up. In a multiplayer room every player sees the character crouch. See Player input and touch controls.
In water
Inside a water object a character is buoyed up, slowed, and carried by the water's current, using the same Buoyancy, Linear drag, and Current velocity settings that act on rigid bodies. isInWater() is true while any of it is below the surface, and getWaterSubmersion() returns how much of its height is under, from 0 to 1. jump() swims upward in water — it raises the upward speed to half of Jump Speed — and works whether or not the character is touching the bottom. See Physics, collisions, and triggers for the water object's settings.
Where a character's origin sits
A character's position is at its feet, not its centre. Its bounds run from y = 0 up to its full height, so a 2m character standing on a floor at y = 0 has position.y = 0, and its eye is at roughly position.y + 1.65.
This differs from primitives — a capsule with the same collider dimensions is centred, so its bounds straddle its origin. See Coordinates and transforms for the full per-type table. It is the single most common source of a first-person camera that renders from hip height.
What a new character looks like
A character needs no mesh asset of its own. One with no meshAssetId renders a bundled model: a humanoid roughly 1.8m tall, facing local +Z, with its origin at its feet — the same conventions above. Choosing a mesh asset in the inspector replaces it; the empty option puts it back.
The model is not fitted to the collider. A new character's capsule is 1.5m tall (Radius 0.25, Half Height 0.5), so the model's head rises about 0.3m above it. Raycasts, sphere queries, and head-height clearance all use the capsule, not the model. To match the model, set Half Height to 0.65, which makes the capsule 1.8m.
That model carries twenty-four animation clips, covering locomotion in all eight directions, turns on the spot, the air, crouching, a small combat set and an emote. gameObject.enableAutoAnimation({}) with no arguments drives the locomotion ones correctly, because every clip name it looks for is a name the bundled model already uses. Animation playback API lists them all and documents the state machine.
The character's Current Animation dropdown in the inspector lists whichever clips the loaded model actually has, which is the quickest way to see them without running the scene.
A character's clips play in place: the horizontal movement of the root bone is removed so the model stays on its capsule, while vertical movement is kept, so a death or crouch clip lowers the body to the floor. Move the character across the ground from its script. This also applies to clips added with Add Animation. See "Root motion" in Animation playback API, which also covers attaching a weapon or a hat to one of the model's bones.
A character created through an MCP client or the AI assistant gets the same New Character settings as one added from the editor's menu (the 1.5m capsule, Walk Speed 4, Run Speed 8, Jump Speed 6, mass 70, and the rest) for every field the request leaves out.
Cameras and input
Following a character with a camera — controlMode, targetCharacterId, eye height, the follow boom, pitch limits, and hiding the character's own body from a first-person view — is covered in Cameras and follow modes.
Turning the body toward the view. In a multiplayer room a control script can read the player's aim as input.aimYaw and input.aimPitch, in world-space radians, and turn the character to face it with gameObject.setRotation(0, math.sin(yaw / 2), 0, math.cos(yaw / 2)). Pair that with a First Person (Absolute Aim) camera (first_person_absolute). A plain first_person camera adds look to the body's rotation, so turning the body from look turns the view twice.
What actually produces movement and look on each device, how the touch sticks behave, and how authored action buttons reach Lua are covered in Player input and touch controls.