syncing an enum value versus commands for networked animation states

In the docs for networking animation state changes, the examples use a command to broadcast that a trigger should be fired. I was originally using this method in my implementation but I’ve just refactored it to using a [sync] on an int, since my “behavior” for animations is just a state machine anyways, so I’m able to transition the state on the int being sync’d just as well as reacting to a command.

An advantage of this approach is that the sync annotation is less likely to be misconfigured, since I don’t need to go into the Coherence Sync config and ensure that my command method is configured to be networked, I can just slap this decorator on the property and be done with it.

Is there any reason not to use a sync representing a “state” (an enum) in this way? My only thought on why you might not want to do this is that it could be less performant since presumably now the SDK has to monitor the value explicitely rather than just send a command on demand.

Here’s my full script for further context:

public class UnitAnimationBehavior : MonoBehaviour
{
    private class AnimationStateMachine : AbstractObservableMachine<AnimationBehaviorStates>
    {
        public AnimationStateMachine(AnimationBehaviorStates initialState) : base(initialState)
        {}
        public void Transition(AnimationBehaviorStates s) { TransitionInternalState(s); }
    }

    private static readonly int AttackTrigger = Animator.StringToHash("Attack");
    private static readonly int DeathTrigger = Animator.StringToHash("Death");
    private static readonly int MovementTrigger = Animator.StringToHash("Movement");
    private Animator _animator;
    private AnimationStateMachine _machine;
    private CoherenceSync _coherenceSync;
    
    [Sync]
    [OnValueSynced(nameof(OnBehaviorStateValueSync))]
    public int iNetBehaviorState = (int)AnimationBehaviorStates.Idle;

    void Awake()
    {
        _coherenceSync = GetComponent<CoherenceSync>();
        
        _machine = new AnimationStateMachine(AnimationBehaviorStates.Idle);
        _animator = GetComponent<Animator>();
        
        // Exit transitions
        _machine.OnStateExit(AnimationBehaviorStates.Harvesting, () => _animator.ResetTrigger(AttackTrigger));
        // more transitions not shown...
        
        // Enter transitions
        _machine.OnStateEnter(AnimationBehaviorStates.Harvesting, () => _animator.SetTrigger(AttackTrigger));
        // more transitions not shown...
    }

    public void OnBehaviorStateValueSync(int oldValue, int newValue)
    {
        this.LogLog($"{(AnimationBehaviorStates)oldValue} -> {(AnimationBehaviorStates)newValue}");
        TransitionState((AnimationBehaviorStates)newValue);
    }
    
    public void TransitionState(AnimationBehaviorStates nextState)
    {
        _machine.Transition(nextState);
        AuthorityHelper.DoIfStateAuthority(_coherenceSync, () =>
        {
            iNetBehaviorState = (int)nextState;
        });
    }
}

Are you aware of the [Command] attribute? With it, you can mark a method to be baked into a command, exactly how [Sync] does it for properties.
(you still need to invoke the command as usual)

Thanks for the reminder about [Command] here Ciro. I think that might be a better fit for this use-caes.

1 Like