Nebula

NetworkAnimator

Replicate Animator parameters, triggers, layer states, transitions, and weights.

Add NetworkAnimator to keep a Unity Animator in sync across workers and clients. Nebula sends parameter changes, triggers, and per-layer state while each copy continues running its local Animator.

If you are migrating from Unity Netcode for GameObjects (NGO), use the comparison table at the end of this page.

The component uses the sync channel and supports worker or owner authority.

Add the component to the GameObject with the Animator, or assign an Animator to its Animator field. Nebula logs an error during spawn when it cannot find one.

What is replicated

WhatHow
Parametersint, float and bool parameters, by index in Animator.parameters. Floats are sent when they move by at least FloatThreshold. Triggers travel separately. Parameters controlled by an animation curve are skipped. The first 255 parameters are synchronized; more logs a warning.
TriggersCall NetworkAnimator.SetTrigger. Calling Animator.SetTrigger affects only the local Animator.
Layer statePer layer: the current state's full path hash, its normalized time, and the layer weight.
TransitionsPer layer, when in one: the next state's hash, the transition duration and its normalized time, so a state entered on the authority is entered everywhere with the same blend.

Keyframes (sent on a keyframe tick, on the first send after gaining authority, and in the spawn snapshot) carry all of the above, so a late joiner or a fresh ghost starts in the right state at the right playhead. See the keyframe section below for how playhead drift is handled.

Options

FieldDefaultNotes
Animatorthe one on this GameObjectThe Animator to replicate.
SyncParameterstrueSend parameter changes.
SyncLayerStatestrueSend state hash and normalized time per layer.
SyncLayerWeightstrueSend layer weights (applied for layers above 0).
SyncTransitionstrueSend in-progress transitions.
FloatThreshold0.001A float parameter must move by at least this much to be sent.
NormalizedTimeResyncThreshold0.25A keyframe changes the playback position only when its normalized-time difference exceeds this value.
DisableRootMotionOnRemotetrueNon-authoritative copies do not apply root motion.
AuthorityServerAuthorityMode.Server or AuthorityMode.Owner, from NetworkSyncBehaviour.

Turning SyncLayerStates, SyncLayerWeights and SyncTransitions all off removes the layer section from the chunk entirely; turning SyncParameters off removes the parameter section.

Triggers and authority

public void SetTrigger(string name);
public void SetTrigger(int hash);
public void ResetTrigger(string name);
public void ResetTrigger(int hash);

What SetTrigger does depends on who calls it:

CallerBehaviour
The sync authority (the worker under server authority, the owning client under owner authority)Fires the trigger on the local Animator, queues the hash, marks the behaviour dirty. The trigger goes out with the next chunk and every non-authoritative copy calls Animator.SetTrigger with the same hash.
The owning client under server authoritySends a ServerRpc asking the worker to fire it. The worker fires and replicates the trigger, so the client sees it after one network round trip.
Anyone elseLogs a warning; nothing happens.

The worker-side handler ignores the request if it is not authoritative for the entity or if the component is in owner mode (the owner should have fired it locally).

ResetTrigger is local only and is never replicated: a trigger that has already been sent has already been consumed on the remote copies. It also removes the hash from the pending list if it has not gone out yet.

The default. The worker drives the Animator, usually from NetworkTick or Simulate (set parameters from the simulated state; call SetTrigger for one-shot actions). Every client, the owner included, follows. Parameter changes made on a client are overwritten by the next chunk that carries them.

public sealed class Pawn : NetworkBehaviour
{
    [SerializeField] private NetworkAnimator _anim;

    public override void NetworkTick(uint tick, float deltaTime)
    {
        _anim.Animator.SetFloat("Speed", Identity.Motion.Velocity.magnitude);
    }

    [ServerRpc]
    private void RpcAttack()
    {
        if (!HasAuthority) return;
        _anim.SetTrigger("Attack");
    }
}

Change detection

Once per tick, AuthorityTick compares each synchronized parameter and layer with its last recorded value. A partial update contains only changes and pending triggers. A keyframe contains every current value. Nebula clears the pending changes after sending them.

On a worker relaying an owner, the received chunk is applied and its parameter, trigger and layer entries are re-queued as dirty so they go out to everyone else.

Keyframes and late joiners

A keyframe carries every synchronised parameter and every layer. When one arrives at a non-authoritative copy:

  • A parameter is set to the keyframe's value.
  • A layer whose current state differs from the keyframe's (and that is not already transitioning to it) plays the keyframe's state from its normalized time.
  • A layer already in the same state compares playback positions. If the difference exceeds NormalizedTimeResyncThreshold, the copy moves to the keyframe's normalized time. Smaller differences are left alone to avoid visible jumps.
  • A transition in progress on the authority is started with CrossFadeInFixedTime on the copy if it is not already heading there.

Deltas apply the same rules without the playhead comparison (they only carry layers that changed).

The gateway caches the newest keyframe per behaviour index for every entity it knows and puts them in the spawn message it sends to a late joiner, so the joiner's Animator is in the right state before its first frame. A worker receiving a ghost or an authority transfer starts from the same snapshot.

Root motion on remotes

Enable DisableRootMotionOnRemote when remote copies receive their position from Nebula. The option disables applyRootMotion on those copies so animation does not also move them. Nebula restores the original value when a copy gains authority.

Disable this option when you want Nebula to leave applyRootMotion unchanged.

Delivery

NetworkAnimator is always reliable-ordered (SyncDelivery is Delivery.ReliableOrdered; there is no unreliable option). A lost trigger could never be recovered from a later keyframe because a trigger is an event, not a value. Its keyframes therefore go out only when the component is dirty on a keyframe tick. Unreliable child NetworkTransform components send periodic keyframes; root transforms use a separate stream with reliable recovery after movement.

Because the worker sends one sync message per delivery class per entity per tick, an animator and an unreliable transform on the same entity travel in separate packets; a reliable transform shares the animator's packet.

What is not replicated

  • Trigger parameters set through Animator.SetTrigger directly.
  • Parameters driven by animation curves (Animator.IsParameterControlledByCurve).
  • Animator speed, IK, Animator.Play calls made outside a synchronised layer state (the resulting state change is picked up on the next tick if SyncLayerStates is on).
  • Blend tree internals: only the parameters feeding them.

Compare with Netcode for GameObjects

NGONebula
NetworkAnimatorNetworkAnimator with Authority = Server (default).
OwnerNetworkAnimatorNetworkAnimator with Authority = Owner.
NetworkAnimator.SetTrigger(string / int)Same.
NetworkAnimator.ResetTrigger(string / int)Same; local only.
Animator fieldSame.
Float parameter threshold (hard-coded in NGO)FloatThreshold.
Curve-controlled parameters skippedSame.
Root motion left to the userDisableRootMotionOnRemote (on by default).

On this page