com.unity.netcode.gameobjects@1.3.1
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). ## [1.3.1] - 2023-03-27 ### Added - Added detection and graceful handling of corrupt packets for additional safety. (#2419) ### Changed - The UTP component UI has been updated to be more user-friendly for new users by adding a simple toggle to switch between local-only (127.0.0.1) and remote (0.0.0.0) binding modes, using the toggle "Allow Remote Connections" (#2408) - Updated `UnityTransport` dependency on `com.unity.transport` to 1.3.3. (#2450) - `NetworkShow()` of `NetworkObject`s are delayed until the end of the frame to ensure consistency of delta-driven variables like `NetworkList`. - Dirty `NetworkObject` are reset at end-of-frame and not at serialization time. - `NetworkHide()` of an object that was just `NetworkShow()`n produces a warning, as remote clients will _not_ get a spawn/despawn pair. - Renamed the NetworkTransform.SetState parameter `shouldGhostsInterpolate` to `teleportDisabled` for better clarity of what that parameter does. (#2228) - Network prefabs are now stored in a ScriptableObject that can be shared between NetworkManagers, and have been exposed for public access. By default, a Default Prefabs List is created that contains all NetworkObject prefabs in the project, and new NetworkManagers will default to using that unless that option is turned off in the Netcode for GameObjects settings. Existing NetworkManagers will maintain their existing lists, which can be migrated to the new format via a button in their inspector. (#2322) ### Fixed - Fixed issue where changes to a layer's weight would not synchronize unless a state transition was occurring.(#2399) - Fixed issue where `NetworkManager.LocalClientId` was returning the `NetworkTransport.ServerClientId` as opposed to the `NetworkManager.m_LocalClientId`. (#2398) - Fixed issue where a dynamically spawned `NetworkObject` parented under an in-scene placed `NetworkObject` would have its `InScenePlaced` value changed to `true`. This would result in a soft synchronization error for late joining clients. (#2396) - Fixed a UTP test that was failing when you install Unity Transport package 2.0.0 or newer. (#2347) - Fixed issue where `NetcodeSettingsProvider` would throw an exception in Unity 2020.3.x versions. (#2345) - Fixed server side issue where, depending upon component ordering, some NetworkBehaviour components might not have their OnNetworkDespawn method invoked if the client side disconnected. (#2323) - Fixed a case where data corruption could occur when using UnityTransport when reaching a certain level of send throughput. (#2332) - Fixed an issue in `UnityTransport` where an exception would be thrown if starting a Relay host/server on WebGL. This exception should only be thrown if using direct connections (where WebGL can't act as a host/server). (#2321) - Fixed `NetworkAnimator` issue where it was not checking for `AnimatorStateTtansition.destinationStateMachine` and any possible sub-states defined within it. (#2309) - Fixed `NetworkAnimator` issue where the host client was receiving the ClientRpc animation updates when the host was the owner.(#2309) - Fixed `NetworkAnimator` issue with using pooled objects and when specific properties are cleaned during despawn and destroy.(#2309) - Fixed issue where `NetworkAnimator` was checking for animation changes when the associated `NetworkObject` was not spawned.(#2309) - Corrected an issue with the documentation for BufferSerializer (#2401)
This commit is contained in:
@@ -1,10 +1,15 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[assembly: InternalsVisibleTo("Unity.Netcode.EditorTests")]
|
||||
[assembly: InternalsVisibleTo("Unity.Netcode.Editor.CodeGen")]
|
||||
[assembly: InternalsVisibleTo("Unity.Netcode.Editor")]
|
||||
[assembly: InternalsVisibleTo("TestProject.EditorTests")]
|
||||
[assembly: InternalsVisibleTo("TestProject.RuntimeTests")]
|
||||
#endif
|
||||
[assembly: InternalsVisibleTo("Unity.Netcode.Editor.CodeGen")]
|
||||
#endif // UNITY_EDITOR
|
||||
|
||||
#if UNITY_INCLUDE_TESTS
|
||||
[assembly: InternalsVisibleTo("Unity.Netcode.RuntimeTests")]
|
||||
[assembly: InternalsVisibleTo("TestProject.RuntimeTests")]
|
||||
#if UNITY_EDITOR
|
||||
[assembly: InternalsVisibleTo("Unity.Netcode.EditorTests")]
|
||||
[assembly: InternalsVisibleTo("TestProject.EditorTests")]
|
||||
#endif // UNITY_EDITOR
|
||||
#endif // UNITY_INCLUDE_TESTS
|
||||
|
||||
@@ -23,12 +23,6 @@ namespace Unity.Netcode.Components
|
||||
/// </summary>
|
||||
private void FlushMessages()
|
||||
{
|
||||
foreach (var clientId in m_ClientsToSynchronize)
|
||||
{
|
||||
m_NetworkAnimator.ServerSynchronizeNewPlayer(clientId);
|
||||
}
|
||||
m_ClientsToSynchronize.Clear();
|
||||
|
||||
foreach (var sendEntry in m_SendParameterUpdates)
|
||||
{
|
||||
m_NetworkAnimator.SendParametersUpdateClientRpc(sendEntry.ParametersUpdateMessage, sendEntry.ClientRpcParams);
|
||||
@@ -64,9 +58,10 @@ namespace Unity.Netcode.Components
|
||||
}
|
||||
|
||||
// Everyone applies any parameters updated
|
||||
foreach (var parameterUpdate in m_ProcessParameterUpdates)
|
||||
for (int i = 0; i < m_ProcessParameterUpdates.Count; i++)
|
||||
{
|
||||
m_NetworkAnimator.UpdateParameters(parameterUpdate);
|
||||
var parameterUpdate = m_ProcessParameterUpdates[i];
|
||||
m_NetworkAnimator.UpdateParameters(ref parameterUpdate);
|
||||
}
|
||||
m_ProcessParameterUpdates.Clear();
|
||||
|
||||
@@ -80,20 +75,6 @@ namespace Unity.Netcode.Components
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clients that need to be synchronized to the relative Animator
|
||||
/// </summary>
|
||||
private List<ulong> m_ClientsToSynchronize = new List<ulong>();
|
||||
|
||||
/// <summary>
|
||||
/// When a new client is connected, they are added to the
|
||||
/// m_ClientsToSynchronize list.
|
||||
/// </summary>
|
||||
internal void SynchronizeClient(ulong clientId)
|
||||
{
|
||||
m_ClientsToSynchronize.Add(clientId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A pending outgoing Animation update for (n) clients
|
||||
/// </summary>
|
||||
@@ -157,13 +138,6 @@ namespace Unity.Netcode.Components
|
||||
m_SendTriggerUpdates.Add(new TriggerUpdate() { AnimationTriggerMessage = animationTriggerMessage, SendToServer = true });
|
||||
}
|
||||
|
||||
private Queue<NetworkAnimator.AnimationMessage> m_AnimationMessageQueue = new Queue<NetworkAnimator.AnimationMessage>();
|
||||
|
||||
internal void AddAnimationMessageToProcessQueue(NetworkAnimator.AnimationMessage message)
|
||||
{
|
||||
m_AnimationMessageQueue.Enqueue(message);
|
||||
}
|
||||
|
||||
internal void DeregisterUpdate()
|
||||
{
|
||||
NetworkUpdateLoop.UnregisterNetworkUpdate(this, NetworkUpdateStage.PreUpdate);
|
||||
@@ -226,13 +200,82 @@ namespace Unity.Netcode.Components
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void ParseStateMachineStates(int layerIndex, ref AnimatorController animatorController, ref AnimatorStateMachine stateMachine)
|
||||
{
|
||||
for (int y = 0; y < stateMachine.states.Length; y++)
|
||||
{
|
||||
var animatorState = stateMachine.states[y].state;
|
||||
for (int z = 0; z < animatorState.transitions.Length; z++)
|
||||
{
|
||||
var transition = animatorState.transitions[z];
|
||||
if (transition.conditions.Length == 0 && transition.isExit)
|
||||
{
|
||||
// We don't need to worry about exit transitions with no conditions
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var condition in transition.conditions)
|
||||
{
|
||||
var parameterName = condition.parameter;
|
||||
|
||||
var parameters = animatorController.parameters;
|
||||
// Find the associated parameter for the condition
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
// Only process the associated parameter(s)
|
||||
if (parameter.name != parameterName)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (parameter.type)
|
||||
{
|
||||
case AnimatorControllerParameterType.Trigger:
|
||||
{
|
||||
|
||||
if (transition.destinationStateMachine != null)
|
||||
{
|
||||
var destinationStateMachine = transition.destinationStateMachine;
|
||||
ParseStateMachineStates(layerIndex, ref animatorController, ref destinationStateMachine);
|
||||
}
|
||||
else if (transition.destinationState != null)
|
||||
{
|
||||
var transitionInfo = new TransitionStateinfo()
|
||||
{
|
||||
Layer = layerIndex,
|
||||
OriginatingState = animatorState.nameHash,
|
||||
DestinationState = transition.destinationState.nameHash,
|
||||
TransitionDuration = transition.duration,
|
||||
TriggerNameHash = parameter.nameHash,
|
||||
TransitionIndex = z
|
||||
};
|
||||
TransitionStateInfoList.Add(transitionInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"[{name}][Conditional Transition for {animatorState.name}] Conditional triggered transition has neither a DestinationState nor a DestinationStateMachine! This transition is not likely to synchronize properly. " +
|
||||
$"Please file a GitHub issue about this error with details about your Animator's setup.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Creates the TransitionStateInfoList table
|
||||
/// </summary>
|
||||
private void BuildTransitionStateInfoList()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (UnityEditor.EditorApplication.isUpdating)
|
||||
if (UnityEditor.EditorApplication.isUpdating || UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -245,54 +288,8 @@ namespace Unity.Netcode.Components
|
||||
|
||||
for (int x = 0; x < animatorController.layers.Length; x++)
|
||||
{
|
||||
var layer = animatorController.layers[x];
|
||||
|
||||
for (int y = 0; y < layer.stateMachine.states.Length; y++)
|
||||
{
|
||||
var animatorState = layer.stateMachine.states[y].state;
|
||||
var transitions = layer.stateMachine.GetStateMachineTransitions(layer.stateMachine);
|
||||
for (int z = 0; z < animatorState.transitions.Length; z++)
|
||||
{
|
||||
var transition = animatorState.transitions[z];
|
||||
if (transition.conditions.Length == 0 && transition.isExit)
|
||||
{
|
||||
// We don't need to worry about exit transitions with no conditions
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var condition in transition.conditions)
|
||||
{
|
||||
var parameterName = condition.parameter;
|
||||
var parameters = animatorController.parameters;
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
switch (parameter.type)
|
||||
{
|
||||
case AnimatorControllerParameterType.Trigger:
|
||||
{
|
||||
// Match the condition with an existing trigger
|
||||
if (parameterName == parameter.name)
|
||||
{
|
||||
var transitionInfo = new TransitionStateinfo()
|
||||
{
|
||||
Layer = x,
|
||||
OriginatingState = animatorState.nameHash,
|
||||
DestinationState = transition.destinationState.nameHash,
|
||||
TransitionDuration = transition.duration,
|
||||
TriggerNameHash = parameter.nameHash,
|
||||
TransitionIndex = z
|
||||
};
|
||||
TransitionStateInfoList.Add(transitionInfo);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var stateMachine = animatorController.layers[x].stateMachine;
|
||||
ParseStateMachineStates(x, ref animatorController, ref stateMachine);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -488,8 +485,17 @@ namespace Unity.Netcode.Components
|
||||
private int[] m_AnimationHash;
|
||||
private float[] m_LayerWeights;
|
||||
private static byte[] s_EmptyArray = new byte[] { };
|
||||
private List<int> m_ParametersToUpdate;
|
||||
private List<ulong> m_ClientSendList;
|
||||
private ClientRpcParams m_ClientRpcParams;
|
||||
private AnimationMessage m_AnimationMessage;
|
||||
private NetworkAnimatorStateChangeHandler m_NetworkAnimatorStateChangeHandler;
|
||||
|
||||
/// <summary>
|
||||
/// Used for integration test purposes
|
||||
/// </summary>
|
||||
internal List<AnimatorStateInfo> SynchronizationStateInfo;
|
||||
|
||||
private unsafe struct AnimatorParamCache
|
||||
{
|
||||
internal int Hash;
|
||||
@@ -519,75 +525,44 @@ namespace Unity.Netcode.Components
|
||||
}
|
||||
}
|
||||
|
||||
private void Cleanup()
|
||||
/// <summary>
|
||||
/// Only things instantiated/created within OnNetworkSpawn should be
|
||||
/// cleaned up here.
|
||||
/// </summary>
|
||||
private void SpawnCleanup()
|
||||
{
|
||||
if (m_NetworkAnimatorStateChangeHandler != null)
|
||||
{
|
||||
m_NetworkAnimatorStateChangeHandler.DeregisterUpdate();
|
||||
m_NetworkAnimatorStateChangeHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_CachedNetworkManager != null)
|
||||
{
|
||||
m_CachedNetworkManager.OnClientConnectedCallback -= OnClientConnectedCallback;
|
||||
}
|
||||
public override void OnDestroy()
|
||||
{
|
||||
SpawnCleanup();
|
||||
|
||||
if (m_CachedAnimatorParameters != null && m_CachedAnimatorParameters.IsCreated)
|
||||
{
|
||||
m_CachedAnimatorParameters.Dispose();
|
||||
}
|
||||
|
||||
if (m_ParameterWriter.IsInitialized)
|
||||
{
|
||||
m_ParameterWriter.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
Cleanup();
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
private List<int> m_ParametersToUpdate;
|
||||
private List<ulong> m_ClientSendList;
|
||||
private ClientRpcParams m_ClientRpcParams;
|
||||
private AnimationMessage m_AnimationMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Used for integration test to validate that the
|
||||
/// AnimationMessage.AnimationStates remains the same
|
||||
/// size as the layer count.
|
||||
/// </summary>
|
||||
internal AnimationMessage GetAnimationMessage()
|
||||
{
|
||||
return m_AnimationMessage;
|
||||
}
|
||||
|
||||
// Only used in Cleanup
|
||||
private NetworkManager m_CachedNetworkManager;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnNetworkSpawn()
|
||||
private void Awake()
|
||||
{
|
||||
int layers = m_Animator.layerCount;
|
||||
|
||||
// Initializing the below arrays for everyone handles an issue
|
||||
// when running in owner authoritative mode and the owner changes.
|
||||
m_TransitionHash = new int[layers];
|
||||
m_AnimationHash = new int[layers];
|
||||
m_LayerWeights = new float[layers];
|
||||
|
||||
if (IsServer)
|
||||
{
|
||||
m_ClientSendList = new List<ulong>(128);
|
||||
m_ClientRpcParams = new ClientRpcParams();
|
||||
m_ClientRpcParams.Send = new ClientRpcSendParams();
|
||||
m_ClientRpcParams.Send.TargetClientIds = m_ClientSendList;
|
||||
|
||||
// Cache the NetworkManager instance to remove the OnClientConnectedCallback subscription
|
||||
m_CachedNetworkManager = NetworkManager;
|
||||
NetworkManager.OnClientConnectedCallback += OnClientConnectedCallback;
|
||||
}
|
||||
// We initialize the m_AnimationMessage for all instances in the event that
|
||||
// ownership or authority changes during runtime.
|
||||
m_AnimationMessage = new AnimationMessage();
|
||||
@@ -612,6 +587,7 @@ namespace Unity.Netcode.Components
|
||||
var parameters = m_Animator.parameters;
|
||||
m_CachedAnimatorParameters = new NativeArray<AnimatorParamCache>(parameters.Length, Allocator.Persistent);
|
||||
m_ParametersToUpdate = new List<int>(parameters.Length);
|
||||
|
||||
for (var i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
var parameter = parameters[i];
|
||||
@@ -652,110 +628,163 @@ namespace Unity.Netcode.Components
|
||||
|
||||
m_CachedAnimatorParameters[i] = cacheParam;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used for integration test to validate that the
|
||||
/// AnimationMessage.AnimationStates remains the same
|
||||
/// size as the layer count.
|
||||
/// </summary>
|
||||
internal AnimationMessage GetAnimationMessage()
|
||||
{
|
||||
return m_AnimationMessage;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
if (IsServer)
|
||||
{
|
||||
m_ClientSendList = new List<ulong>(128);
|
||||
m_ClientRpcParams = new ClientRpcParams();
|
||||
m_ClientRpcParams.Send = new ClientRpcSendParams();
|
||||
m_ClientRpcParams.Send.TargetClientIds = m_ClientSendList;
|
||||
}
|
||||
|
||||
// Create a handler for state changes
|
||||
m_NetworkAnimatorStateChangeHandler = new NetworkAnimatorStateChangeHandler(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
Cleanup();
|
||||
SpawnCleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Synchronizes newly joined players
|
||||
/// Wries all parameter and state information needed to initially synchronize a client
|
||||
/// </summary>
|
||||
internal void ServerSynchronizeNewPlayer(ulong playerId)
|
||||
private void WriteSynchronizationData<T>(ref BufferSerializer<T> serializer) where T : IReaderWriter
|
||||
{
|
||||
m_ClientSendList.Clear();
|
||||
m_ClientSendList.Add(playerId);
|
||||
m_ClientRpcParams.Send.TargetClientIds = m_ClientSendList;
|
||||
// With synchronization we send all parameters
|
||||
m_ParametersToUpdate.Clear();
|
||||
for (int i = 0; i < m_CachedAnimatorParameters.Length; i++)
|
||||
// Parameter synchronization
|
||||
{
|
||||
m_ParametersToUpdate.Add(i);
|
||||
}
|
||||
SendParametersUpdate(m_ClientRpcParams);
|
||||
|
||||
// Reset the dirty count before synchronizing the newly connected client with all layers
|
||||
m_AnimationMessage.IsDirtyCount = 0;
|
||||
|
||||
for (int layer = 0; layer < m_Animator.layerCount; layer++)
|
||||
{
|
||||
AnimatorStateInfo st = m_Animator.GetCurrentAnimatorStateInfo(layer);
|
||||
var stateHash = st.fullPathHash;
|
||||
var normalizedTime = st.normalizedTime;
|
||||
var isInTransition = m_Animator.IsInTransition(layer);
|
||||
|
||||
// Grab one of the available AnimationState entries so we can fill it with the current
|
||||
// layer's animation state.
|
||||
var animationState = m_AnimationMessage.AnimationStates[layer];
|
||||
|
||||
// Synchronizing transitions with trigger conditions for late joining clients is now
|
||||
// handled by cross fading between the late joining client's current layer's AnimationState
|
||||
// and the transition's destination AnimationState.
|
||||
if (isInTransition)
|
||||
// We include all parameters for the initial synchronization
|
||||
m_ParametersToUpdate.Clear();
|
||||
for (int i = 0; i < m_CachedAnimatorParameters.Length; i++)
|
||||
{
|
||||
var tt = m_Animator.GetAnimatorTransitionInfo(layer);
|
||||
var nextState = m_Animator.GetNextAnimatorStateInfo(layer);
|
||||
m_ParametersToUpdate.Add(i);
|
||||
}
|
||||
// Write, apply, and serialize
|
||||
WriteParameters(ref m_ParameterWriter);
|
||||
var parametersMessage = new ParametersUpdateMessage
|
||||
{
|
||||
Parameters = m_ParameterWriter.ToArray()
|
||||
};
|
||||
serializer.SerializeValue(ref parametersMessage);
|
||||
}
|
||||
|
||||
if (nextState.length > 0)
|
||||
{
|
||||
var nextStateTotalSpeed = nextState.speed * nextState.speedMultiplier;
|
||||
var nextStateAdjustedLength = nextState.length * nextStateTotalSpeed;
|
||||
// TODO: We need to get the transition curve for the target state as well as some
|
||||
// reasonable RTT estimate in order to get a more precise normalized synchronization time
|
||||
var transitionTime = Mathf.Min(tt.duration, tt.duration * tt.normalizedTime) * 0.5f;
|
||||
normalizedTime = Mathf.Min(1.0f, transitionTime > 0.0f ? transitionTime / nextStateAdjustedLength : 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
normalizedTime = 0.0f;
|
||||
}
|
||||
stateHash = nextState.fullPathHash;
|
||||
// Animation state synchronization
|
||||
{
|
||||
// Reset the dirty count before synchronizing the newly connected client with all layers
|
||||
m_AnimationMessage.IsDirtyCount = 0;
|
||||
|
||||
// Use the destination state to transition info lookup table to see if this is a transition we can
|
||||
// synchronize using cross fading
|
||||
if (m_DestinationStateToTransitioninfo.ContainsKey(layer))
|
||||
for (int layer = 0; layer < m_Animator.layerCount; layer++)
|
||||
{
|
||||
var synchronizationStateInfo = m_Animator.GetCurrentAnimatorStateInfo(layer);
|
||||
if (SynchronizationStateInfo != null)
|
||||
{
|
||||
if (m_DestinationStateToTransitioninfo[layer].ContainsKey(nextState.shortNameHash))
|
||||
SynchronizationStateInfo.Add(synchronizationStateInfo);
|
||||
}
|
||||
var stateHash = synchronizationStateInfo.fullPathHash;
|
||||
var normalizedTime = synchronizationStateInfo.normalizedTime;
|
||||
var isInTransition = m_Animator.IsInTransition(layer);
|
||||
|
||||
// Grab one of the available AnimationState entries so we can fill it with the current
|
||||
// layer's animation state.
|
||||
var animationState = m_AnimationMessage.AnimationStates[layer];
|
||||
|
||||
// Synchronizing transitions with trigger conditions for late joining clients is now
|
||||
// handled by cross fading between the late joining client's current layer's AnimationState
|
||||
// and the transition's destination AnimationState.
|
||||
if (isInTransition)
|
||||
{
|
||||
var tt = m_Animator.GetAnimatorTransitionInfo(layer);
|
||||
var nextState = m_Animator.GetNextAnimatorStateInfo(layer);
|
||||
|
||||
if (nextState.length > 0)
|
||||
{
|
||||
var destinationInfo = m_DestinationStateToTransitioninfo[layer][nextState.shortNameHash];
|
||||
stateHash = destinationInfo.OriginatingState;
|
||||
// Set the destination state to cross fade to from the originating state
|
||||
animationState.DestinationStateHash = destinationInfo.DestinationState;
|
||||
var nextStateTotalSpeed = nextState.speed * nextState.speedMultiplier;
|
||||
var nextStateAdjustedLength = nextState.length * nextStateTotalSpeed;
|
||||
// TODO: We need to get the transition curve for the target state as well as some
|
||||
// reasonable RTT estimate in order to get a more precise normalized synchronization time
|
||||
var transitionTime = Mathf.Min(tt.duration, tt.duration * tt.normalizedTime) * 0.5f;
|
||||
normalizedTime = Mathf.Min(1.0f, transitionTime > 0.0f ? transitionTime / nextStateAdjustedLength : 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
normalizedTime = 0.0f;
|
||||
}
|
||||
stateHash = nextState.fullPathHash;
|
||||
|
||||
// Use the destination state to transition info lookup table to see if this is a transition we can
|
||||
// synchronize using cross fading
|
||||
if (m_DestinationStateToTransitioninfo.ContainsKey(layer))
|
||||
{
|
||||
if (m_DestinationStateToTransitioninfo[layer].ContainsKey(nextState.shortNameHash))
|
||||
{
|
||||
var destinationInfo = m_DestinationStateToTransitioninfo[layer][nextState.shortNameHash];
|
||||
stateHash = destinationInfo.OriginatingState;
|
||||
// Set the destination state to cross fade to from the originating state
|
||||
animationState.DestinationStateHash = destinationInfo.DestinationState;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
animationState.Transition = isInTransition; // The only time this could be set to true
|
||||
animationState.StateHash = stateHash; // When a transition, this is the originating/starting state
|
||||
animationState.NormalizedTime = normalizedTime;
|
||||
animationState.Layer = layer;
|
||||
animationState.Weight = m_LayerWeights[layer];
|
||||
|
||||
// Apply the changes
|
||||
m_AnimationMessage.AnimationStates[layer] = animationState;
|
||||
}
|
||||
|
||||
animationState.Transition = isInTransition; // The only time this could be set to true
|
||||
animationState.StateHash = stateHash; // When a transition, this is the originating/starting state
|
||||
animationState.NormalizedTime = normalizedTime;
|
||||
animationState.Layer = layer;
|
||||
animationState.Weight = m_LayerWeights[layer];
|
||||
|
||||
// Apply the changes
|
||||
m_AnimationMessage.AnimationStates[layer] = animationState;
|
||||
// Send all animation states
|
||||
m_AnimationMessage.IsDirtyCount = m_Animator.layerCount;
|
||||
m_AnimationMessage.NetworkSerialize(serializer);
|
||||
}
|
||||
// Send all animation states
|
||||
m_AnimationMessage.IsDirtyCount = m_Animator.layerCount;
|
||||
SendAnimStateClientRpc(m_AnimationMessage, m_ClientRpcParams);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Required for the server to synchronize newly joining players
|
||||
/// Used to synchronize newly joined clients
|
||||
/// </summary>
|
||||
private void OnClientConnectedCallback(ulong playerId)
|
||||
protected override void OnSynchronize<T>(ref BufferSerializer<T> serializer)
|
||||
{
|
||||
m_NetworkAnimatorStateChangeHandler.SynchronizeClient(playerId);
|
||||
if (serializer.IsWriter)
|
||||
{
|
||||
WriteSynchronizationData(ref serializer);
|
||||
}
|
||||
else
|
||||
{
|
||||
var parameters = new ParametersUpdateMessage();
|
||||
var animationStates = new AnimationMessage();
|
||||
serializer.SerializeValue(ref parameters);
|
||||
UpdateParameters(ref parameters);
|
||||
serializer.SerializeValue(ref animationStates);
|
||||
HandleAnimStateUpdate(ref animationStates);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks for changes in both Animator parameters and state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is only invoked by clients that are the owner when not in server authoritative mode
|
||||
/// or by the server itself when in server authoritative mode.
|
||||
/// </remarks>
|
||||
internal void CheckForAnimatorChanges()
|
||||
{
|
||||
if (!IsOwner && !IsServerAuthoritative() || IsServerAuthoritative() && !IsServer)
|
||||
if (!IsSpawned || (!IsOwner && !IsServerAuthoritative()) || (IsServerAuthoritative() && !IsServer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -817,6 +846,11 @@ namespace Unity.Netcode.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just notify all remote clients and not the local server
|
||||
m_ClientSendList.Clear();
|
||||
m_ClientSendList.AddRange(NetworkManager.ConnectedClientsIds);
|
||||
m_ClientSendList.Remove(NetworkManager.LocalClientId);
|
||||
m_ClientRpcParams.Send.TargetClientIds = m_ClientSendList;
|
||||
SendAnimStateClientRpc(m_AnimationMessage);
|
||||
}
|
||||
}
|
||||
@@ -824,10 +858,7 @@ namespace Unity.Netcode.Components
|
||||
|
||||
private void SendParametersUpdate(ClientRpcParams clientRpcParams = default, bool sendDirect = false)
|
||||
{
|
||||
m_ParameterWriter.Seek(0);
|
||||
m_ParameterWriter.Truncate();
|
||||
|
||||
WriteParameters(m_ParameterWriter, sendDirect);
|
||||
WriteParameters(ref m_ParameterWriter);
|
||||
|
||||
var parametersMessage = new ParametersUpdateMessage
|
||||
{
|
||||
@@ -961,8 +992,10 @@ namespace Unity.Netcode.Components
|
||||
/// This uses the m_ParametersToUpdate list to write out only
|
||||
/// the parameters that have changed
|
||||
/// </summary>
|
||||
private unsafe void WriteParameters(FastBufferWriter writer, bool sendCacheState)
|
||||
private unsafe void WriteParameters(ref FastBufferWriter writer)
|
||||
{
|
||||
writer.Seek(0);
|
||||
writer.Truncate();
|
||||
// Write how many parameter entries we are going to write
|
||||
BytePacker.WriteValuePacked(writer, (uint)m_ParametersToUpdate.Count);
|
||||
foreach (var parameterIndex in m_ParametersToUpdate)
|
||||
@@ -1049,7 +1082,7 @@ namespace Unity.Netcode.Components
|
||||
/// <summary>
|
||||
/// Applies the ParametersUpdateMessage state to the Animator
|
||||
/// </summary>
|
||||
internal unsafe void UpdateParameters(ParametersUpdateMessage parametersUpdate)
|
||||
internal unsafe void UpdateParameters(ref ParametersUpdateMessage parametersUpdate)
|
||||
{
|
||||
if (parametersUpdate.Parameters != null && parametersUpdate.Parameters.Length != 0)
|
||||
{
|
||||
@@ -1067,6 +1100,16 @@ namespace Unity.Netcode.Components
|
||||
/// </summary>
|
||||
internal void UpdateAnimationState(AnimationState animationState)
|
||||
{
|
||||
// Handle updating layer weights first.
|
||||
if (animationState.Layer < m_LayerWeights.Length)
|
||||
{
|
||||
if (m_LayerWeights[animationState.Layer] != animationState.Weight)
|
||||
{
|
||||
m_Animator.SetLayerWeight(animationState.Layer, animationState.Weight);
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no state transition then return
|
||||
if (animationState.StateHash == 0)
|
||||
{
|
||||
return;
|
||||
@@ -1114,7 +1157,6 @@ namespace Unity.Netcode.Components
|
||||
m_Animator.Play(animationState.StateHash, animationState.Layer, animationState.NormalizedTime);
|
||||
}
|
||||
}
|
||||
m_Animator.SetLayerWeight(animationState.Layer, animationState.Weight);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1134,7 +1176,7 @@ namespace Unity.Netcode.Components
|
||||
{
|
||||
return;
|
||||
}
|
||||
UpdateParameters(parametersUpdate);
|
||||
UpdateParameters(ref parametersUpdate);
|
||||
if (NetworkManager.ConnectedClientsIds.Count > (IsHost ? 2 : 1))
|
||||
{
|
||||
m_ClientSendList.Clear();
|
||||
@@ -1183,7 +1225,6 @@ namespace Unity.Netcode.Components
|
||||
UpdateAnimationState(animationState);
|
||||
}
|
||||
|
||||
m_NetworkAnimatorStateChangeHandler.AddAnimationMessageToProcessQueue(animationMessage);
|
||||
if (NetworkManager.ConnectedClientsIds.Count > (IsHost ? 2 : 1))
|
||||
{
|
||||
m_ClientSendList.Clear();
|
||||
@@ -1196,6 +1237,18 @@ namespace Unity.Netcode.Components
|
||||
}
|
||||
}
|
||||
|
||||
internal void HandleAnimStateUpdate(ref AnimationMessage animationMessage)
|
||||
{
|
||||
var isServerAuthoritative = IsServerAuthoritative();
|
||||
if (!isServerAuthoritative && !IsOwner || isServerAuthoritative)
|
||||
{
|
||||
foreach (var animationState in animationMessage.AnimationStates)
|
||||
{
|
||||
UpdateAnimationState(animationState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internally-called RPC client receiving function to update some animation state on a client
|
||||
/// </summary>
|
||||
@@ -1211,15 +1264,7 @@ namespace Unity.Netcode.Components
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var isServerAuthoritative = IsServerAuthoritative();
|
||||
if (!isServerAuthoritative && !IsOwner || isServerAuthoritative)
|
||||
{
|
||||
foreach (var animationState in animationMessage.AnimationStates)
|
||||
{
|
||||
UpdateAnimationState(animationState);
|
||||
}
|
||||
}
|
||||
HandleAnimStateUpdate(ref animationMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1319,7 +1364,7 @@ namespace Unity.Netcode.Components
|
||||
m_NetworkAnimatorStateChangeHandler.QueueTriggerUpdateToClient(animTriggerMessage);
|
||||
if (!IsHost)
|
||||
{
|
||||
InternalSetTrigger(hash);
|
||||
InternalSetTrigger(hash, setTrigger);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1328,7 +1373,7 @@ namespace Unity.Netcode.Components
|
||||
m_NetworkAnimatorStateChangeHandler.QueueTriggerUpdateToServer(animTriggerMessage);
|
||||
if (!IsServerAuthoritative())
|
||||
{
|
||||
InternalSetTrigger(hash);
|
||||
InternalSetTrigger(hash, setTrigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1092,10 +1092,9 @@ namespace Unity.Netcode.Components
|
||||
/// <param name="posIn"></param> new position to move to. Can be null
|
||||
/// <param name="rotIn"></param> new rotation to rotate to. Can be null
|
||||
/// <param name="scaleIn">new scale to scale to. Can be null</param>
|
||||
/// <param name="shouldGhostsInterpolate">Should other clients interpolate this change or not. True by default</param>
|
||||
/// new scale to scale to. Can be null
|
||||
/// <param name="teleportDisabled">When true (the default) the <see cref="NetworkObject"/> will not be teleported and, if enabled, will interpolate. When false the <see cref="NetworkObject"/> will teleport/apply the parameters provided immediately.</param>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public void SetState(Vector3? posIn = null, Quaternion? rotIn = null, Vector3? scaleIn = null, bool shouldGhostsInterpolate = true)
|
||||
public void SetState(Vector3? posIn = null, Quaternion? rotIn = null, Vector3? scaleIn = null, bool teleportDisabled = true)
|
||||
{
|
||||
if (!IsSpawned)
|
||||
{
|
||||
@@ -1119,16 +1118,16 @@ namespace Unity.Netcode.Components
|
||||
{
|
||||
m_ClientIds[0] = OwnerClientId;
|
||||
m_ClientRpcParams.Send.TargetClientIds = m_ClientIds;
|
||||
SetStateClientRpc(pos, rot, scale, !shouldGhostsInterpolate, m_ClientRpcParams);
|
||||
SetStateClientRpc(pos, rot, scale, !teleportDisabled, m_ClientRpcParams);
|
||||
}
|
||||
else // Preserving the ability for server authoritative mode to accept state changes from owner
|
||||
{
|
||||
SetStateServerRpc(pos, rot, scale, !shouldGhostsInterpolate);
|
||||
SetStateServerRpc(pos, rot, scale, !teleportDisabled);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
SetStateInternal(pos, rot, scale, !shouldGhostsInterpolate);
|
||||
SetStateInternal(pos, rot, scale, !teleportDisabled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user