Skip to content

Animator Parameters

TL;DR: AvatarDefinitions.CoreParameters is the source of truth for the 22 names the runtime treats as “core” (read-only via SetParameter). Twelve of those are network-replicated through AvatarCoreSyncData; the other ten are computed locally on each receiver. Two extra [Obsolete] properties (Swimming, AFK) are writable from scripts but not synced.

Every name below is recognised by the runtime with the matching type. Declare a parameter on your avatar’s Animator with the exact name and type and the engine will drive it for you — no extra component required.

NameTypeNet-replicated?Read-only via SetParameter?Notes
MovementXfloatyes (core sync)yesStrafe input. BetterBetterCharacterController divides by worldSprintMultiplier, so a sprinting avatar produces values up to 1.0.
MovementYfloatyes (core sync)yesForward input, same normalisation.
Groundedboolyes (core sync)yesTrue while IsGrounded() OR IsSwimming() OR IsSitting() OR IsImmobilized.
Crouchingboolyes (core sync)yesDriven by BetterBetterCharacterController.crouching.
Proneboolyes (core sync)yesDriven by BetterBetterCharacterController.prone.
Flyingboolyes (core sync)yesTrue if the controller is flying or in zero-gravity mode.
Sittingboolyes (core sync)yesTrue while attached to a CVRSeat.
GestureLeftfloatyes (core sync)yesHand gesture float (see Hand Gestures).
GestureRightfloatyes (core sync)yesHand gesture float.
Toggleintyes (core sync as float)yesDriven by CVRInputManager.toggleState, integer 0..7. Stored as float in AvatarCoreSyncData.
Emoteintyes (core sync as float)yes0 = none, 1..8 = Emote1..Emote8. Auto-resets to 0 after 0.1 s (PlayerSetup.ResetEmoteAfterTime).
CancelEmoteboolyes (core sync)yesSet true on movement, hide, death, or visibility transitions.
GestureLeftIdxintlocal-computedyesAuto-set to Mathf.RoundToInt(GestureLeft) whenever GestureLeft is written.
GestureRightIdxintlocal-computedyesSame as left.
DistanceTofloatlocal-computedyesWorld distance from the local player to this avatar. 0 on the local avatar.
IsLocalboollocal-computedyestrue on the local PlayerSetup, false on every PuppetMaster.
IsFriendboollocal-computedyesWhether the viewer is friended with the avatar’s owner (per-receiver lookup).
VisemeIdxintlocal-computedyesIndex of the dominant viseme 0..14. Read from each side’s LipSyncManager.
VisemeLoudnessfloatlocal-computedyesSmoothed amplitude. For viseme idx 0 (silence) this is 1 - peak — invert if you want a “speaking” curve.
VelocityXfloatlocal-computedyescharacterMovement.velocity.x (local) or netIkController.GetRootVelocity().x (remote).
VelocityYfloatlocal-computedyesVertical component, same source.
VelocityZfloatlocal-computedyesForward component, same source.
Swimmingboolno (local-only)no[Obsolete("Swimming is not yet a core parameter!")]. Engine drives it via BetterBetterCharacterController.IsSwimmingAnimator() but it is not in CoreParameters and not synced.
AFKboolno (local-only)no[Obsolete("AFK is not yet a core parameter!")]. Set when _vrAfkDetectionEnabled && isUsingVr && !HeadsetOnHead, or when CVRInputManager.AFKToggle fires. Not synced.

AvatarDefinitions.CoreParameters has exactly 22 entries — the table rows above except Swimming and AFK. AvatarDefinitions.IsCoreParameter(name) is what marks a parameter read-only from the script API.

Three buckets, three different paths over the wire:

Sent in the avatar movement packet:

public struct AvatarCoreSyncData
{
public float MovementX, MovementY, GestureLeft, GestureRight;
public float Emote, Toggle;
public bool Grounded, Sitting, Crouching, Flying, Prone, CancelEmote;
}

Emote and Toggle are stored here as float for compactness even though the public API exposes them as int.

Read-only core parameters that are not in AvatarCoreSyncData. Each receiver computes them independently — PuppetMaster.AnimateCoreParameters and PlayerSetup.AnimateCoreParameters derive them from local state and the incoming movement packet. For example, DistanceTo uses Vector3.Distance(transform.position, PlayerSetup.Instance.GetPlayerPosition()); VelocityX/Y/Z reads netIkController.GetRootVelocity() on remotes; VisemeIdx/VisemeLoudness come from the receiver’s local LipSyncManager.

GestureLeftIdx and GestureRightIdx are produced as a side effect of writing GestureLeft/GestureRight — see the setter:

public float GestureRight
{
set
{
_coreSyncData.GestureRight = value;
if (Parameters.TryGetValue("GestureRight", out var p)) SetParameter_Internal(p, value);
if (Parameters.TryGetValue("GestureRightIdx", out var pIdx))
SetParameter_Internal(pIdx, GestureRightIdx = Mathf.RoundToInt(value));
}
}

Every other Animator parameter that is:

  • not local (does not start with #, is not a Trigger)
  • not read-only (not in CoreParameters, not curve-controlled)
  • still under the 3200-bit budget when registered

…is added to _aasOutboundCacheFloat/Int/Bool and packed into AvatarAdditionalSyncData per tick. See AAS Bookkeeping for the budget mechanics.

The [Obsolete] Swimming and AFK parameters fall outside all three — they bypass IsCoreParameter, are written via SetParameter from the engine, and never enter the sync buffers. They behave as “engine-driven local-only” parameters.

Any animator parameter whose name starts with # is treated as local:

public static bool IsLocalParameter(string name) => name.StartsWith("#");

AvatarParam.IsSynced returns false for them, so they are skipped by the AAS sync layer and consume 0 bits of the 3200-bit budget.

AnimatorControllerParameterType.Trigger is always local — AvatarParam’s constructor sets isLocal |= type == Trigger, regardless of name. Triggers fire only on the local client.

Use #-prefixed names for menu state, debug toggles, or any parameter that should never leave the local client.

TypeBits
Float32
Int32
Bool1 (packed 8 per byte)
Trigger0 (always local)
using ABI_RC.Core.Player;
using ABI_RC.Core.Util.AnimatorManager;
var mgr = PlayerSetup.Instance.AnimatorManager; // local AvatarAnimatorManager
// Core parameters: write through the typed properties (bypasses the read-only guard).
mgr.MovementX = 1f;
mgr.GestureLeft = 2f; // also auto-updates GestureLeftIdx
mgr.Emote = 3; // does NOT auto-reset; only PlayerSetup.TriggerEmote() does
mgr.CancelEmote = true;
// Custom / AAS parameter (must be declared on the avatar's controller):
mgr.SetParameter("MyHairToggle", true);
mgr.SetParameter("HairColor-r", 0.42f);
// Read:
mgr.GetParameter("HairColor-r", out float r);

For a remote avatar, substitute puppetMaster.AnimatorManager. Calling SetParameter(name, …) for a core parameter is a no-op — IsCoreParameter(name) is checked inside CVRAnimatorManager.SetParameter and the typed properties on AvatarAnimatorManager are the only valid write path.

ABI_RC.API.Player.CoreParameters (the read-only view exposed to scripts) wraps a subset:

MovementX, MovementY, Grounded, Crouching, Prone, Flying, Sitting,
GestureLeft, GestureLeftIdx, GestureRight, GestureRightIdx,
Toggle, Emote, CancelEmote, DistanceTo, VisemeIdx, VisemeLoudness,
Swimming, AFK

IsLocal, IsFriend, and VelocityX/Y/Z are not surfaced through Player.Core — read them off AvatarAnimatorManager directly if you need them from a mod.