This document is about: FUSION 2
SWITCH TO

Sync Idle, Run & Attack Animations

This page accompanies the Photon Fusion 2 Tutorial – Sync Idle, Run & Attack Animations video and explains how to synchronize character animations in Photon Fusion Shared Mode.

Use this page when implementing networked animation states such as idle, walking, running, attacking, interacting, or performing temporary actions.

If you would like to dive deeper into Animations or the Network Mecanim Animator, you can visit the dedicated pages via both links.

Photon Fusion 2 Tutorial – Sync Idle, Run & Attack Animations

Overview

Fusion provides multiple approaches for synchronizing Unity Animator state.

The simplest option is the built-in NetworkMecanimAnimator. It can synchronize Animator parameters with minimal custom networking code.

A more explicit option is to synchronize only the required gameplay values through [Networked] properties and update the Unity Animator from Render().

This video demonstrates both approaches:

  • synchronizing idle and walking with NetworkMecanimAnimator;
  • synchronizing an attack trigger;
  • buffering rendered-frame input for Fusion ticks;
  • replacing NetworkMecanimAnimator with custom Networked properties;
  • updating visual animation state from Render();
  • comparing convenience, bandwidth, and control;
  • preventing repeated or spammed trigger updates;
  • identifying when tick-accurate animation systems are required.

Video Timeline

Time Section
00:00 Introduction
00:19 Project setup and verification
00:57 Idle and walking animations
02:59 NetworkMecanimAnimator overview
04:26 Scripting for NetworkMecanimAnimator
07:20 Attack animation and trigger synchronization
09:40 Two animation synchronization methods
10:54 Synchronizing animations with Networked properties
14:03 Testing
14:07 Fixing a potential spam issue
14:35 Tick-based animations technical sample
15:09 Outro

Project Setup

Before configuring animation synchronization, verify the Fusion project setup.

Open:

Tools > Fusion > Fusion Hub

Confirm that the project has a valid Fusion App ID.

When the sample starts:

  1. Click Start Client.
  2. Wait for the player prefab to spawn.
  3. Verify that movement and connection already work.
  4. Confirm that the character currently moves without playing animation.

The sample already includes:

  • Shared Mode connection logic;
  • player spawning;
  • a NetworkObject;
  • a networked character controller;
  • player movement.

The tutorial focuses only on animation setup and synchronization.

Character Prefab

Open the player prefab in the project’s animation folder.

The character already contains the core networking and movement components.

The relevant setup includes:

Component Purpose
NetworkObject Gives the player a Fusion network identity.
Network character controller Handles synchronized player movement.
Animator Plays Unity animation states.
Character model Contains the animated skeleton and renderer.

The next step is to create the Unity Animator Controller.

Preparing Animation Clips

The sample uses character and animation assets from Kenney.

The required clips are:

  • idle;
  • walk;
  • attack.

If the clips are embedded inside an FBX file, duplicate or extract them into separate project assets.

For each clip:

  1. Select the clip inside the FBX asset.
  2. Duplicate or extract it into the project.
  3. Rename it clearly.
  4. Configure looping where required.

Enable Loop Time for:

  • idle;
  • walk.

Do not normally enable looping for a one-shot attack animation.

Creating the Animator Controller

Drag the idle animation onto the character to create an Animator Controller automatically.

Open:

Window > Animation > Animator

Create a Blend Tree for locomotion.

The Blend Tree contains:

  • idle;
  • walk.

Create a float parameter named:

Speed

The Blend Tree uses Speed to blend between idle and walking.

A simple setup can use:

Speed Animation
0 Idle
Positive movement value Walk or run

The exact threshold depends on the character controller’s velocity range.

Animator Parameters

The tutorial uses two Animator parameters:

Parameter Type Purpose
Speed Float Blends between idle and movement.
Attack Trigger Starts the one-shot attack animation.

Parameter names in code must match the Animator Controller exactly.

To avoid repeated string lookups, cache parameter hashes.

Example:

C#

private static readonly int SpeedHash =
    Animator.StringToHash("Speed");

private static readonly int AttackHash =
    Animator.StringToHash("Attack");

Approach 1: NetworkMecanimAnimator

Fusion includes:

NetworkMecanimAnimator

This component integrates Unity’s Animator with Fusion network synchronization.

Add it to the player prefab and assign the character’s Animator.

The component can synchronize supported Animator parameters across the network.

This is useful when:

  • prototyping;
  • synchronizing a small Animator Controller quickly;
  • minimizing custom networking code;
  • implementing non-critical animation state.

NetworkMecanimAnimator Setup

On the player prefab:

  1. Add NetworkMecanimAnimator.
  2. Assign the Unity Animator.
  3. Review the component’s synchronization settings.
  4. Configure the parameters that need to be synchronized.
  5. Save or apply the prefab changes.

Supported Animator parameter categories can include:

  • floats;
  • integers;
  • booleans;
  • triggers.

Only synchronize parameters that affect the required networked visual result.

PlayerAnimatorNMA

Create a new script:

PlayerAnimatorNMA

The script inherits from:

C#

NetworkBehaviour

Its responsibilities are:

  • cache the movement controller;
  • cache the NetworkMecanimAnimator;
  • calculate movement speed;
  • update the Animator’s speed parameter;
  • submit attack triggers;
  • buffer attack input between Unity frames and Fusion ticks.

Caching References

The script needs references to:

  • the Animator;
  • the character movement controller;
  • the NetworkMecanimAnimator.

Example:

C#

using Fusion;
using UnityEngine;

public class PlayerAnimatorNMA : NetworkBehaviour
{
    [SerializeField]
    private Animator _animator;

    [SerializeField]
    private NetworkMecanimAnimator _networkAnimator;

    [SerializeField]
    private NetworkCharacterController _characterController;

    private static readonly int SpeedHash =
        Animator.StringToHash("Speed");

    private static readonly int AttackHash =
        Animator.StringToHash("Attack");
}

The exact movement controller type depends on the sample.

Calculating Movement Speed

In FixedUpdateNetwork(), read the character’s velocity.

For locomotion animation, horizontal speed is usually more useful than total 3D speed.

Conceptually:

C#

public override void FixedUpdateNetwork()
{
    Vector3 velocity =
        _characterController.Velocity;

    velocity.y = 0f;

    float speed = velocity.magnitude;

    _animator.SetFloat(
        SpeedHash,
        speed
    );
}

Removing the vertical component prevents jumping or falling from being interpreted as horizontal running.

Authority and Animation Input

Only the player that controls the object should read local device input and submit animation actions.

In Shared Mode, this commonly means checking:

C#

Object.HasStateAuthority

before reading attack input or modifying authoritative Networked state.

Proxy players should display synchronized animation results, not read the local keyboard.

Attack Animation Setup

Add the attack animation to the Animator Controller.

A common setup is:

  1. Add an Attack state.
  2. Create a transition from Any State to Attack.
  3. Add the Attack trigger condition.
  4. Create a transition from Attack back to locomotion.
  5. Remove or reduce transition duration for a responsive result.
  6. Disable looping on the attack clip.

The attack should play once and then return to the locomotion Blend Tree.

Capturing Attack Input

Unity input is read during rendered frames.

Fusion simulation updates occur on network ticks.

A one-frame key press can be missed if it is checked only during FixedUpdateNetwork().

Buffer the attack request in Update():

C#

private bool _isAttacking;

private void Update()
{
    if (!Object ||
        !Object.HasStateAuthority)
    {
        return;
    }

    _isAttacking |=
        Input.GetKeyDown(KeyCode.Space);
}

Using |= preserves an earlier press until the next network tick consumes it.

Consuming the Attack Request

In FixedUpdateNetwork():

C#

public override void FixedUpdateNetwork()
{
    UpdateMovementAnimation();

    if (!_isAttacking)
    {
        return;
    }

    _isAttacking = false;

    _networkAnimator.SetTrigger(
        AttackHash
    );
}

The request is cleared after it has been sent.

This prevents one key press from repeatedly triggering the animation.

Why Use NetworkMecanimAnimator.SetTrigger?

Do not use only:

C#

_animator.SetTrigger(AttackHash);

for a networked trigger that other peers need to observe.

Use:

C#

_networkAnimator.SetTrigger(AttackHash);

so Fusion synchronizes the trigger through NetworkMecanimAnimator.

A direct Animator trigger affects only the local Unity Animator unless another synchronization mechanism is implemented.

Testing NetworkMecanimAnimator

Test with multiple clients.

  1. Start the first client.
  2. Move the character.
  3. Confirm that idle and walking blend correctly.
  4. Trigger the attack.
  5. Start a second client.
  6. Observe the first character remotely.
  7. Verify that locomotion and attack animation appear on both peers.

Check the result from:

  • the local player;
  • the remote proxy;
  • both directions between two players.

Advantages of NetworkMecanimAnimator

NetworkMecanimAnimator provides:

  • quick setup;
  • minimal custom synchronization code;
  • direct integration with Animator parameters;
  • built-in trigger support;
  • a practical prototyping workflow.

It is suitable when development speed matters more than fine-grained control.

Limitations of NetworkMecanimAnimator

Automatic Animator synchronization can replicate more data than the project actually needs.

Potential limitations include:

  • synchronization overhead for multiple parameters;
  • less control over the exact network representation;
  • tighter coupling between Animator Controller parameters and network data;
  • reduced ability to optimize values individually;
  • limited suitability for deterministic, tick-critical combat animation.

For production projects, manual Networked properties can provide more explicit control.

Approach 2: Networked Properties

The second approach removes NetworkMecanimAnimator.

Instead, the project defines only the Networked values required for animation.

Create:

PlayerAnimatorNetworkProperties

The script uses:

  • a Networked speed value;
  • a Networked attack counter;
  • FixedUpdateNetwork() for state updates;
  • Render() for Animator presentation.

Separating State from Presentation

With this approach:

  • FixedUpdateNetwork() writes synchronized gameplay-facing values;
  • Render() reads those values and updates the Unity Animator.

This follows a clear separation:

Method Responsibility
FixedUpdateNetwork() Write or update network state.
Render() Apply network state to visual presentation.

The Animator remains a Unity presentation component rather than the source of network state.

Networked Speed

Define only the locomotion value required by the Animator:

C#

[Networked]
private float NetSpeed { get; set; }

In FixedUpdateNetwork():

C#

public override void FixedUpdateNetwork()
{
    if (Object.HasStateAuthority)
    {
        Vector3 velocity =
            _characterController.Velocity;

        velocity.y = 0f;

        NetSpeed = velocity.magnitude;
    }
}

Only State Authority writes the property.

Fusion replicates the value to proxies.

Updating the Animator from Render

In Render():

C#

public override void Render()
{
    _animator.SetFloat(
        SpeedHash,
        NetSpeed
    );
}

Every peer uses the synchronized NetSpeed value to update its local Animator.

This includes:

  • the authoritative player;
  • remote proxies;
  • interpolated render state.

Synchronizing an Attack Event

An attack trigger is an event rather than a continuous state.

A Networked counter can represent how many attack events have occurred.

Example:

C#

[Networked]
private byte AttackCount { get; set; }

When the player attacks:

C#

AttackCount++;

A byte is sufficient when only change detection is required and wraparound is acceptable.

Why Use a Counter Instead of a Boolean?

A boolean can fail to represent repeated identical events.

For example:

  1. Attack = true.
  2. The attack plays.
  3. Another attack occurs while the value is still true.
  4. No Networked value change occurs.

A counter changes for every event:

0 → 1 → 2 → 3

Even if the same animation plays repeatedly, each increment represents a new attack.

Detecting Attack Changes

Store the last rendered attack count locally:

C#

private byte _lastAttackCount;

In Render():

C#

public override void Render()
{
    _animator.SetFloat(
        SpeedHash,
        NetSpeed
    );

    if (_lastAttackCount != AttackCount)
    {
        _lastAttackCount = AttackCount;

        _animator.SetTrigger(
            AttackHash
        );
    }
}

The trigger fires only when the synchronized counter changes.

Initializing the Local Counter

When the object spawns, initialize the local comparison value.

Example:

C#

public override void Spawned()
{
    _lastAttackCount = AttackCount;
}

This prevents a proxy from interpreting the initial Networked value as a new attack event.

The required initialization can vary depending on when Render() first executes and how spawned objects are initialized.

Full Manual Synchronization Example

A simplified implementation can look like:

C#

using Fusion;
using UnityEngine;

public class PlayerAnimatorNetworkProperties :
    NetworkBehaviour
{
    [SerializeField]
    private Animator _animator;

    [SerializeField]
    private NetworkCharacterController
        _characterController;

    [Networked]
    private float NetSpeed { get; set; }

    [Networked]
    private byte AttackCount { get; set; }

    private bool _attackRequested;
    private byte _lastAttackCount;

    private static readonly int SpeedHash =
        Animator.StringToHash("Speed");

    private static readonly int AttackHash =
        Animator.StringToHash("Attack");

    private void Update()
    {
        if (!Object ||
            !Object.HasStateAuthority)
        {
            return;
        }

        _attackRequested |=
            Input.GetKeyDown(KeyCode.Space);
    }

    public override void Spawned()
    {
        _lastAttackCount = AttackCount;
    }

    public override void FixedUpdateNetwork()
    {
        if (!Object.HasStateAuthority)
        {
            return;
        }

        Vector3 velocity =
            _characterController.Velocity;

        velocity.y = 0f;

        NetSpeed = velocity.magnitude;

        if (_attackRequested)
        {
            _attackRequested = false;
            AttackCount++;
        }
    }

    public override void Render()
    {
        _animator.SetFloat(
            SpeedHash,
            NetSpeed
        );

        if (_lastAttackCount ==
            AttackCount)
        {
            return;
        }

        _lastAttackCount =
            AttackCount;

        _animator.SetTrigger(
            AttackHash
        );
    }
}

The exact controller APIs and authority checks depend on the sample architecture.

Preventing Attack Spam

A potential issue occurs when the attack request remains active across multiple ticks.

If it is not cleared correctly, the attack counter can increment every network tick.

Ensure the buffered request is reset immediately after consumption:

C#

if (_attackRequested)
{
    _attackRequested = false;
    AttackCount++;
}

Additional gameplay restrictions can include:

  • attack cooldown;
  • animation-state validation;
  • weapon cooldown;
  • stamina requirements;
  • action locks;
  • server or authority validation.

Attack Cooldown

For production gameplay, do not rely only on animation duration to prevent repeated attacks.

Store gameplay cooldown state using Fusion-compatible state such as:

C#

TickTimer

Conceptually:

C#

[Networked]
private TickTimer AttackCooldown {
    get;
    set;
}

Then allow a new attack only when the timer has expired.

Animation state should represent gameplay state rather than define whether gameplay actions are legal.

Comparing Both Approaches

Criteria NetworkMecanimAnimator Manual Networked Properties
Setup speed Faster More implementation work
Custom code Minimal Explicit
Parameter control Component-driven Fully controlled by project code
Network data Can synchronize several Animator parameters Only declared values are synchronized
Trigger support Built in Implemented with counters or state
Optimization Convenient but less granular More granular
Prototyping Strong fit More work than necessary
Production control Suitable for many projects Better when explicit bandwidth control matters
Tick-critical combat Limited by normal Mecanim presentation Requires a more advanced tick-based approach

When to Use NetworkMecanimAnimator

Use NetworkMecanimAnimator when:

  • prototyping;
  • building a small co-op game;
  • animation synchronization is mostly visual;
  • the Animator Controller is simple;
  • minimal implementation time is preferred;
  • tick-accurate animation is not required.

When to Use Networked Properties

Use custom Networked properties when:

  • only a few values need synchronization;
  • bandwidth should be controlled explicitly;
  • networking should remain independent from Animator configuration;
  • animation state needs custom validation;
  • events need a compact representation;
  • presentation logic should be driven from Render().

Animation State vs Gameplay State

Animation should generally visualize gameplay state.

For example:

  • velocity determines the locomotion animation;
  • an accepted attack action increments the attack counter;
  • a gameplay death state selects the death animation;
  • a weapon state determines reload animation.

Avoid making the Animator the authority for critical gameplay logic.

The Animator can be affected by:

  • blending;
  • transition timing;
  • local frame rate;
  • presentation settings;
  • animation speed.

Critical gameplay state should remain in Networked simulation data.

Tick-Accurate Animations

Normal Mecanim synchronization is suitable for many visual animation requirements.

Competitive games may require animation to be aligned precisely with simulation ticks.

Examples include:

  • fighting-game hitboxes;
  • melee collision windows;
  • competitive shooter reload states;
  • frame-specific invulnerability;
  • deterministic combat timing;
  • rollback-sensitive attacks.

For these cases, use the Fusion Animations technical sample and its tick-accurate animation patterns.

Tick-Accurate Combat

In a tick-critical system, gameplay should know the exact simulation tick for:

  • attack start;
  • active hitbox window;
  • recovery period;
  • animation completion;
  • cancel window;
  • damage application.

The Unity Animator can display the result, but gameplay timing should be represented in network state.

Render Method

Render() is called for Unity presentation updates.

It is a suitable place to apply synchronized values to:

  • Animator parameters;
  • model transforms;
  • visual effects;
  • UI;
  • cosmetic state.

Do not use Render() to modify authoritative Networked properties.

FixedUpdateNetwork Method

FixedUpdateNetwork() is Fusion’s tick-based simulation callback.

Use it for:

  • reading network input;
  • updating Networked properties;
  • validating attack requests;
  • calculating synchronized movement state;
  • changing gameplay state.

Keep visual Animator updates separate where possible.

Float Synchronization Considerations

A raw float speed value may change frequently.

Projects can reduce network variation by:

  • normalizing the speed range;
  • quantizing the value;
  • synchronizing a smaller state enum;
  • synchronizing movement intent instead of exact velocity;
  • applying thresholds before updating;
  • deriving animation speed locally from synchronized movement.

The best approach depends on animation quality and bandwidth requirements.

State-Based Locomotion Alternative

Instead of synchronizing a float, a project can synchronize a compact locomotion state:

C#

public enum LocomotionState : byte
{
    Idle,
    Walk,
    Run
}

This reduces data but removes continuous blending unless the client derives a blend value locally.

Use a float when smooth blending is important.

Use an enum when discrete animation states are sufficient.

Testing

Test with at least two clients.

Verify:

  1. Local idle animation.
  2. Remote idle animation.
  3. Local walk or run animation.
  4. Remote walk or run animation.
  5. Local attack playback.
  6. Remote attack playback.
  7. Repeated attacks.
  8. Joining while another player is already moving.
  9. Joining during or after an attack.
  10. Different network conditions.

Late Join Behavior

Current Networked properties are available to late joiners.

A late joiner can receive:

  • current speed;
  • current movement state;
  • current persistent animation state.

A one-shot attack that already finished should not normally replay for a late joiner.

The counter pattern represents event history numerically, so initialize local comparison state carefully to avoid replaying old events on spawn.

Animator Transition Configuration

Networking code can be correct while the Animator still produces poor results.

Check:

  • transition duration;
  • exit time;
  • interruption settings;
  • looping;
  • Blend Tree thresholds;
  • attack return transition;
  • layer weights;
  • avatar configuration.

For a responsive attack:

  • use a short transition into attack;
  • avoid unexpected exit-time delays;
  • ensure the state returns to locomotion;
  • prevent unintended looping.

Root Motion

The tutorial synchronizes animations for a character whose movement is controlled by the network character controller.

If Root Motion is enabled, animation can also move the character.

This creates additional networking considerations.

For standard networked movement:

  • keep gameplay movement controlled by Fusion;
  • use animations as visual representation;
  • disable Root Motion unless the project has a deliberate synchronized Root Motion architecture.

Multiple Animator Layers

Projects can extend the same patterns to:

  • upper-body attack layers;
  • additive aiming;
  • weapon layers;
  • emote layers;
  • damage reactions.

Only synchronize the values required to reconstruct the visual result.

For example:

  • movement speed;
  • equipped weapon ID;
  • attack counter;
  • aim weight;
  • stance state.
  1. Prepare the animation clips.
  2. Create the Animator Controller.
  3. Add locomotion parameters and transitions.
  4. Add NetworkMecanimAnimator.
  5. Assign the Animator.
  6. Cache parameter hashes.
  7. Update movement values from FixedUpdateNetwork().
  8. Buffer local one-frame input.
  9. Send triggers through NetworkMecanimAnimator.SetTrigger.
  10. Test with multiple clients.
  1. Identify the minimum state required for animation.
  2. Create Networked properties for that state.
  3. Let State Authority write the values.
  4. Buffer rendered-frame input until the next network tick.
  5. Represent repeated events with counters.
  6. Clear buffered requests after consumption.
  7. Read synchronized values from Render().
  8. Update the Animator locally.
  9. Initialize event comparison values during spawn.
  10. Add cooldown and validation for gameplay actions.

Key Concepts

Concept Description
NetworkMecanimAnimator Fusion component for synchronizing Unity Animator parameters.
Animator parameter Float, boolean, integer, or trigger that drives animation state.
Blend Tree Animator state that blends multiple clips using a parameter.
FixedUpdateNetwork() Tick-based Fusion simulation callback.
Render() Unity presentation callback for displaying synchronized state.
[Networked] Declares replicated Fusion state.
Attack counter Incrementing Networked value used to represent repeated events.
Buffered input Local input stored until a Fusion tick consumes it.
State Authority Peer allowed to write the player object’s Networked properties.
Parameter hash Cached integer identifier for an Animator parameter.
Tick-accurate animation Animation state aligned precisely with simulation ticks.
TickTimer Fusion timer suitable for synchronized cooldowns and durations.

Best Practices

Use this checklist when synchronizing Fusion animations:

  • Keep gameplay state separate from Unity Animator state.
  • Use NetworkMecanimAnimator for fast setup and prototypes.
  • Use Networked properties when explicit control is required.
  • Synchronize only the values needed by remote clients.
  • Let State Authority write animation-related Networked state.
  • Update visual Animator parameters from Render().
  • Buffer one-frame local input before consuming it in FixedUpdateNetwork().
  • Clear buffered actions after they are processed.
  • Use counters for repeated one-shot events.
  • Initialize local counter tracking when the object spawns.
  • Cache Animator parameter hashes.
  • Remove vertical velocity when calculating horizontal locomotion speed.
  • Add gameplay cooldowns independently from animation transitions.
  • Test all animation states from local and proxy perspectives.
  • Use tick-based animation systems for competitive frame-sensitive gameplay.

Common Pitfalls

Pitfall Why It Is a Problem
Reading GetKeyDown only in FixedUpdateNetwork() The one-frame input can be missed between network ticks.
Calling Animator.SetTrigger only locally Remote peers do not receive the event.
Letting proxy players read local input Remote player objects may react to the wrong client’s device input.
Using a boolean for repeated attacks Repeated identical events may not produce a replicated change.
Not clearing the attack request The attack can trigger every network tick.
Updating Animator state as authoritative gameplay logic Animator timing is presentation-driven and frame-dependent.
Synchronizing every Animator parameter Can add unnecessary network state and coupling.
Updating Networked properties from Render() Render() is intended for presentation, not authoritative state changes.
Forgetting to initialize the last attack counter A previous event can replay when the object spawns.
Using animation duration as the only attack cooldown Visual state should not be the sole gameplay authority.
Enabling Root Motion without a network architecture for it Animation and network movement can conflict.
Using regular Mecanim sync for tick-critical hitboxes Visual animation may not align precisely across simulation ticks.

Summary

Fusion provides two practical approaches for synchronizing Unity animations.

NetworkMecanimAnimator offers a fast setup for synchronizing Animator parameters and triggers with minimal code.

Manual Networked properties provide more explicit control. State Authority writes values such as movement speed and attack counters during FixedUpdateNetwork(), while every peer applies those values to its local Animator from Render().

For ordinary idle, movement, attack, and co-op animation requirements, either approach can work. For competitive gameplay where hitboxes and animation phases must align with exact simulation ticks, use a tick-accurate animation architecture.

Last updated on

Back to top