using Unity.Netcode;
using UnityEngine;
///
/// Replicates the pose system's animation state across the network. The owner publishes its selected
/// pose index and resolved locomotion category (idle / walk / run / fall); remote avatars switch their
/// to network-driven mode and play the matching clip, so a remote
/// player shows the same pose and walk/run/fall animation the owner sees — not just a sliding idle.
///
/// Position/rotation come from the NetworkTransform; this only carries the small animation state. Both
/// values are owner-authoritative and change rarely (a pose cycle, a locomotion transition), so the
/// NetworkVariables send a delta only when the value actually changes.
///
/// Runs after the PlayerPoseController (execution order 100) so the owner reads this frame's resolved
/// state; remote avatars apply via OnValueChanged, independent of frame order.
///
[RequireComponent(typeof(PlayerPoseController))]
[DisallowMultipleComponent]
[DefaultExecutionOrder(200)]
public class PlayerAnimationSync : NetworkBehaviour
{
private readonly NetworkVariable _poseIndex =
new(0, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
// Locomotion category stored as a byte (PlayerPoseController.Locomotion has only a handful of values).
private readonly NetworkVariable _locomotion =
new(0, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
private PlayerPoseController _pose;
void Awake() => _pose = GetComponent();
public override void OnNetworkSpawn()
{
if (IsOwner)
return;
// Remote avatar: stop reading local input/physics and drive the pose from replicated state.
_pose.EnableNetworkDrive();
_poseIndex.OnValueChanged += OnPoseIndexChanged;
_locomotion.OnValueChanged += OnLocomotionChanged;
ApplyRemote(); // spawn-time values are already replicated
}
public override void OnNetworkDespawn()
{
_poseIndex.OnValueChanged -= OnPoseIndexChanged;
_locomotion.OnValueChanged -= OnLocomotionChanged;
}
void Update()
{
if (!IsOwner || !_pose)
return;
// Publish the owner's resolved animation state. Assigning the same value is a no-op for
// replication (the NetworkVariable dirties only on an actual change), but guard anyway to keep
// intent clear.
int index = _pose.CurrentIndex;
byte loco = (byte)_pose.CurrentLocomotion;
if (_poseIndex.Value != index) _poseIndex.Value = index;
if (_locomotion.Value != loco) _locomotion.Value = loco;
}
private void OnPoseIndexChanged(int previous, int current) => ApplyRemote();
private void OnLocomotionChanged(byte previous, byte current) => ApplyRemote();
private void ApplyRemote() =>
_pose.SetNetworkState(_poseIndex.Value, (PlayerPoseController.Locomotion)_locomotion.Value);
}