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
| What | How |
|---|---|
| Parameters | int, 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. |
| Triggers | Call NetworkAnimator.SetTrigger. Calling Animator.SetTrigger affects only the local Animator. |
| Layer state | Per layer: the current state's full path hash, its normalized time, and the layer weight. |
| Transitions | Per 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
| Field | Default | Notes |
|---|---|---|
Animator | the one on this GameObject | The Animator to replicate. |
SyncParameters | true | Send parameter changes. |
SyncLayerStates | true | Send state hash and normalized time per layer. |
SyncLayerWeights | true | Send layer weights (applied for layers above 0). |
SyncTransitions | true | Send in-progress transitions. |
FloatThreshold | 0.001 | A float parameter must move by at least this much to be sent. |
NormalizedTimeResyncThreshold | 0.25 | A keyframe changes the playback position only when its normalized-time difference exceeds this value. |
DisableRootMotionOnRemote | true | Non-authoritative copies do not apply root motion. |
Authority | Server | AuthorityMode.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:
| Caller | Behaviour |
|---|---|
| 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 authority | Sends 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 else | Logs 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
CrossFadeInFixedTimeon 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.SetTriggerdirectly. - Parameters driven by animation curves (
Animator.IsParameterControlledByCurve). - Animator speed, IK,
Animator.Playcalls made outside a synchronised layer state (the resulting state change is picked up on the next tick ifSyncLayerStatesis on). - Blend tree internals: only the parameters feeding them.
Compare with Netcode for GameObjects
| NGO | Nebula |
|---|---|
NetworkAnimator | NetworkAnimator with Authority = Server (default). |
OwnerNetworkAnimator | NetworkAnimator with Authority = Owner. |
NetworkAnimator.SetTrigger(string / int) | Same. |
NetworkAnimator.ResetTrigger(string / int) | Same; local only. |
Animator field | Same. |
| Float parameter threshold (hard-coded in NGO) | FloatThreshold. |
| Curve-controlled parameters skipped | Same. |
| Root motion left to the user | DisableRootMotionOnRemote (on by default). |