avatar
- body — a
.bundle: a skinned.mesh+ bone entities (a character imported from a GLB/FBX). The look. - controller — the movement system. Default: the standard humanoid kit
(
CharacterController+MovementState+Humanoid). - animation — the system that animates the body. Default: the shared
Locomotionblend space (reads the controller's velocity, plays retargeted clips). OrClipPlayer. Or your own component.
Movement and animation are not tied together — attaching the controller never
pulls in Locomotion, and choosing your own animation keeps the standard
controller. Authoring your own locomotion is a one-field swap (animation = ...).
Because the standard clip set retargets onto any humanoid rig, the default
locomotion drives any humanoid body with no per-body setup and no editing of any
built-in content.
Make an imported character playable
-- After importing a rigged humanoid GLB, the importer leaves a `.bundle`
-- (skinned mesh + bones). Survey for it, then wrap it in an avatar:
local body = asset.resolve("my_character", "bundle")
local hero = asset.create("avatar", "my_hero", { body = body })
To make hero the body every connecting player spawns with, point the scene's
PlayerPrototype.body at a body entity whose Asset source is hero (authored
in the scene tree — see man scene). To drive the local player with it right
now, instantiate it and bind the entity:
local root = hero:instantiate() -- spawns the body + movement kit
layers.active.players.localPlayer.avatar = root -- bind the live body
Only the avatar bound as the local player's body answers the player's
controls. Every other avatar you instantiate (an NPC, a mannequin, a preview)
stands where it is until something drives it: with the standard controller,
write its CharacterController input bus (inputX / inputZ / inputJump)
from a component of your own, the way an AI brain does (man CharacterController).
local npc = hero:instantiate() -- stands still
npc.component.get("CharacterController").inputZ = -2 -- walks while this is written each frame
An avatar is not humanoid-only. The body can be anything — a humanoid, a
vehicle, an unrigged plane with a custom controller and shader. The defaults
adapt to the body: a humanoid body defaults to the shared humanoid Locomotion
(the synty clip preset); a non-humanoid / unrigged body gets no
auto-animation (you supply your own controller and animation). This is the
non-humanoid detection — an arbitrary animated model is never auto-driven by
humanoid locomotion — and it never blocks creating the avatar.
-- Non-humanoid avatar (e.g. a custom-controlled body), fully generic:
asset.create("avatar", "ship", { body = shipBundle, controller = { system = "ShipController" }, animation = false })
Options
asset.create("avatar", name, opts):
| opt | meaning |
|---|---|
body (required) | the body .bundle ref |
controller | movement: true/nil = standard kit, false = none, or a component name / { system, data } |
animation | a component name or { system, ... } — author your own locomotion here; defaults to Locomotion |
locomotion | clip preset for the default Locomotion (default "synty") |
clip | a single .animation ref — selects a one-clip ClipPlayer |
looping / speed / sourceRig | ClipPlayer tuning (with clip) |
Author your own locomotion (keeps the standard movement):
asset.create("avatar", "my_hero", { body = body, animation = { system = "MyLocomotion" } })
avatar.json:
{
"body": { "__ref": "<bundle guid>" },
"controller": true,
"animation": { "system": "Locomotion", "preset": "synty" }
}
The body's facing is read from its rig (the importer records a forward for each
body — {0,0,1} for a +Z Synty/FBX body, {0,0,-1} for a -Z glTF body), so
Locomotion turns the body to its movement direction regardless of how its source
authored it. No per-avatar facing knob.
Use
local root = avatarRef:instantiate() -- spawn body + movement kit
avatarRef:setCharacter(root, asset.resolve("knight", "bundle")) -- swap the body, keep the behaviour
:instantiate() spawns the body and attaches the movement system bound to it.
:setCharacter(root, body) despawns the current body and spawns a new one in its
place; the Locomotion kit on the avatar root re-resolves the new body's rig, so
the look swaps while the behaviour is kept. Items seated with
Humanoid:attach are kept: each moves to the new body's socket for the same
point, with the transform it was seated with. An item whose point the new body
lacks stays on the avatar root and is seated by the next swap to a body that
has it. The new body takes the old body's id and is composed with the options
the avatar was instantiated with, and the avatar's Asset id map names it.
In the Inspector
Selecting an avatar opens its Avatar section, read from avatar.json: its label when it has one, how it moves (controller: the standard mover, none, or the system it names), what animates it, and whether it is humanoid. It links the body the avatar wears, or says the avatar spawns no visible body, and offers to open avatar.json in the Code panel.