Sync per-pose index and locomotion state across the network
Add PlayerAnimationSync NetworkBehaviour with owner-authoritative NetworkVariables for pose index and locomotion category (idle/walk/run/fall). Remote avatars switch their PlayerPoseController into network-controlled mode, skipping local input, sprint reset, capsule resize and camera framing, and take pose index plus locomotion externally. Disable pose persistence on network-driven remotes so foreign avatars can't overwrite the local save. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
4fa443f630
commit
7f2f4e5999
5 files changed
+186
-29
No files matched your search
@@ -0,0 +1,70 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// <see cref="PlayerPoseController"/> 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.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(PlayerPoseController))]
|
||||
[DisallowMultipleComponent]
|
||||
[DefaultExecutionOrder(200)]
|
||||
public class PlayerAnimationSync : NetworkBehaviour
|
||||
{
|
||||
private readonly NetworkVariable<int> _poseIndex =
|
||||
new(0, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
||||
|
||||
// Locomotion category stored as a byte (PlayerPoseController.Locomotion has only a handful of values).
|
||||
private readonly NetworkVariable<byte> _locomotion =
|
||||
new(0, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
||||
|
||||
private PlayerPoseController _pose;
|
||||
|
||||
void Awake() => _pose = GetComponent<PlayerPoseController>();
|
||||
|
||||
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);
|
||||
}
|
||||
Reference in new issue
Block a user