diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 7105c92dc8..e1d0a53d3b 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -13,10 +13,12 @@ Additional documentation and release notes are available at [Multiplayer Documen - It is now possible to control which port clients will bind to using the `UnityTransport.ConnectionData.ClientBindPort` field. If not set, clients will bind to an ephemeral port (same as before this change). (#3764) - Added a flag to override command-line arguments (port and ip) in `SetConnectionData`. (#3760) - Added a command-line singleton to parse environment command-line arguments. (#3760) - +- Added `NetworkAnimator.AuthorityMode` which allows you to select whether the `NetworkAnimator` will use a server or owner authority model for state updates (like `NetworkTransform`). (#3586) +- Added the ability to select which `Animator` parameters the authority `NetworkAnimator` instance should synchronize. This can be done via the inspector view interface or during runtime via `NetworkAnimator.EnableParameterSynchronization`. (#3586) ### Changed +- Changed NetworkAnimator to use the `RpcAttribute` along with the appropriate `SendTo` parameter. (#3586) - Improve performance of `NetworkTransformState`. (#3770) @@ -32,6 +34,7 @@ Additional documentation and release notes are available at [Multiplayer Documen - Fixed issue where invoking an RPC, on another `NetworkBehaviour` associated with the same `NetworkObject` that is ordered before the `NetworkBehaviour` invoking the RPC, during `OnNetworkSpawn` could throw an exception if scene management is disabled. (#3782) - Fixed issue where the `Axis to Synchronize` toggles didn't work with multi object editing in `NetworkTransform`. (#3781) - Fixed issue where using the dedicated server package would override all attempts to change the port by code. (#3760) +- Fixed issue with authority animator instance sending itself RPCs. (#3586) ### Security diff --git a/com.unity.netcode.gameobjects/Documentation~/components/helper/networkanimator.md b/com.unity.netcode.gameobjects/Documentation~/components/helper/networkanimator.md index 5f1bd1371b..6954b19bb5 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/helper/networkanimator.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/helper/networkanimator.md @@ -1,55 +1,53 @@ # NetworkAnimator -The NetworkAnimator component provides you with a fundamental example of how to synchronize animations during a network session. Animation states are synchronized with players joining an existing network session and any client already connected before the animation state changing. +Use the NetworkAnimator component to synchronize animations during a network session. -* Players joining an existing network session will be synchronized with: - * All the `Animator`'s current properties and states. - * *With exception to `Animator` trigger properties. These are only synchronized with already connected clients.* - * Any in progress transition -* Players already connected will be synchronized with changes to `Animator`: - * States - * Transitions - * Properties - * NetworkAnimator will only synchronize properties that have changed since the earlier frame property values. - * Since triggers are similar to an "event," when an `Animator` property is set to `true` it will always be synchronized. +When using the [NetworkAnimator](xref:Unity.Netcode.Components.NetworkAnimator) component, [Animator](https://docs.unity3d.com/Documentation/ScriptReference/Animator.html) animation states are synchronized with players joining an existing network session and any client already connected when the animation state changes. You can also use the NetworkAnimator component as an example of how to create your own custom animation synchronization system. -NetworkAnimator can operate in two authoritative modes: +## NetworkAnimator overview -* Server Authoritative (default): Server initiates animation state changes. - * Owner's can still invoke `NetworkAnimator.SetTrigger`. -* Client Authoritative: Client owners start animation state changes. + Players joining an existing network session are synchronized with: +* All the Animator's current parameters and states, with the following exceptions: + * Animator trigger parameters. These are only synchronized with already connected clients. Late joining clients are synchronized with the Animator's current state. + * Any Animator parameter specifically excluded from being synchronized. +* Any in progress transition. -> [!NOTE] -> You need to use `Unity.Netcode.Components` to reference components such as NetworkAnimator. - -## Animator Trigger Property +Players already connected are synchronized with changes to Animator: -The `Animator` trigger property type ("trigger") is basically nothing more than a Boolean value that, when set to `true`, will get automatically reset back to `false` after the `Animator` component has processed the trigger. Usually, a trigger is used to start a transition between `Animator` layer states. In this sense, one can think of a trigger as a way to signal the "beginning of an event." Because trigger properties have this unique behavior, they **require** that you to set the trigger value via the `NetworkAnimator.SetTrigger` method. +* States +* Transitions +* Parameters + * NetworkAnimator only synchronizes parameters that have changed since the previous frame's parameter values. + * Because triggers are similar to an event, when an Animator parameter is set to `true` it will always be synchronized. > [!NOTE] -> If you set a trigger property using `Animator.SetTrigger` then this won't be synchronized with non-owner clients. +> You need to include `Unity.Netcode.Components` as a using directive to reference components such as NetworkAnimator. -## Server Authoritative Mode +## Authority modes -The default setting for NetworkAnimator is server authoritative mode. When operating in server authoritative mode, any animation state changes that are set (triggers) or detected (change in layer, state, or any `Animator` properties excluding triggers) on the server side will be synchronized with all clients. Because the server initiates any synchronization of changes to an `Animator` 's state, a client that's the owner of the NetworkObject associated with the NetworkAnimator can lag by roughly the full round trip time (RTT). Below is a timing diagram to show this: +NetworkAnimator can operate in two authority modes: -![ServerAuthMode](../../images/NetworkAnimatorServerAuthTiming.png) +* [Server authoritative (default)](#server-authoritative-mode): The server dictates changes to Animator state(s) and parameters. + * Owners can still invoke `NetworkAnimator.SetTrigger`. +* [Owner authoritative](#owner-authoritative-mode): The owner of the spawned NetworkObject dictates changes to Animator state(s) and parameters. -In the above diagram, a client might be sending the server an RPC to tell the server that the player is performing some kind of action that can change the player's animations (including setting a trigger). Under this scenario, the client sends an RPC to the server (half RTT), the server processes the RPC, the associated `Animator` state changes are detected by the NetworkAnimator (server-side), and then all clients (including the owner client) are synchronized with the changed. +### Server authoritative mode -**Server authoritative model benefits:** +The default setting for NetworkAnimator is server authoritative mode. When operating in server authoritative mode, any animation state changes that are set (triggers) or detected (changes in layer, state, or any Animator parameters excluding triggers) on the server side will be synchronized with all clients. Because the server initiates any synchronization of changes to an Animator's state, the owner of the NetworkObject associated with the NetworkAnimator can lag by roughly the full round-trip time (RTT). The following timing diagram illustrates this: -* If running a plain server (non-host), this model helps reduce the synchronization latency between all client animations. +![ServerAuthMode](../../images/NetworkAnimatorServerAuthTiming.png) -**Server authoritative model drawbacks:** +A client might be sending the server an RPC to tell the server that the player is performing an action that will change the player's animations (including setting a trigger). In this scenario, the client sends an RPC to the server (half RTT), the server processes the RPC, the associated Animator state changes are detected by the NetworkAnimator (server-side), and then all clients (including the owning client) are synchronized with the changes. -* Hosts will always be "slightly ahead" of all other clients which may or may not be an issue for your project. -* Client owners will experience a latency between performing an action (moving, picking something up, anything that causes an `Animator` state change). +| Server authoritative mode benefits | Server authoritative mode drawbacks | +|------------|--------------------------------| +| If you're running a non-host server, this model reduces the synchronization latency between all client animations. | Hosts will always be slightly ahead of all other clients, which may or may not be an issue depending on the needs of your project. | +| | Client owners will experience a latency between performing an action (moving, picking something up, anything that causes an Animator state change) and seeing the corresponding animation update. | -## Owner Authoritative Mode +### Owner authoritative mode -Usually, your project's design (or personal preference) might require that owners are immediately updated to any `Animator` state changes. The most typical reason would be to give the local player with instantaneous visual (animation) feedback. To create an owner authoritative NetworkAnimator you need to create a new class that's derived from NetworkAnimator, override the `NetworkAnimator.OnIsServerAuthoritative` method, and within the overridden `OnIsServerAuthoritative` method you should return false like in the example provided below: +Your project's design (or personal preference) might require that owners are immediately updated when any Animator state changes to provide the local player with instantaneous visual (animation) feedback. To create an owner authoritative NetworkAnimator, you need to create a new class that's derived from NetworkAnimator, override the `NetworkAnimator.OnIsServerAuthoritative` method, and return `false` within the overridden `OnIsServerAuthoritative` method as in the following example: ```csharp public class OwnerNetworkAnimator : NetworkAnimator @@ -61,66 +59,360 @@ Usually, your project's design (or personal preference) might require that owner } ``` -Looking at the timing for an owner authoritative NetworkAnimator, in the diagram below, you can see that while the owner client gets "immediate visual animation response" the non-owner clients end up being roughly one full RTT behind the owner client and a host would be half RTT behind the owner client. +The following diagram illustrates the timing for an owner authoritative NetworkAnimator. While the owner client gets an immediate visual update, non-owner clients end up being roughly one full RTT behind the owner client and a host would be half RTT behind the owner client. ![ServerAuthMode](../../images/NetworkAnimatorOwnerAuthTiming.png) -In the above diagram, it shows that the owner client has an `Animator` state change that's detected by the NetworkAnimator ( `OwnerNetworkAnimator`) which automatically synchronizes the server with the changed state. The server applies the change(s) locally and then broadcasts this state change to all non-owner clients. +The owner client has an Animator state change that's detected by the NetworkAnimator (`OwnerNetworkAnimator`) which automatically synchronizes the server with the changed state. The server applies the change(s) locally and then broadcasts this state change to all non-owner clients. -**Owner authoritative model benefits:** +| Owner authoritative mode benefits | Owner authoritative mode drawbacks | +|------------|--------------------------------| +| The owner gets instant visual feedback of Animator state changes, which provides a smoother experience for the local player. | Non-owner clients lag behind the owner client's animation by roughly one full RTT. | +| | The host lags behind the owner client's animation by roughly half RTT. | -* The owner is provided instant visual feedback of `Animator` state changes, which does offer a smoother experience for the local player. +> [!NOTE] +> The same rule for setting trigger parameters applies to owner clients. As such, if you want to programmatically set a trigger then you still need to use `NetworkAnimator.SetTrigger`. -**Owner authoritative model drawbacks:** +## Using NetworkAnimator -* Non-owner clients lag behind the owner client's animation by roughly one full RTT. -* A host lags behind the owner client's animation by roughly half RTT. +Some aspects of how you use NetworkAnimator depends on which authority model you're using: server authoritative or owner authoritative. > [!NOTE] -> The same rule for setting trigger properties still applies to owner clients. As such, if you want to programmatically set a trigger then you still need to use `NetworkAnimator.SetTrigger`. +> NetworkAnimator is one of several possible ways to synchronize animations during a network session. Netcode for GameObjects provides you with the building blocks (RPCs, NetworkVariables, and custom messages) required to create a custom animation synchronization system to suit your needs. NetworkAnimator is an introductory approach provided for users already familiar with the Animator component and might be all that you need, depending on your project's design requirements. -## Using NetworkAnimator +### Changing meshes -Using NetworkAnimator is a pretty straight forward approach with the only subtle difference being whether you are using a server or owner authoritative model. +When swapping a skinned mesh with another re-parented skinned mesh, invoke the `Rebind` method on the Animator component (`Animator.Rebind()`). -> [!NOTE] -> NetworkAnimator is one of several possible ways to synchronize animations during a network session. Netcode for GameObjects provides you with the building blocks (RPCs, NetworkVariables, and Custom Messages) needed to create a completely unique animation synchronization system that has a completely different and potentially more optimized approach. NetworkAnimator is a straight forward approach provided for users already familiar with the `Animator` component and, depending upon your project's design requirements, might be all that you need. +### Assigning the Animator -### Server Authoritative +![Usage-1](../../images/networkanimator/usingnetworkanimator.png) -If you decide you want to use the server authoritative model, then you can add a NetworkAnimator component to either the same GameObject that has the NetworkObject component attached to it or any child GameObject. In the below screenshot, you can see a network Prefab that houses two authoritative models. The `NetworkAnimatorCube-Server` GameObject has an `Animator` component, an `AnimatedCubeController` component (used for manual testing), and the NetworkAnimator component that has a reference to the `Animator` component. +When adding a NetworkAnimator component to a network prefab, you need to drag the Animator component onto the **Animator** field in the **Inspector** window. The Animator component can be on the root GameObject of the network prefab or a child under the root GameObject. -![Usage-1](../../images/NetworkAnimatorUsage-1.png) +### Selecting the authority mode -### Owner Authoritative +The NetworkAnimator [authority mode](#authority-modes) determines which instance of a spawned network prefab pushes updates to the Animator's state. You can select which authority mode to use from the **Authority Mode** drop-down menu in the **Inspector** window: -If you decide you want to use the owner authoritative model, then (for example purposes) you would use your derived `OwnerNetworkAnimator` component as opposed to the default NetworkAnimator component like in the screenshot below: +![Authority Mode drop-down menu](../../images/networkanimator/animatorauthority.png) -![Usage-1](../../images/NetworkAnimatorUsage-2.png) +Alternatively, you can change the authority mode by deriving from the `NetworkAnimator` class and overriding the `NetworkAnimator.OnIsServerAuthoritative` method, where returning `true` indicates server authoritative mode and returning `false` indicates owner authoritative mode. > [!NOTE] -> While it isn't advised to have different NetworkAnimator authoritative models "under the same root network Prefab GameObject, " you can have multiple children that each have their own `Animator` and NetworkAnimator all housed under a single NetworkObject and all use the same authoritative model. However, you should always consider the balance between performance (CPU or bandwidth consumption) and convenience/modularity. +> Using `NetworkAnimator.OnIsServerAuthoritative` overrides the NetworkAnimator **Authority Mode** setting specified via the **Inspector** window. + +### Changing Animator parameters + +You can set all Animator parameters (except for triggers) directly via the Animator class. For example, if you have a jumping animation and need to handle transitioning out of the part of the sequence where the player is falling from the jump (or falling when walking off of the edge of a platform), then you might have a `bool` parameter called `Grounded` that must be set when the player isn't grounded. One way to do this would be to set the value on the authoritative instance (server or owner) as follows: -### Changing Animator Properties +```csharp +// m_Animator is a reference to the Animator component +m_Animator.SetFloat("Grounded", false); +``` -For all `Animator` properties (except for triggers), you can set them directly via the `Animator` class. As an example, you might use the player's normalized velocity as a way to control the walking or running animation of a player. You might have an `Animator` `float` property called "AppliedMotion" that you would set on the authoritative instance (server or owner) like such: +This example works, but ideally you want to pre-calculate the hash value of the parameter's name and use that pre-calculated value to apply updates to parameters. The following provides an example of how you can accomplish this: ```csharp -public void ApplyMotion(Vector3 playerVelocity) +private int m_GroundedParameterId; +private bool m_WasGrounded; +private Animator m_Animator; +private CharacterController m_CharacterController; + +protected override void OnNetworkPreSpawn(ref NetworkManager networkManager) { - m_Animator.SetFloat("AppliedMotion", playerVelocity.normalized.magnitude); + // Pre-calculate the hash for quick lookup. + m_GroundedParameterId = Animator.StringToHash("Grounded"); + // Get the CharacterController. + m_CharacterController = GetComponent(); +} + +private void CheckForFalling() +{ + // If the last status of being grounded is not the current. + if (m_CharacterController.isGrounded != m_WasGrounded) + { + // Set the Grounded parameter to match the change in the grounded state. + m_Animator.SetBool(m_GroundedParameterId, m_CharacterController.isGrounded); + // Update to be able to detect when it changes back. + m_WasGrounded = m_CharacterController.isGrounded; + } } ``` -For triggers you always want to use NetworkAnimator. One example might be that you use a trigger, called it " `IsJumping`, " to start a blended transition between the player's walking/running animation and the jumping animation: +## Animator trigger parameter + +The Animator trigger parameter type is a Boolean value that, when set to `true`, is automatically reset back to `false` after the Animator component has processed the trigger. Usually, a trigger is used to start a transition between Animator layer states and as a way to signal the beginning of an event. Because trigger parameters have this unique behavior, they require you to set the trigger value via the `NetworkAnimator.SetTrigger` method. + +> [!NOTE] +> If you set a trigger parameter using `Animator.SetTrigger` then this trigger sequence won't be properly synchronized with non-authority instances. You must use `NetworkAnimator.SetTrigger` to ensure proper synchronization. + +For example, you might have a trigger parameter called `IsJumping` to start a blended transition between the player's walking animation and the jumping animation. The following script adds `m_NetworkAnimator`, which is assigned during `OnNetworkPreSpawn` (unless you need to access it in `Start`, it's recommended to handle getting components within `OnNetworkPreSpawn` because it's invoked prior to `Start` when first spawning an instance). ```csharp +private int m_GroundedParameterId; +private int m_JumpingParameterId; +private bool m_WasGrounded; +private Animator m_Animator; +private NetworkAnimator m_NetworkAnimator; +private CharacterController m_CharacterController; + +protected override void OnNetworkPreSpawn(ref NetworkManager networkManager) +{ + // Pre-calculate the hash values for performance purposes. + m_GroundedParameterId = Animator.StringToHash("Grounded"); + m_JumpingParameterId = Animator.StringToHash("IsJumping"); + + // Get the CharacterController. + m_CharacterController = GetComponent(); + // Get the NetworkAnimator component. + m_NetworkAnimator = GetComponent(); +} + +private void CheckForFalling() +{ + // If the last status of being grounded is not the current. + if (m_CharacterController.isGrounded != m_WasGrounded) + { + // Set the Grounded parameter to match the change in the grounded state. + m_Animator.SetBool(m_GroundedParameterId, m_CharacterController.isGrounded); + // Update to be able to detect when it changes back. + m_WasGrounded = m_CharacterController.isGrounded; + } +} + public void SetPlayerJumping(bool isJumping) { - m_NetworkAnimator.SetTrigger("IsJumping"); + // You only need to pass in the parameters hash/id to set the trigger + m_NetworkAnimator.SetTrigger(m_JumpingParameterId); } ``` -> [!NOTE] -> Changing meshes
-> When swapping a skinned mesh with another reparented skinned mesh, you can invoke the `Rebind ` method on the `Animator` components: `Animator.Rebind()`. +## Exclude parameters from synchronization + +By default, all parameters within the Animator are marked for synchronization by the NetworkAnimator component. You can exclude parameters from synchronization to help reduce network traffic and improve performance using the **Animator Parameter Entries** field in the **Inspector** window. + +![Animator Parameter Entries field](../../images/networkanimator/parametersynchronization.png) + +For example, if you have a `float` parameter called `Speed` that's updated every frame based on the player's linear velocity, then keeping it synchronized would generate at least one RPC per frame for each spawned authority instance of the network prefab. In a project with many spawned instances, this can lead to excessive network traffic and performance issues, even with Netcode for GameObjects' message batching. + +### Unsynchronized parameters + +You can update unsynchronized parameters in two ways, depending on your project's needs: + +* Synchronize the values using your own custom solution, such as sending the values at a specific interval via RPC or using a [NetworkVariable](../../basics/networkvariable.md) that synchronizes the delt on each network tick. + * This approach still contributes to bandwidth usage and processing time, but can provide you with the ability to lerp between the previous and current value over (n) period of time. +* Update the parameters locally based on values that you already have access to. + * This approach doesn't use any bandwidth, but requires some additional scripts. + +#### Updating locally + +The following example shows how to locally update the `Speed` parameter based on changes to the player's position over time. The scenario: + +* You're using a modified version of the `ThirdPersonController`, or a similar approach with two parameters that determine how quickly a player might play a walking or running animation: + * `Speed` parameter: Updated each frame based on the player's input. + * `MotionSpeed`: Determines the magnitude of the player's input. + * Most of the time this ends up being either 1.0 or 0.0 with an analog device, so this example assumes you're not using an analog device to control the amount of speed to apply over time. +* You're using a [NetworkTransform](networktransform.md) to synchronize changes in position and rotation, or you have written your own custom [NetworkBehaviour](../core/networkbehaviour.md) that accomplishes the same thing. +* You have [interpolation](../../learn/clientside-interpolation.md) enabled, or your custom solution uses some form of interpolation where a single state update is applied over (n) period of time (typically a network tick). + +Each non-authority instance receives delta transform state updates from the authority instance, so knows when a player is moving. If the player is moving, then animations should be being played based on the speed (linear velocity) of the authority player instance. So it's possible, on the non-authority instance, to calculate some values based on changes to the transform's position on a frame by frame basis. To begin with, mark the two parameters as unsynchronized within the NetworkAnimator's **Animator Parameter Entries** list (for this example): + +![Usage-1](../../images/networkanimator/parametersnosynch.png) + +This means that the authority will no longer send updates to synchronize these values. + +Then you need to implement a script that calculates values on non-authority instances based on the non-authority instances movement over time. The following is an example psuedo-script to accomplish this: + +```csharp +[Range(0.0001f, 1.0f)] +public float m_NonAuthorityMotionThreshold = 0.01f; +private Vector3 m_LastPosition; +private float m_UnitsPerSecond; +private bool m_WasMoving; + +// Can be used to toggle parameter synchronization during runtime +private NetworkVariable m_SynchronizeSpeedParameter = new NetworkVariable(false); + +protected override void OnNetworkPostSpawn() +{ + _controller.enabled = IsLocalPlayer; + _hasAnimator = TryGetComponent(out _animator); + if (IsLocalPlayer) + { + // Register the authority for both the Update and PostLateUpdate player loop stages + // Update used to handle input and apply motion. + NetworkUpdateLoop.RegisterNetworkUpdate(this, NetworkUpdateStage.Update); + + // PostLateUpdate handles camera rotation adjustments + NetworkUpdateLoop.RegisterNetworkUpdate(this, NetworkUpdateStage.PostLateUpdate); + _input.enabled = true; + _playerInput.enabled = true; + m_SynchronizeSpeedParameter.Value = false; + m_NetworkAnimator.EnableParameterSynchronization("Speed", m_SynchronizeSpeedParameter.Value); + m_NetworkAnimator.EnableParameterSynchronization("MotionSpeed", m_SynchronizeSpeedParameter.Value); + } + else + { + // When a non-authority instance is spawned, it initializes the last known position + m_LastPosition = transform.position; + + // Non-authority instances register for the pre-late update to assure any adjustments to + // position have been applied before calculating the animation speed. + NetworkUpdateLoop.RegisterNetworkUpdate(this, NetworkUpdateStage.PreLateUpdate); + + } + base.OnNetworkPostSpawn(); +} + +public override void OnNetworkPreDespawn() +{ + _controller.enabled = false; + // Before de-spawning, unregister from all updates for this instance + NetworkUpdateLoop.UnregisterAllNetworkUpdates(this); + base.OnNetworkPreDespawn(); +} + +// This class implements INetworkUpdateSystem +public void NetworkUpdate(NetworkUpdateStage updateStage) +{ + if (!IsSpawned) + { + return; + } + + switch (updateStage) + { + + case NetworkUpdateStage.Update: + { + // Authority only + AuthorityUpdate(); + break; + } + case NetworkUpdateStage.PreLateUpdate: + { + if (!m_SynchronizeSpeedParameter.Value) + { + // Non-authority only + NonAuthorityUpdate(); + } + else if (m_WasMoving) + { + // If synchronizing speed and we were moving, then + // reset the fields used to calculate speed + m_WasMoving = false; + m_UnitsPerSecond = 0.0f; + _animationBlend = 0.0f; + _speed = 0.0f; + } + break; + } + case NetworkUpdateStage.PostLateUpdate: + { + // Authority only + CameraRotation(); + break; + } + } +} + +private void NonAuthorityUpdate() +{ + // Get the delta from last frame + var deltaVector3 = transform.position - m_LastPosition; + // An approximated calculation of the potential unity world space units per second by getting the quotient of delta time divided into 1. We are only interested in x and z deltas, so use a Vector2, and then obtain the magnitude of the quotient times the Vector2. + var unitsPerSecond = (new Vector2(deltaVector3.x, deltaVector3.z) * (1.0f / Time.deltaTime)).magnitude; + + // Only trigger when the delta per frame exceeds the non-authority motion threshold + if (unitsPerSecond > m_NonAuthorityMotionThreshold) + { + // if the new delta is > or < the last value stored + if (unitsPerSecond != m_UnitsPerSecond) + { + // Lerp towards the new delta to mock the player input + m_UnitsPerSecond = Mathf.Lerp(m_UnitsPerSecond, unitsPerSecond, Time.deltaTime * SpeedChangeRate); + + // Clamp to the maximum world space units per second + m_UnitsPerSecond = Mathf.Clamp(m_UnitsPerSecond, 0.0f, SprintSpeed); + + // round speed to 3 decimal places like it does with player input + _speed = (float)System.Math.Round(m_UnitsPerSecond, 3); + + // Track that we are now moving + m_WasMoving = true; + } + else + { + // Maintain the current speed + _speed = m_UnitsPerSecond; + } + + // If we are half of the non-authority motion threshold then come to a stop + if (_speed < (m_NonAuthorityMotionThreshold * 0.5f)) + { + _speed = 0f; + m_UnitsPerSecond = 0f; + // Reset the magnitude to zero + _animator.SetFloat(_animIDMotionSpeed, 0.0f); + } + else + { + // Set maximum magnitude + _animator.SetFloat(_animIDMotionSpeed, 1.0f); + } + // Apply the calculated speed value + _animator.SetFloat(_animIDSpeed, _speed); + + } + else if (m_WasMoving) + { + // Reset everything until next motion + m_WasMoving = false; + _animator.SetFloat(_animIDSpeed, 0.0f); + _animator.SetFloat(_animIDMotionSpeed, 0.0f); + m_UnitsPerSecond = 0.0f; + _animationBlend = 0.0f; + _speed = 0.0f; + } + m_LastPosition = transform.position; +} +``` + +The pseudo-script works as follows: + +* It keeps track of the last known position. + * This is initialized on non-authority instances during post spawn. +* The delta between the last known position and current position is used to determine what the world units per second would be if the delta was maintained for one second. +* The script then ensures that the (world) units per second exceeds a specific threshold to avoid edge cases. +* If the units per second is larger than the threshold: + * Lerping from the last known units per second value towards the new/current units per second value. + * _This handles accelerating towards or away from the current value._ + * Clamp the calculated value to the maximum speed. + * Round the result and assign it to the `_speed` field (from `ThirdPersonController`). +* Check if the currently known speed is less than a predetermined minimum value. + * If so, then set the speed and motion speed to zero. +* Update the local Animator's `Speed` and `MotionSpeed` parameters. + +The end result is that (NetworkAnimator relative) the only time this particular set up would send RPCs would be if the player jumps or falls, since speed dictates the idle, walking, and running animations: + +![Usage-1](../../images/networkanimator/animator-idle-walk-run.png) + +### Update synchronization status during runtime + +You can update the synchronization status of parameters during runtime by invoking `NetworkAnimator.EnableParameterSynchronization`. Perhaps you have a lot of existing network prefab assets that might be too time intensive to adjust or you only want to adjust certain instances. + +The following is an example script that does this when the backslash key is pressed: + +```csharp +if (Input.GetKeyDown(KeyCode.Backslash)) +{ + m_SynchronizeSpeedParameter.Value = !m_SynchronizeSpeedParameter.Value; + m_NetworkAnimator.EnableParameterSynchronization(_animIDSpeed, m_SynchronizeSpeedParameter.Value); + m_NetworkAnimator.EnableParameterSynchronization(_animIDMotionSpeed, m_SynchronizeSpeedParameter.Value); +} +``` + +## Additional resources + +- [NetworkAnimator API documentation](xref:Unity.Netcode.Components.NetworkAnimator) +- [Animator component documentation](https://docs.unity3d.com/Documentation/ScriptReference/Animator.html) \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Documentation~/images/networkanimator/animator-idle-walk-run.png b/com.unity.netcode.gameobjects/Documentation~/images/networkanimator/animator-idle-walk-run.png new file mode 100644 index 0000000000..f063a158af Binary files /dev/null and b/com.unity.netcode.gameobjects/Documentation~/images/networkanimator/animator-idle-walk-run.png differ diff --git a/com.unity.netcode.gameobjects/Documentation~/images/networkanimator/animatorauthority.png b/com.unity.netcode.gameobjects/Documentation~/images/networkanimator/animatorauthority.png new file mode 100644 index 0000000000..c909b9ce3a Binary files /dev/null and b/com.unity.netcode.gameobjects/Documentation~/images/networkanimator/animatorauthority.png differ diff --git a/com.unity.netcode.gameobjects/Documentation~/images/networkanimator/parametersnosynch.png b/com.unity.netcode.gameobjects/Documentation~/images/networkanimator/parametersnosynch.png new file mode 100644 index 0000000000..d1891a2fdd Binary files /dev/null and b/com.unity.netcode.gameobjects/Documentation~/images/networkanimator/parametersnosynch.png differ diff --git a/com.unity.netcode.gameobjects/Documentation~/images/networkanimator/parametersynchronization.png b/com.unity.netcode.gameobjects/Documentation~/images/networkanimator/parametersynchronization.png new file mode 100644 index 0000000000..c305ddffdc Binary files /dev/null and b/com.unity.netcode.gameobjects/Documentation~/images/networkanimator/parametersynchronization.png differ diff --git a/com.unity.netcode.gameobjects/Documentation~/images/networkanimator/usingnetworkanimator.png b/com.unity.netcode.gameobjects/Documentation~/images/networkanimator/usingnetworkanimator.png new file mode 100644 index 0000000000..94ed20cb29 Binary files /dev/null and b/com.unity.netcode.gameobjects/Documentation~/images/networkanimator/usingnetworkanimator.png differ diff --git a/com.unity.netcode.gameobjects/Editor/NetworkAnimatorParameterEntryDrawer.cs b/com.unity.netcode.gameobjects/Editor/NetworkAnimatorParameterEntryDrawer.cs new file mode 100644 index 0000000000..9060b8993c --- /dev/null +++ b/com.unity.netcode.gameobjects/Editor/NetworkAnimatorParameterEntryDrawer.cs @@ -0,0 +1,96 @@ + +#if COM_UNITY_MODULES_ANIMATION +using Unity.Netcode.Components; +using UnityEditor; +using UnityEngine; + +namespace Unity.Netcode.Editor +{ + [CustomPropertyDrawer(typeof(NetworkAnimator.AnimatorParametersListContainer))] + internal class NetworkAnimatorParameterEntryDrawer : PropertyDrawer + { + // Draw the property inside the given rect + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + EditorGUI.BeginProperty(position, label, property); + + // Draw the foldout for the list + SerializedProperty items = property.FindPropertyRelative(nameof(NetworkAnimator.AnimatorParameterEntries.ParameterEntries)); + position.height = EditorGUIUtility.singleLineHeight; + property.isExpanded = EditorGUI.Foldout(position, property.isExpanded, label); + + if (property.isExpanded) + { + // Set the indention level down + EditorGUI.indentLevel++; + for (int i = 0; i < items.arraySize; i++) + { + position.y += EditorGUIUtility.singleLineHeight + 2; + SerializedProperty element = items.GetArrayElementAtIndex(i); + var nameField = element.FindPropertyRelative(nameof(NetworkAnimator.AnimatorParameterEntry.name)); + // Draw the foldout for the item + element.isExpanded = EditorGUI.Foldout(position, element.isExpanded, nameField.stringValue); + if (!element.isExpanded) + { + continue; + } + // Draw the contents of the item + position.y += EditorGUIUtility.singleLineHeight + 2; + // Set the indention level down + EditorGUI.indentLevel++; + // Calculate rects + var nameHashRect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight); + position.y += EditorGUIUtility.singleLineHeight + 2; + var paramRect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight); + position.y += EditorGUIUtility.singleLineHeight + 2; + var syncRect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight); + + // Get the three properties we want to visualize in the inspector view + var synchronizeField = element.FindPropertyRelative(nameof(NetworkAnimator.AnimatorParameterEntry.Synchronize)); + var nameHashField = element.FindPropertyRelative(nameof(NetworkAnimator.AnimatorParameterEntry.NameHash)); + var parameterTypeField = element.FindPropertyRelative(nameof(NetworkAnimator.AnimatorParameterEntry.ParameterType)); + + // Draw the read only fields + GUI.enabled = false; + EditorGUI.PropertyField(nameHashRect, nameHashField); + EditorGUI.PropertyField(paramRect, parameterTypeField); + GUI.enabled = true; + // Draw the read/write fields + EditorGUI.PropertyField(syncRect, synchronizeField); + // Set the indention level up + EditorGUI.indentLevel--; + } + // Set the indention level up + EditorGUI.indentLevel--; + } + EditorGUI.EndProperty(); + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + var totalHeight = EditorGUIUtility.singleLineHeight; + if (!property.isExpanded) + { + return totalHeight; + } + var singleLineWithSpace = EditorGUIUtility.singleLineHeight + 2; + SerializedProperty items = property.FindPropertyRelative(nameof(NetworkAnimator.AnimatorParameterEntries.ParameterEntries)); + + totalHeight += singleLineWithSpace; + for (int i = 0; i < items.arraySize; i++) + { + SerializedProperty element = items.GetArrayElementAtIndex(i); + if (element.isExpanded) + { + totalHeight += (singleLineWithSpace * 4); + } + else + { + totalHeight += EditorGUIUtility.singleLineHeight; + } + } + return totalHeight; + } + } +} +#endif diff --git a/com.unity.netcode.gameobjects/Editor/NetworkAnimatorParameterEntryDrawer.cs.meta b/com.unity.netcode.gameobjects/Editor/NetworkAnimatorParameterEntryDrawer.cs.meta new file mode 100644 index 0000000000..f3adbfd688 --- /dev/null +++ b/com.unity.netcode.gameobjects/Editor/NetworkAnimatorParameterEntryDrawer.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 3ab75768bda56e545a8ebd0324eecbaf \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs index e5f8d7f9f1..db2edff3df 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs @@ -1,6 +1,7 @@ #if COM_UNITY_MODULES_ANIMATION using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Netcode.Runtime; @@ -33,7 +34,7 @@ private void FlushMessages() } else { - m_NetworkAnimator.SendAnimStateClientRpc(animationUpdate.AnimationMessage, animationUpdate.ClientRpcParams); + m_NetworkAnimator.SendClientAnimStateRpc(animationUpdate.AnimationMessage, animationUpdate.RpcParams); } } @@ -47,7 +48,7 @@ private void FlushMessages() } else { - m_NetworkAnimator.SendParametersUpdateClientRpc(sendEntry.ParametersUpdateMessage, sendEntry.ClientRpcParams); + m_NetworkAnimator.SendClientParametersUpdateRpc(sendEntry.ParametersUpdateMessage, sendEntry.RpcParams); } } m_SendParameterUpdates.Clear(); @@ -62,17 +63,24 @@ private void FlushMessages() { if (!sendEntry.SendToServer) { - m_NetworkAnimator.SendAnimTriggerClientRpc(sendEntry.AnimationTriggerMessage, sendEntry.ClientRpcParams); + m_NetworkAnimator.SendClientAnimTriggerRpc(sendEntry.AnimationTriggerMessage, sendEntry.RpcParams); } else { - m_NetworkAnimator.SendAnimTriggerServerRpc(sendEntry.AnimationTriggerMessage); + m_NetworkAnimator.SendServerAnimTriggerRpc(sendEntry.AnimationTriggerMessage); } } } m_SendTriggerUpdates.Clear(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool HasAuthority() + { + var isServerAuthority = m_NetworkAnimator.IsServerAuthoritative(); + return (!isServerAuthority && m_NetworkAnimator.IsOwner) || (isServerAuthority && (m_NetworkAnimator.IsServer || m_NetworkAnimator.IsOwner)); + } + /// public void NetworkUpdate(NetworkUpdateStage updateStage) { @@ -80,25 +88,32 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) { case NetworkUpdateStage.PreUpdate: { - // Only the owner or the server send messages - if (m_NetworkAnimator.IsOwner || m_IsServer) + // NOTE: This script has an order of operations requirement where + // the authority and/or server will flush messages first, parameter updates are applied + // for all instances, and then only the authority will check for animator changes. Changing + // the order could cause timing related issues. + + var hasAuthority = HasAuthority(); + // Only the authority or the server will send messages + if (hasAuthority || m_IsServer) { // Flush any pending messages FlushMessages(); } // Everyone applies any parameters updated - for (int i = 0; i < m_ProcessParameterUpdates.Count; i++) + if (m_ProcessParameterUpdates.Count > 0) { - var parameterUpdate = m_ProcessParameterUpdates[i]; - m_NetworkAnimator.UpdateParameters(ref parameterUpdate); + for (int i = 0; i < m_ProcessParameterUpdates.Count; i++) + { + var parameterUpdate = m_ProcessParameterUpdates[i]; + m_NetworkAnimator.UpdateParameters(ref parameterUpdate); + } + m_ProcessParameterUpdates.Clear(); } - m_ProcessParameterUpdates.Clear(); - var isServerAuthority = m_NetworkAnimator.IsServerAuthoritative(); - // owners when owner authoritative or the server when server authoritative are the only instances that - // checks for Animator changes - if ((!isServerAuthority && m_NetworkAnimator.IsOwner) || (isServerAuthority && m_NetworkAnimator.IsServer)) + // Only the authority checks for Animator changes + if (hasAuthority) { m_NetworkAnimator.CheckForAnimatorChanges(); } @@ -112,7 +127,7 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) /// private struct AnimationUpdate { - public ClientRpcParams ClientRpcParams; + public RpcParams RpcParams; public NetworkAnimator.AnimationMessage AnimationMessage; } @@ -121,14 +136,14 @@ private struct AnimationUpdate /// /// Invoked when a server needs to forwarding an update to the animation state /// - internal void SendAnimationUpdate(NetworkAnimator.AnimationMessage animationMessage, ClientRpcParams clientRpcParams = default) + internal void SendAnimationUpdate(NetworkAnimator.AnimationMessage animationMessage, RpcParams rpcParams = default) { - m_SendAnimationUpdates.Add(new AnimationUpdate() { ClientRpcParams = clientRpcParams, AnimationMessage = animationMessage }); + m_SendAnimationUpdates.Add(new AnimationUpdate() { RpcParams = rpcParams, AnimationMessage = animationMessage }); } private struct ParameterUpdate { - public ClientRpcParams ClientRpcParams; + public RpcParams RpcParams; public NetworkAnimator.ParametersUpdateMessage ParametersUpdateMessage; } @@ -137,9 +152,9 @@ private struct ParameterUpdate /// /// Invoked when a server needs to forwarding an update to the parameter state /// - internal void SendParameterUpdate(NetworkAnimator.ParametersUpdateMessage parametersUpdateMessage, ClientRpcParams clientRpcParams = default) + internal void SendParameterUpdate(NetworkAnimator.ParametersUpdateMessage parametersUpdateMessage, RpcParams rpcParams = default) { - m_SendParameterUpdates.Add(new ParameterUpdate() { ClientRpcParams = clientRpcParams, ParametersUpdateMessage = parametersUpdateMessage }); + m_SendParameterUpdates.Add(new ParameterUpdate() { RpcParams = rpcParams, ParametersUpdateMessage = parametersUpdateMessage }); } private List m_ProcessParameterUpdates = new List(); @@ -151,7 +166,7 @@ internal void ProcessParameterUpdate(NetworkAnimator.ParametersUpdateMessage par private struct TriggerUpdate { public bool SendToServer; - public ClientRpcParams ClientRpcParams; + public RpcParams RpcParams; public NetworkAnimator.AnimationTriggerMessage AnimationTriggerMessage; } @@ -160,9 +175,9 @@ private struct TriggerUpdate /// /// Invoked when a server needs to forward an update to a Trigger state /// - internal void QueueTriggerUpdateToClient(NetworkAnimator.AnimationTriggerMessage animationTriggerMessage, ClientRpcParams clientRpcParams = default) + internal void QueueTriggerUpdateToClient(NetworkAnimator.AnimationTriggerMessage animationTriggerMessage, RpcParams clientRpcParams = default) { - m_SendTriggerUpdates.Add(new TriggerUpdate() { ClientRpcParams = clientRpcParams, AnimationTriggerMessage = animationTriggerMessage }); + m_SendTriggerUpdates.Add(new TriggerUpdate() { RpcParams = clientRpcParams, AnimationTriggerMessage = animationTriggerMessage }); } internal void QueueTriggerUpdateToServer(NetworkAnimator.AnimationTriggerMessage animationTriggerMessage) @@ -190,6 +205,12 @@ internal NetworkAnimatorStateChangeHandler(NetworkAnimator networkAnimator) [HelpURL(HelpUrls.NetworkAnimator)] public class NetworkAnimator : NetworkBehaviour, ISerializationCallbackReceiver { +#if UNITY_EDITOR + [HideInInspector] + [SerializeField] + internal bool NetworkAnimatorExpanded; +#endif + [Serializable] internal class TransitionStateinfo { @@ -202,6 +223,49 @@ internal class TransitionStateinfo public int TransitionIndex; } + /// + /// Determines if the server or client owner pushes animation state updates. + /// + public enum AuthorityModes + { + /// + /// Server pushes animator state updates. + /// + Server, + /// + /// Client owner pushes animator state updates. + /// + Owner, + } + + /// + /// Determines whether this instance will have state updates pushed by the server or the client owner. + /// + /// +#if MULTIPLAYER_SERVICES_SDK_INSTALLED + [Tooltip("Selects who has authority(sends state updates) over the instance.When the network topology is set to distributed authority, this always defaults to owner authority.If server (the default), then only server-side adjustments to the " + + " instance will be synchronized with clients. If owner (or client), then only the owner-side adjustments to the instance will be synchronized with both the server and other clients.")] +#else + [Tooltip("Selects who has authority (sends state updates) over the instance. If server (the default), then only server-side adjustments to the instance will be synchronized with clients. If owner (or client), " + + "then only the owner-side adjustments to the instance will be synchronized with both the server and other clients.")] +#endif + public AuthorityModes AuthorityMode; + + [Tooltip("The animator that this NetworkAnimator component will be synchronizing.")] + [SerializeField] private Animator m_Animator; + + /// + /// The associated with this instance. + /// + public Animator Animator + { + get { return m_Animator; } + set + { + m_Animator = value; + } + } + /// /// Used to build the destination state to transition info table /// @@ -237,8 +301,36 @@ private void BuildDestinationToTransitionInfoTable() } } + [Serializable] + internal class AnimatorParameterEntry + { +#pragma warning disable IDE1006 + [HideInInspector] + public string name; +#pragma warning restore IDE1006 + public int NameHash; + public bool Synchronize; + public AnimatorControllerParameterType ParameterType; + } + + [Serializable] + internal class AnimatorParametersListContainer + { + public List ParameterEntries = new List(); + } + + [SerializeField] + internal AnimatorParametersListContainer AnimatorParameterEntries; + + internal Dictionary AnimatorParameterEntryTable = new Dictionary(); #if UNITY_EDITOR + [HideInInspector] + [SerializeField] + internal bool AnimatorParametersExpanded; + + internal Dictionary ParameterToNameLookup = new Dictionary(); + private void ParseStateMachineStates(int layerIndex, ref AnimatorController animatorController, ref AnimatorStateMachine stateMachine) { for (int y = 0; y < stateMachine.states.Length; y++) @@ -329,6 +421,73 @@ private void BuildTransitionStateInfoList() } } + internal void ProcessParameterEntries() + { + if (!Animator) + { + if (AnimatorParameterEntries != null && AnimatorParameterEntries.ParameterEntries.Count > 0) + { + AnimatorParameterEntries.ParameterEntries.Clear(); + } + return; + } + + var parameters = Animator.parameters; + + var parametersToRemove = new List(); + ParameterToNameLookup.Clear(); + foreach (var parameter in parameters) + { + ParameterToNameLookup.Add(parameter.nameHash, parameter); + } + + // Rebuild the parameter entry table for the inspector view + AnimatorParameterEntryTable.Clear(); + foreach (var parameterEntry in AnimatorParameterEntries.ParameterEntries) + { + // Check for removed parameters. + if (!ParameterToNameLookup.ContainsKey(parameterEntry.NameHash)) + { + parametersToRemove.Add(parameterEntry); + // Skip this removed entry + continue; + } + + // Build the list of known parameters + if (!AnimatorParameterEntryTable.ContainsKey(parameterEntry.NameHash)) + { + AnimatorParameterEntryTable.Add(parameterEntry.NameHash, parameterEntry); + } + + var parameter = ParameterToNameLookup[parameterEntry.NameHash]; + parameterEntry.name = parameter.name; + parameterEntry.ParameterType = parameter.type; + } + + // Update for removed parameters + foreach (var parameterEntry in parametersToRemove) + { + AnimatorParameterEntries.ParameterEntries.Remove(parameterEntry); + } + + // Update any newly added parameters + foreach (var parameterLookUp in ParameterToNameLookup) + { + if (!AnimatorParameterEntryTable.ContainsKey(parameterLookUp.Value.nameHash)) + { + var animatorParameterEntry = new AnimatorParameterEntry() + { + name = parameterLookUp.Value.name, + NameHash = parameterLookUp.Value.nameHash, + ParameterType = parameterLookUp.Value.type, + Synchronize = true, + }; + AnimatorParameterEntries.ParameterEntries.Add(animatorParameterEntry); + AnimatorParameterEntryTable.Add(parameterLookUp.Value.nameHash, animatorParameterEntry); + } + } + } + /// /// In-Editor Only /// Virtual OnValidate method for custom derived NetworkAnimator classes. @@ -336,6 +495,7 @@ private void BuildTransitionStateInfoList() protected virtual void OnValidate() { BuildTransitionStateInfoList(); + ProcessParameterEntries(); } #endif @@ -489,24 +649,19 @@ public void NetworkSerialize(BufferSerializer serializer) where T : IReade } } - [SerializeField] private Animator m_Animator; - - public Animator Animator - { - get { return m_Animator; } - set - { - m_Animator = value; - } - } - - internal bool IsServerAuthoritative() + /// + /// Determines whether the is or based on the field. + /// Optionally, you can still derive from and override the method. + /// + /// or + public bool IsServerAuthoritative() { return OnIsServerAuthoritative(); } /// - /// Override this method and return false to switch to owner authoritative mode. + /// Override this method and return false to switch to owner authoritative mode.
+ /// Alternately, you can update the field within the inspector view to select the authority mode. ///
/// /// When using a distributed authority network topology, this will default to @@ -514,12 +669,11 @@ internal bool IsServerAuthoritative() /// protected virtual bool OnIsServerAuthoritative() { - if (!m_LocalNetworkManager) + if (DistributedAuthorityMode) { - return true; + return false; } - - return !DistributedAuthorityMode; + return AuthorityMode == AuthorityModes.Server; } private int[] m_TransitionHash; @@ -527,8 +681,8 @@ protected virtual bool OnIsServerAuthoritative() private float[] m_LayerWeights; private static byte[] s_EmptyArray = new byte[] { }; private List m_ParametersToUpdate; - private List m_ClientSendList; - private ClientRpcParams m_ClientRpcParams; + private RpcParams m_RpcParams; + private RpcTargetGroup m_TargetGroup; private AnimationMessage m_AnimationMessage; private NetworkAnimatorStateChangeHandler m_NetworkAnimatorStateChangeHandler; @@ -539,6 +693,7 @@ protected virtual bool OnIsServerAuthoritative() private unsafe struct AnimatorParamCache { + internal bool Exclude; internal int Hash; internal int Type; internal fixed byte Value[4]; // this is a max size of 4 bytes @@ -583,6 +738,8 @@ public override void OnDestroy() { SpawnCleanup(); + m_TargetGroup?.Dispose(); + if (m_CachedAnimatorParameters != null && m_CachedAnimatorParameters.IsCreated) { m_CachedAnimatorParameters.Dispose(); @@ -605,6 +762,11 @@ protected virtual void Awake() return; } + foreach (var parameterEntry in AnimatorParameterEntries.ParameterEntries) + { + AnimatorParameterEntryTable.TryAdd(parameterEntry.NameHash, parameterEntry); + } + int layers = m_Animator.layerCount; // Initializing the below arrays for everyone handles an issue // when running in owner authoritative mode and the owner changes. @@ -648,11 +810,17 @@ protected virtual void Awake() for (var i = 0; i < parameters.Length; i++) { var parameter = parameters[i]; + var synchronizeParameter = true; + if (AnimatorParameterEntryTable.ContainsKey(parameter.nameHash)) + { + synchronizeParameter = AnimatorParameterEntryTable[parameter.nameHash].Synchronize; + } var cacheParam = new AnimatorParamCache { Type = UnsafeUtility.EnumToInt(parameter.type), - Hash = parameter.nameHash + Hash = parameter.nameHash, + Exclude = !synchronizeParameter }; unsafe @@ -736,12 +904,12 @@ public override void OnNetworkSpawn() NetworkLog.LogWarningServer($"[{gameObject.name}][{nameof(NetworkAnimator)}] {nameof(Animator)} is not assigned! Animation synchronization will not work for this instance!"); } - m_ClientSendList = new List(128); - m_ClientRpcParams = new ClientRpcParams + m_TargetGroup = RpcTarget.Group(new List(128), RpcTargetUse.Persistent) as RpcTargetGroup; + m_RpcParams = new RpcParams() { - Send = new ClientRpcSendParams + Send = new RpcSendParams() { - TargetClientIds = m_ClientSendList + Target = m_TargetGroup } }; @@ -766,6 +934,10 @@ private void WriteSynchronizationData(ref BufferSerializer serializer) whe m_ParametersToUpdate.Clear(); for (int i = 0; i < m_CachedAnimatorParameters.Length; i++) { + if (m_CachedAnimatorParameters[i].Exclude) + { + continue; + } m_ParametersToUpdate.Add(i); } // Write, apply, and serialize @@ -1009,27 +1181,27 @@ internal void CheckForAnimatorChanges() else if (!IsServer && IsOwner) { - SendAnimStateServerRpc(m_AnimationMessage); + SendServerAnimStateRpc(m_AnimationMessage); } else { // Just notify all remote clients and not the local server - m_ClientSendList.Clear(); + m_TargetGroup.Clear(); foreach (var clientId in m_LocalNetworkManager.ConnectionManager.ConnectedClientIds) { if (clientId == m_LocalNetworkManager.LocalClientId || !NetworkObject.Observers.Contains(clientId)) { continue; } - m_ClientSendList.Add(clientId); + m_TargetGroup.Add(clientId); } - m_ClientRpcParams.Send.TargetClientIds = m_ClientSendList; - SendAnimStateClientRpc(m_AnimationMessage, m_ClientRpcParams); + m_RpcParams.Send.Target = m_TargetGroup; + SendClientAnimStateRpc(m_AnimationMessage, m_RpcParams); } } } - private void SendParametersUpdate(ClientRpcParams clientRpcParams = default, bool sendDirect = false) + private void SendParametersUpdate(RpcParams rpcParams = default, bool sendDirect = false) { WriteParameters(ref m_ParameterWriter); @@ -1052,17 +1224,17 @@ private void SendParametersUpdate(ClientRpcParams clientRpcParams = default, boo { if (!IsServer) { - SendParametersUpdateServerRpc(parametersMessage); + SendServerParametersUpdateRpc(parametersMessage); } else { if (sendDirect) { - SendParametersUpdateClientRpc(parametersMessage, clientRpcParams); + SendClientParametersUpdateRpc(parametersMessage, rpcParams); } else { - m_NetworkAnimatorStateChangeHandler.SendParameterUpdate(parametersMessage, clientRpcParams); + m_NetworkAnimatorStateChangeHandler.SendParameterUpdate(parametersMessage, rpcParams); } } } @@ -1093,6 +1265,11 @@ private unsafe bool CheckParametersChanged() { ref var cacheValue = ref UnsafeUtility.ArrayElementAsRef(m_CachedAnimatorParameters.GetUnsafePtr(), i); + if (cacheValue.Exclude) + { + continue; + } + // If a parameter gets controlled by a curve during runtime after initialization of NetworkAnimator // then ignore changes to this parameter. We are not removing the parameter in the event that // it no longer is controlled by a curve. @@ -1149,6 +1326,13 @@ private unsafe void WriteParameters(ref FastBufferWriter writer) foreach (var parameterIndex in m_ParametersToUpdate) { ref var cacheValue = ref UnsafeUtility.ArrayElementAsRef(m_CachedAnimatorParameters.GetUnsafePtr(), parameterIndex); + + if (cacheValue.Exclude) + { + Debug.LogWarning($"Parameter hash:{cacheValue.Hash} should be excluded but is in the parameters to update list when writing parameter values!"); + continue; + } + var hash = cacheValue.Hash; BytePacker.WriteValuePacked(writer, (uint)parameterIndex); if (cacheValue.Type == AnimationParamEnumWrapper.AnimatorControllerParameterInt) @@ -1317,8 +1501,8 @@ internal void UpdateAnimationState(AnimationState animationState) /// Server-side animator parameter update request /// The server sets its local parameters and then forwards the message to the remaining clients ///
- [ServerRpc] - private unsafe void SendParametersUpdateServerRpc(ParametersUpdateMessage parametersUpdate, ServerRpcParams serverRpcParams = default) + [Rpc(SendTo.Server, AllowTargetOverride = true, InvokePermission = RpcInvokePermission.Owner)] + private unsafe void SendServerParametersUpdateRpc(ParametersUpdateMessage parametersUpdate, RpcParams rpcParams = default) { if (IsServerAuthoritative()) { @@ -1326,7 +1510,7 @@ private unsafe void SendParametersUpdateServerRpc(ParametersUpdateMessage parame } else { - if (serverRpcParams.Receive.SenderClientId != OwnerClientId) + if (rpcParams.Receive.SenderClientId != OwnerClientId) { return; } @@ -1337,26 +1521,26 @@ private unsafe void SendParametersUpdateServerRpc(ParametersUpdateMessage parame return; } - m_ClientSendList.Clear(); + m_TargetGroup.Clear(); foreach (var clientId in connectedClientIds) { - if (clientId == serverRpcParams.Receive.SenderClientId || clientId == NetworkManager.ServerClientId || !NetworkObject.Observers.Contains(clientId)) + if (clientId == rpcParams.Receive.SenderClientId || clientId == NetworkManager.ServerClientId || !NetworkObject.Observers.Contains(clientId)) { continue; } - m_ClientSendList.Add(clientId); + m_TargetGroup.Add(clientId); } - m_ClientRpcParams.Send.TargetClientIds = m_ClientSendList; - m_NetworkAnimatorStateChangeHandler.SendParameterUpdate(parametersUpdate, m_ClientRpcParams); + m_RpcParams.Send.Target = m_TargetGroup; + m_NetworkAnimatorStateChangeHandler.SendParameterUpdate(parametersUpdate, m_RpcParams); } } /// /// Distributed Authority: Updates the client's animator's parameters /// - [Rpc(SendTo.NotAuthority)] - internal void SendParametersUpdateRpc(ParametersUpdateMessage parametersUpdate) + [Rpc(SendTo.NotAuthority, AllowTargetOverride = true, InvokePermission = RpcInvokePermission.Owner)] + internal void SendParametersUpdateRpc(ParametersUpdateMessage parametersUpdate, RpcParams rpcParams = default) { m_NetworkAnimatorStateChangeHandler.ProcessParameterUpdate(parametersUpdate); } @@ -1364,11 +1548,11 @@ internal void SendParametersUpdateRpc(ParametersUpdateMessage parametersUpdate) /// /// Client-Server: Updates the client's animator's parameters /// - [ClientRpc] - internal void SendParametersUpdateClientRpc(ParametersUpdateMessage parametersUpdate, ClientRpcParams clientRpcParams = default) + [Rpc(SendTo.NotMe, AllowTargetOverride = true)] + internal void SendClientParametersUpdateRpc(ParametersUpdateMessage parametersUpdate, RpcParams rpcParams = default) { var isServerAuthoritative = IsServerAuthoritative(); - if (!isServerAuthoritative && !IsOwner || isServerAuthoritative) + if ((!isServerAuthoritative && !IsOwner) || (isServerAuthoritative && !IsServer)) { m_NetworkAnimatorStateChangeHandler.ProcessParameterUpdate(parametersUpdate); } @@ -1378,8 +1562,8 @@ internal void SendParametersUpdateClientRpc(ParametersUpdateMessage parametersUp /// Server-side animation state update request /// The server sets its local state and then forwards the message to the remaining clients /// - [ServerRpc] - private void SendAnimStateServerRpc(AnimationMessage animationMessage, ServerRpcParams serverRpcParams = default) + [Rpc(SendTo.Server, AllowTargetOverride = true)] + private void SendServerAnimStateRpc(AnimationMessage animationMessage, RpcParams rcParams = default) { if (IsServerAuthoritative()) { @@ -1387,7 +1571,7 @@ private void SendAnimStateServerRpc(AnimationMessage animationMessage, ServerRpc } else { - if (serverRpcParams.Receive.SenderClientId != OwnerClientId) + if (rcParams.Receive.SenderClientId != OwnerClientId) { return; } @@ -1403,25 +1587,26 @@ private void SendAnimStateServerRpc(AnimationMessage animationMessage, ServerRpc return; } - m_ClientSendList.Clear(); + m_TargetGroup.Clear(); + foreach (var clientId in connectedClientIds) { - if (clientId == serverRpcParams.Receive.SenderClientId || clientId == NetworkManager.ServerClientId || !NetworkObject.Observers.Contains(clientId)) + if (clientId == rcParams.Receive.SenderClientId || clientId == NetworkManager.ServerClientId || !NetworkObject.Observers.Contains(clientId)) { continue; } - m_ClientSendList.Add(clientId); + m_TargetGroup.Add(clientId); } - m_ClientRpcParams.Send.TargetClientIds = m_ClientSendList; - m_NetworkAnimatorStateChangeHandler.SendAnimationUpdate(animationMessage, m_ClientRpcParams); + m_RpcParams.Send.Target = m_TargetGroup; + m_NetworkAnimatorStateChangeHandler.SendAnimationUpdate(animationMessage, m_RpcParams); } } /// /// Client-Server: Internally-called RPC client-side receiving function to update animation states /// - [ClientRpc] - internal void SendAnimStateClientRpc(AnimationMessage animationMessage, ClientRpcParams clientRpcParams = default) + [Rpc(SendTo.NotServer, AllowTargetOverride = true)] + internal void SendClientAnimStateRpc(AnimationMessage animationMessage, RpcParams rpcParams = default) { ProcessAnimStates(animationMessage); } @@ -1429,12 +1614,16 @@ internal void SendAnimStateClientRpc(AnimationMessage animationMessage, ClientRp /// /// Distributed Authority: Internally-called RPC non-authority receiving function to update animation states /// - [Rpc(SendTo.NotAuthority)] - internal void SendAnimStateRpc(AnimationMessage animationMessage) + [Rpc(SendTo.NotAuthority, AllowTargetOverride = true, InvokePermission = RpcInvokePermission.Owner)] + internal void SendAnimStateRpc(AnimationMessage animationMessage, RpcParams rpcParams = default) { ProcessAnimStates(animationMessage); } + /// + /// Process incoming . + /// + /// The message to process. private void ProcessAnimStates(AnimationMessage animationMessage) { if (HasAuthority) @@ -1454,17 +1643,15 @@ private void ProcessAnimStates(AnimationMessage animationMessage) } } - - /// /// Server-side trigger state update request /// The server sets its local state and then forwards the message to the remaining clients /// - [ServerRpc] - internal void SendAnimTriggerServerRpc(AnimationTriggerMessage animationTriggerMessage, ServerRpcParams serverRpcParams = default) + [Rpc(SendTo.Server, AllowTargetOverride = true)] + internal void SendServerAnimTriggerRpc(AnimationTriggerMessage animationTriggerMessage, RpcParams rpcParams = default) { // Ignore if a non-owner sent this. - if (serverRpcParams.Receive.SenderClientId != OwnerClientId) + if (rpcParams.Receive.SenderClientId != OwnerClientId) { if (m_LocalNetworkManager.LogLevel == LogLevel.Developer) { @@ -1478,23 +1665,22 @@ internal void SendAnimTriggerServerRpc(AnimationTriggerMessage animationTriggerM var connectedClientIds = m_LocalNetworkManager.ConnectionManager.ConnectedClientIds; - m_ClientSendList.Clear(); + m_TargetGroup.Clear(); foreach (var clientId in connectedClientIds) { if (clientId == NetworkManager.ServerClientId || !NetworkObject.Observers.Contains(clientId)) { continue; } - m_ClientSendList.Add(clientId); + m_TargetGroup.Add(clientId); } if (IsServerAuthoritative()) { - m_NetworkAnimatorStateChangeHandler.QueueTriggerUpdateToClient(animationTriggerMessage, m_ClientRpcParams); + m_NetworkAnimatorStateChangeHandler.QueueTriggerUpdateToClient(animationTriggerMessage, m_RpcParams); } else if (connectedClientIds.Count > (IsHost ? 2 : 1)) { - m_ClientSendList.Remove(serverRpcParams.Receive.SenderClientId); - m_NetworkAnimatorStateChangeHandler.QueueTriggerUpdateToClient(animationTriggerMessage, m_ClientRpcParams); + m_NetworkAnimatorStateChangeHandler.QueueTriggerUpdateToClient(animationTriggerMessage, m_RpcParams); } } @@ -1507,12 +1693,12 @@ private void InternalSetTrigger(int hash, bool isSet = true) } /// - /// Distributed Authority: Internally-called RPC client receiving function to update a trigger when the server wants to forward - /// a trigger to a client + /// Distributed Authority: Internally-called RPC client receiving function to update a trigger when the authority wants + /// to forward a trigger to a client /// /// the payload containing the trigger data to apply - [Rpc(SendTo.NotAuthority)] - internal void SendAnimTriggerRpc(AnimationTriggerMessage animationTriggerMessage) + [Rpc(SendTo.NotAuthority, AllowTargetOverride = true, InvokePermission = RpcInvokePermission.Owner)] + internal void SendAnimTriggerRpc(AnimationTriggerMessage animationTriggerMessage, RpcParams rpcParams = default) { InternalSetTrigger(animationTriggerMessage.Hash, animationTriggerMessage.IsTriggerSet); } @@ -1523,8 +1709,8 @@ internal void SendAnimTriggerRpc(AnimationTriggerMessage animationTriggerMessage /// /// the payload containing the trigger data to apply /// unused - [ClientRpc] - internal void SendAnimTriggerClientRpc(AnimationTriggerMessage animationTriggerMessage, ClientRpcParams clientRpcParams = default) + [Rpc(SendTo.NotServer, AllowTargetOverride = true)] + internal void SendClientAnimTriggerRpc(AnimationTriggerMessage animationTriggerMessage, RpcParams rpcParams = default) { InternalSetTrigger(animationTriggerMessage.Hash, animationTriggerMessage.IsTriggerSet); } @@ -1567,10 +1753,7 @@ public void SetTrigger(int hash, bool setTrigger = true) { /// as to why we queue m_NetworkAnimatorStateChangeHandler.QueueTriggerUpdateToClient(animTriggerMessage); - if (!IsHost) - { - InternalSetTrigger(hash, setTrigger); - } + InternalSetTrigger(hash, setTrigger); } else { @@ -1599,6 +1782,39 @@ public void ResetTrigger(int hash) { SetTrigger(hash, false); } + + /// + /// Allows for the enabling or disabling the synchronization of a specific parameter. + /// + /// The name of the parameter. + /// Whether to enable or disable the synchronization of the parameter. + public void EnableParameterSynchronization(string parameterName, bool isEnabled) + { + EnableParameterSynchronization(Animator.StringToHash(parameterName), isEnabled); + } + + /// + /// Allows for the enabling or disabling the synchronization of a specific parameter. + /// + /// The hash value (from using ) of the parameter name. + /// Whether to enable or disable the synchronization of the parameter. + public void EnableParameterSynchronization(int parameterNameHash, bool isEnabled) + { + var serverAuthoritative = OnIsServerAuthoritative(); + if (!IsSpawned || serverAuthoritative && IsServer || !serverAuthoritative && IsOwner) + { + for (int i = 0; i < m_CachedAnimatorParameters.Length; i++) + { + var cachedParameter = m_CachedAnimatorParameters[i]; + if (cachedParameter.Hash == parameterNameHash) + { + cachedParameter.Exclude = !isEnabled; + m_CachedAnimatorParameters[i] = cachedParameter; + break; + } + } + } + } } } // COM_UNITY_MODULES_ANIMATION diff --git a/testproject/Assets/Tests/Manual/NetworkAnimatorTests/AnimatedCubeController.cs b/testproject/Assets/Tests/Manual/NetworkAnimatorTests/AnimatedCubeController.cs index 5efbb54fa4..4ed476874f 100644 --- a/testproject/Assets/Tests/Manual/NetworkAnimatorTests/AnimatedCubeController.cs +++ b/testproject/Assets/Tests/Manual/NetworkAnimatorTests/AnimatedCubeController.cs @@ -1,4 +1,3 @@ -using System; using System.Collections; using Unity.Netcode; using Unity.Netcode.Components; @@ -12,19 +11,11 @@ public class AnimatedCubeController : NetworkBehaviour private Animator m_Animator; private bool m_Rotate; private NetworkAnimator m_NetworkAnimator; - private bool m_IsServerAuthoritative = true; + private bool m_IsServerAuthoritative => m_NetworkAnimator ? m_NetworkAnimator.IsServerAuthoritative() : true; private void DetermineNetworkAnimatorComponentType() { m_NetworkAnimator = GetComponent(); - if (m_NetworkAnimator != null) - { - m_IsServerAuthoritative = m_NetworkAnimator.GetType() != typeof(OwnerNetworkAnimator); - } - else - { - throw new Exception($"{nameof(AnimatedCubeController)} requires that it is paired with either a {nameof(NetworkAnimator)} or {nameof(OwnerNetworkAnimator)}. Neither of the two components were found!"); - } } public override void OnNetworkSpawn() @@ -136,8 +127,8 @@ private IEnumerator TestAnimatorRoutine() counter = 0.0f; while (counter < 100) { - m_Animator.SetFloat("TestFloat", UnityEngine.Random.Range(0.0f, 100.0f)); - m_Animator.SetInteger("TestInt", UnityEngine.Random.Range(0, 100)); + m_Animator.SetFloat("TestFloat", Random.Range(0.0f, 100.0f)); + m_Animator.SetInteger("TestInt", Random.Range(0, 100)); counter++; yield return waitForSeconds; } diff --git a/testproject/Assets/Tests/Manual/NetworkAnimatorTests/CubeAnimatorController.controller b/testproject/Assets/Tests/Manual/NetworkAnimatorTests/CubeAnimatorController.controller index 3434711a59..edc31e90ab 100644 --- a/testproject/Assets/Tests/Manual/NetworkAnimatorTests/CubeAnimatorController.controller +++ b/testproject/Assets/Tests/Manual/NetworkAnimatorTests/CubeAnimatorController.controller @@ -761,6 +761,12 @@ AnimatorController: m_DefaultInt: 0 m_DefaultBool: 0 m_Controller: {fileID: 9100000} + - m_Name: ExcludeFromSync + m_Type: 1 + m_DefaultFloat: 0 + m_DefaultInt: 0 + m_DefaultBool: 0 + m_Controller: {fileID: 9100000} m_AnimatorLayers: - serializedVersion: 5 m_Name: Base Layer diff --git a/testproject/Assets/Tests/Manual/NetworkAnimatorTests/NetworkAnimatorServerOwnerTest.unity b/testproject/Assets/Tests/Manual/NetworkAnimatorTests/NetworkAnimatorServerOwnerTest.unity index efd820815d..568acb89e8 100644 --- a/testproject/Assets/Tests/Manual/NetworkAnimatorTests/NetworkAnimatorServerOwnerTest.unity +++ b/testproject/Assets/Tests/Manual/NetworkAnimatorTests/NetworkAnimatorServerOwnerTest.unity @@ -13,7 +13,7 @@ OcclusionCullingSettings: --- !u!104 &2 RenderSettings: m_ObjectHideFlags: 0 - serializedVersion: 9 + serializedVersion: 10 m_Fog: 0 m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} m_FogMode: 3 @@ -38,13 +38,12 @@ RenderSettings: m_ReflectionIntensity: 1 m_CustomReflection: {fileID: 0} m_Sun: {fileID: 0} - m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} m_UseRadianceAmbientProbe: 0 --- !u!157 &3 LightmapSettings: m_ObjectHideFlags: 0 - serializedVersion: 12 - m_GIWorkflowMode: 1 + serializedVersion: 13 + m_BakeOnSceneLoad: 0 m_GISettings: serializedVersion: 2 m_BounceScale: 1 @@ -67,9 +66,6 @@ LightmapSettings: m_LightmapParameters: {fileID: 0} m_LightmapsBakeMode: 1 m_TextureCompression: 1 - m_FinalGather: 0 - m_FinalGatherFiltering: 1 - m_FinalGatherRayCount: 256 m_ReflectionCompression: 2 m_MixedBakeMode: 2 m_BakeBackend: 1 @@ -104,7 +100,7 @@ NavMeshSettings: serializedVersion: 2 m_ObjectHideFlags: 0 m_BuildSettings: - serializedVersion: 2 + serializedVersion: 3 agentTypeID: 0 agentRadius: 0.5 agentHeight: 2 @@ -117,7 +113,7 @@ NavMeshSettings: cellSize: 0.16666667 manualTileSize: 0 tileSize: 256 - accuratePlacement: 0 + buildHeightMesh: 0 maxJobWorkers: 0 preserveTilesOutsideBounds: 0 debug: @@ -153,21 +149,14 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 593a2fe42fa9d37498c96f9a383b6521, type: 3} m_Name: m_EditorClassIdentifier: - RunInBackground: 1 - LogLevel: 1 + NetworkManagerExpanded: 0 NetworkConfig: ProtocolVersion: 0 NetworkTransport: {fileID: 57527694} PlayerPrefab: {fileID: 3214090169675393154, guid: 978fc8cd63a1294438ebf3b352814970, type: 3} - NetworkPrefabs: - - Override: 0 - Prefab: {fileID: 3214090169675393154, guid: 978fc8cd63a1294438ebf3b352814970, - type: 3} - SourcePrefabToOverride: {fileID: 442217489085244684, guid: 5eca8a21314fe4278ba2571c289a9773, - type: 3} - SourceHashToOverride: 0 - OverridingTargetPrefab: {fileID: 0} + Prefabs: + NetworkPrefabsLists: [] TickRate: 30 ClientConnectionBufferTimeout: 10 ConnectionApproval: 0 @@ -181,8 +170,22 @@ MonoBehaviour: NetworkIdRecycleDelay: 120 RpcHashSize: 0 LoadSceneTimeOut: 120 - SpawnTimeout: 1 + SpawnTimeout: 10 EnableNetworkLogs: 1 + NetworkTopology: 0 + UseCMBService: 0 + AutoSpawnPlayerPrefabClientSide: 1 + NetworkProfilingMetrics: 1 + OldPrefabList: + - Override: 0 + Prefab: {fileID: 3214090169675393154, guid: 978fc8cd63a1294438ebf3b352814970, + type: 3} + SourcePrefabToOverride: {fileID: 442217489085244684, guid: 5eca8a21314fe4278ba2571c289a9773, + type: 3} + SourceHashToOverride: 0 + OverridingTargetPrefab: {fileID: 0} + RunInBackground: 1 + LogLevel: 1 --- !u!4 &57527693 Transform: m_ObjectHideFlags: 0 @@ -190,13 +193,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 57527690} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -4.4, y: 0, z: 3.53} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &57527694 MonoBehaviour: @@ -211,6 +214,8 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: m_ProtocolType: 0 + m_UseWebSockets: 0 + m_UseEncryption: 0 m_MaxPacketQueueSize: 128 m_MaxPayloadSize: 80000 m_HeartbeatTimeoutMS: 500 @@ -220,7 +225,7 @@ MonoBehaviour: ConnectionData: Address: 127.0.0.1 Port: 7777 - ServerListenAddress: + ServerListenAddress: 127.0.0.1 DebugSimulator: PacketDelayMS: 0 PacketJitterMS: 0 @@ -234,8 +239,8 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 84683368} - - component: {fileID: 84683367} - component: {fileID: 84683366} + - component: {fileID: 84683367} m_Layer: 0 m_Name: Stats m_TagString: Untagged @@ -256,9 +261,18 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: GlobalObjectIdHash: 3133660421 + InScenePlacedSourceGlobalObjectIdHash: 0 + DeferredDespawnTick: 0 + Ownership: 1 AlwaysReplicateAsRoot: 0 + SynchronizeTransform: 1 + ActiveSceneSynchronization: 0 + SceneMigrationSynchronization: 0 + SpawnWithObservers: 1 DontDestroyWithOwner: 0 AutoObjectParentSync: 1 + SyncOwnerTransformWhenParented: 1 + AllowOwnerToParent: 0 --- !u!114 &84683367 MonoBehaviour: m_ObjectHideFlags: 0 @@ -271,6 +285,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: cb5f3e55f5dd247129d8a4979b80ebbb, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 m_ClientServerToggle: {fileID: 0} m_TrackSceneEvents: 0 --- !u!4 &84683368 @@ -280,13 +295,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 84683365} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 318.45444, y: 110.697815, z: 216.79077} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 6 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &146283178 GameObject: @@ -319,7 +334,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 343841036} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0} m_AnchorMax: {x: 0.5, y: 0} @@ -444,7 +458,9 @@ Canvas: m_OverrideSorting: 0 m_OverridePixelPerfect: 0 m_SortingBucketNormalizedSize: 0 + m_VertexColorAlwaysGammaSpace: 0 m_AdditionalShaderChannelsFlag: 0 + m_UpdateRectTransformForStandalone: 0 m_SortingLayerID: 0 m_SortingOrder: 1 m_TargetDisplay: 0 @@ -462,7 +478,6 @@ RectTransform: m_Children: - {fileID: 146283179} m_Father: {fileID: 0} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -529,13 +544,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 809184351} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &825254415 GameObject: @@ -562,9 +577,8 @@ Light: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 825254415} m_Enabled: 1 - serializedVersion: 10 + serializedVersion: 11 m_Type: 1 - m_Shape: 0 m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} m_Intensity: 1 m_Range: 10 @@ -614,8 +628,12 @@ Light: m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} m_UseBoundingSphereOverride: 0 m_UseViewFrustumForShadowCasterCull: 1 + m_ForceVisible: 0 m_ShadowRadius: 0 m_ShadowAngle: 0 + m_LightUnit: 1 + m_LuxAtDistance: 1 + m_EnableSpotReflector: 1 --- !u!4 &825254417 Transform: m_ObjectHideFlags: 0 @@ -623,13 +641,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 825254415} + serializedVersion: 2 m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} m_LocalPosition: {x: 0, y: 3, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} --- !u!224 &1537133403 stripped RectTransform: @@ -677,9 +695,17 @@ Camera: m_projectionMatrixMode: 1 m_GateFitMode: 2 m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 m_SensorSize: {x: 36, y: 24} m_LensShift: {x: 0, y: 0} - m_FocalLength: 50 m_NormalizedViewPortRect: serializedVersion: 2 x: 0 @@ -713,13 +739,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1543554891} + serializedVersion: 2 m_LocalRotation: {x: 0.28182787, y: -0, z: -0, w: 0.959465} m_LocalPosition: {x: 5.7, y: 90, z: -136} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 32.739, y: 0, z: 0} --- !u!1 &1740491936 GameObject: @@ -797,7 +823,9 @@ Canvas: m_OverrideSorting: 0 m_OverridePixelPerfect: 0 m_SortingBucketNormalizedSize: 0 + m_VertexColorAlwaysGammaSpace: 0 m_AdditionalShaderChannelsFlag: 0 + m_UpdateRectTransformForStandalone: 0 m_SortingLayerID: 0 m_SortingOrder: 0 m_TargetDisplay: 0 @@ -816,7 +844,6 @@ RectTransform: - {fileID: 1820369379} - {fileID: 1537133403} m_Father: {fileID: 0} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -828,6 +855,7 @@ PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 1740491940} m_Modifications: - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, @@ -962,6 +990,9 @@ PrefabInstance: objectReference: {fileID: 11400000, guid: 4a3cdce12e998384f8aca207b5a2c700, type: 2} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 3200770c16e3b2b4ebe7f604154faac7, type: 3} --- !u!224 &1820369379 stripped RectTransform: @@ -974,6 +1005,7 @@ PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 1740491940} m_Modifications: - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, @@ -1087,4 +1119,18 @@ PrefabInstance: value: ConnectionModeButtons objectReference: {fileID: 0} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: d725b5588e1b956458798319e6541d84, type: 3} +--- !u!1660057539 &9223372036854775807 +SceneRoots: + m_ObjectHideFlags: 0 + m_Roots: + - {fileID: 1543554894} + - {fileID: 825254417} + - {fileID: 57527693} + - {fileID: 343841036} + - {fileID: 809184354} + - {fileID: 1740491940} + - {fileID: 84683368} diff --git a/testproject/Assets/Tests/Manual/NetworkAnimatorTests/NetworkAnimatorTestPrefab.prefab b/testproject/Assets/Tests/Manual/NetworkAnimatorTests/NetworkAnimatorTestPrefab.prefab index 293a5c28f9..ecb2989dd7 100644 --- a/testproject/Assets/Tests/Manual/NetworkAnimatorTests/NetworkAnimatorTestPrefab.prefab +++ b/testproject/Assets/Tests/Manual/NetworkAnimatorTests/NetworkAnimatorTestPrefab.prefab @@ -26,6 +26,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 344968812738254812} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 10, y: 10, z: 10} @@ -34,7 +35,6 @@ Transform: - {fileID: 6945271965867870025} - {fileID: 7346803634269932765} m_Father: {fileID: 3078684837582866037} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &1862129067553351562 MeshFilter: @@ -61,6 +61,9 @@ MeshRenderer: m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -94,9 +97,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 344968812738254812} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000002, y: 1, z: 1.0000002} m_Center: {x: 0.0000019073493, y: 0.000000007450581, z: -0.000001907349} --- !u!1 &2187136729444773472 @@ -125,13 +136,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2187136729444773472} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0.505} m_LocalScale: {x: 0.1, y: 0.1, z: 0.1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 2505406658094773853} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3821723076754617548 MeshFilter: @@ -158,6 +169,9 @@ MeshRenderer: m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -191,9 +205,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2187136729444773472} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 1} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &2963963144167575062 @@ -227,7 +249,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 3214090169133903717} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -287,7 +308,7 @@ GameObject: - component: {fileID: 3078684837582866037} - component: {fileID: 3078684837575715027} - component: {fileID: 5295167409644547614} - - component: {fileID: 3632991713004262889} + - component: {fileID: 6991069935266812612} m_Layer: 0 m_Name: NetworkAnimatedCube-Owner m_TagString: Untagged @@ -302,6 +323,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3078684837583100501} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 15, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} @@ -309,11 +331,10 @@ Transform: m_Children: - {fileID: 7635925556881204871} m_Father: {fileID: 3214090169675393152} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!95 &3078684837575715027 Animator: - serializedVersion: 4 + serializedVersion: 7 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -327,10 +348,12 @@ Animator: m_ApplyRootMotion: 0 m_LinearVelocityBlending: 0 m_StabilizeFeet: 0 + m_AnimatePhysics: 0 m_WarningMessage: m_HasTransformHierarchy: 1 m_AllowConstantClipSamplingOptimization: 1 - m_KeepAnimatorControllerStateOnDisable: 0 + m_KeepAnimatorStateOnDisable: 0 + m_WriteDefaultValuesOnDisable: 0 --- !u!114 &5295167409644547614 MonoBehaviour: m_ObjectHideFlags: 0 @@ -343,8 +366,8 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: b322be05414199349ae9d1aea28228a7, type: 3} m_Name: m_EditorClassIdentifier: - TestIterations: 10 ---- !u!114 &3632991713004262889 + ShowTopMostFoldoutHeaderGroup: 1 +--- !u!114 &6991069935266812612 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -353,29 +376,103 @@ MonoBehaviour: m_GameObject: {fileID: 3078684837583100501} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: f6a3556fb5f3bee4e8a0fb88acff87ff, type: 3} + m_Script: {fileID: 11500000, guid: e8d0727d5ae3244e3b569694d3912374, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 + NetworkAnimatorExpanded: 0 + AuthorityMode: 1 + m_Animator: {fileID: 3078684837575715027} TransitionStateInfoList: - - Layer: 0 + - IsCrossFadeExit: 0 + Layer: 0 OriginatingState: 2081823275 DestinationState: -570305638 TransitionDuration: 0.30227518 TriggerNameHash: 1033918907 TransitionIndex: 1 - - Layer: 0 + - IsCrossFadeExit: 0 + Layer: 0 OriginatingState: 2081823275 DestinationState: -1509639022 TransitionDuration: 0.25 TriggerNameHash: 1033918907 TransitionIndex: 2 - - Layer: 1 + - IsCrossFadeExit: 0 + Layer: 1 OriginatingState: 2081823275 DestinationState: -623385122 TransitionDuration: 2.2832582 TriggerNameHash: -623385122 TransitionIndex: 0 - m_Animator: {fileID: 3078684837575715027} + - IsCrossFadeExit: 0 + Layer: 3 + OriginatingState: 2081823275 + DestinationState: -1829531531 + TransitionDuration: 0.25 + TriggerNameHash: 1080829965 + TransitionIndex: 0 + - IsCrossFadeExit: 0 + Layer: 3 + OriginatingState: 2081823275 + DestinationState: 705160537 + TransitionDuration: 0.25 + TriggerNameHash: 1080829965 + TransitionIndex: 1 + - IsCrossFadeExit: 0 + Layer: 3 + OriginatingState: 2081823275 + DestinationState: 801385362 + TransitionDuration: 0.25 + TriggerNameHash: 1080829965 + TransitionIndex: 2 + AnimatorParameterEntries: + ParameterEntries: + - name: Rotate + NameHash: 807753530 + Synchronize: 1 + ParameterType: 4 + - name: Pulse + NameHash: -623385122 + Synchronize: 1 + ParameterType: 9 + - name: TestFloat + NameHash: -758535706 + Synchronize: 1 + ParameterType: 1 + - name: TestInt + NameHash: -1682086748 + Synchronize: 1 + ParameterType: 3 + - name: TestBool + NameHash: 953368263 + Synchronize: 1 + ParameterType: 4 + - name: TestTrigger + NameHash: 1033918907 + Synchronize: 1 + ParameterType: 9 + - name: LateJoinTest + NameHash: -1545343146 + Synchronize: 1 + ParameterType: 4 + - name: Attack + NameHash: 1080829965 + Synchronize: 1 + ParameterType: 9 + - name: WeaponType + NameHash: -1936256502 + Synchronize: 1 + ParameterType: 3 + - name: Weapon + NameHash: 1855955664 + Synchronize: 1 + ParameterType: 3 + - name: ExcludeFromSync + NameHash: -1993481368 + Synchronize: 0 + ParameterType: 1 + AnimatorParametersExpanded: 0 --- !u!1 &3214090169133903718 GameObject: m_ObjectHideFlags: 0 @@ -412,7 +509,6 @@ RectTransform: - {fileID: 8890957386270744200} - {fileID: 4304100640823132132} m_Father: {fileID: 3214090169675393152} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -436,7 +532,9 @@ Canvas: m_OverrideSorting: 0 m_OverridePixelPerfect: 0 m_SortingBucketNormalizedSize: 0 + m_VertexColorAlwaysGammaSpace: 0 m_AdditionalShaderChannelsFlag: 0 + m_UpdateRectTransformForStandalone: 0 m_SortingLayerID: 0 m_SortingOrder: 0 m_TargetDisplay: 0 @@ -506,6 +604,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3214090169675393154} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} @@ -515,7 +614,6 @@ Transform: - {fileID: 3078684837582866037} - {fileID: 3604323723684300772} m_Father: {fileID: 0} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &3214090169675393153 MonoBehaviour: @@ -529,10 +627,19 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} m_Name: m_EditorClassIdentifier: - GlobalObjectIdHash: 951099334 + GlobalObjectIdHash: 3334901199 + InScenePlacedSourceGlobalObjectIdHash: 0 + DeferredDespawnTick: 0 + Ownership: 1 AlwaysReplicateAsRoot: 0 + SynchronizeTransform: 1 + ActiveSceneSynchronization: 0 + SceneMigrationSynchronization: 1 + SpawnWithObservers: 1 DontDestroyWithOwner: 0 AutoObjectParentSync: 1 + SyncOwnerTransformWhenParented: 1 + AllowOwnerToParent: 0 --- !u!114 &5628721071472145353 MonoBehaviour: m_ObjectHideFlags: 0 @@ -545,6 +652,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 65054225619821f498f40221095d527e, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 m_ServerAuthText: {fileID: 554345019093650043} m_OwnerAuthText: {fileID: 6408045601871479648} --- !u!114 &2564457647300903827 @@ -559,6 +667,21 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: e96cb6065543e43c4a752faaa1468eb1, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 + NetworkTransformExpanded: 0 + AutoOwnerAuthorityTickOffset: 1 + PositionInterpolationType: 0 + RotationInterpolationType: 0 + ScaleInterpolationType: 0 + PositionLerpSmoothing: 1 + PositionMaxInterpolationTime: 0.1 + RotationLerpSmoothing: 1 + RotationMaxInterpolationTime: 0.1 + ScaleLerpSmoothing: 1 + ScaleMaxInterpolationTime: 0.1 + AuthorityMode: 0 + TickSyncChildren: 0 + UseUnreliableDeltas: 0 SyncPositionX: 1 SyncPositionY: 1 SyncPositionZ: 1 @@ -571,8 +694,13 @@ MonoBehaviour: PositionThreshold: 0.001 RotAngleThreshold: 0.01 ScaleThreshold: 0.01 + UseQuaternionSynchronization: 0 + UseQuaternionCompression: 0 + UseHalfFloatPrecision: 0 InLocalSpace: 0 + SwitchTransformSpaceWhenParented: 0 Interpolate: 0 + SlerpPosition: 0 --- !u!1 &3953046479900177945 GameObject: m_ObjectHideFlags: 0 @@ -599,6 +727,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3953046479900177945} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -15, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} @@ -606,11 +735,10 @@ Transform: m_Children: - {fileID: 2505406658094773853} m_Father: {fileID: 3214090169675393152} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!95 &6047057957339659638 Animator: - serializedVersion: 4 + serializedVersion: 7 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -624,10 +752,12 @@ Animator: m_ApplyRootMotion: 0 m_LinearVelocityBlending: 0 m_StabilizeFeet: 0 + m_AnimatePhysics: 0 m_WarningMessage: m_HasTransformHierarchy: 1 m_AllowConstantClipSamplingOptimization: 1 - m_KeepAnimatorControllerStateOnDisable: 0 + m_KeepAnimatorStateOnDisable: 0 + m_WriteDefaultValuesOnDisable: 0 --- !u!114 &8104648428997222210 MonoBehaviour: m_ObjectHideFlags: 0 @@ -640,7 +770,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: b322be05414199349ae9d1aea28228a7, type: 3} m_Name: m_EditorClassIdentifier: - TestIterations: 20 + ShowTopMostFoldoutHeaderGroup: 1 --- !u!114 &1245349943772079751 MonoBehaviour: m_ObjectHideFlags: 0 @@ -653,26 +783,100 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: e8d0727d5ae3244e3b569694d3912374, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 + NetworkAnimatorExpanded: 0 + AuthorityMode: 0 + m_Animator: {fileID: 6047057957339659638} TransitionStateInfoList: - - Layer: 0 + - IsCrossFadeExit: 0 + Layer: 0 OriginatingState: 2081823275 DestinationState: -570305638 TransitionDuration: 0.30227518 TriggerNameHash: 1033918907 TransitionIndex: 1 - - Layer: 0 + - IsCrossFadeExit: 0 + Layer: 0 OriginatingState: 2081823275 DestinationState: -1509639022 TransitionDuration: 0.25 TriggerNameHash: 1033918907 TransitionIndex: 2 - - Layer: 1 + - IsCrossFadeExit: 0 + Layer: 1 OriginatingState: 2081823275 DestinationState: -623385122 TransitionDuration: 2.2832582 TriggerNameHash: -623385122 TransitionIndex: 0 - m_Animator: {fileID: 6047057957339659638} + - IsCrossFadeExit: 0 + Layer: 3 + OriginatingState: 2081823275 + DestinationState: -1829531531 + TransitionDuration: 0.25 + TriggerNameHash: 1080829965 + TransitionIndex: 0 + - IsCrossFadeExit: 0 + Layer: 3 + OriginatingState: 2081823275 + DestinationState: 705160537 + TransitionDuration: 0.25 + TriggerNameHash: 1080829965 + TransitionIndex: 1 + - IsCrossFadeExit: 0 + Layer: 3 + OriginatingState: 2081823275 + DestinationState: 801385362 + TransitionDuration: 0.25 + TriggerNameHash: 1080829965 + TransitionIndex: 2 + AnimatorParameterEntries: + ParameterEntries: + - name: Rotate + NameHash: 807753530 + Synchronize: 1 + ParameterType: 4 + - name: Pulse + NameHash: -623385122 + Synchronize: 1 + ParameterType: 9 + - name: TestFloat + NameHash: -758535706 + Synchronize: 1 + ParameterType: 1 + - name: TestInt + NameHash: -1682086748 + Synchronize: 1 + ParameterType: 3 + - name: TestBool + NameHash: 953368263 + Synchronize: 1 + ParameterType: 4 + - name: TestTrigger + NameHash: 1033918907 + Synchronize: 1 + ParameterType: 9 + - name: LateJoinTest + NameHash: -1545343146 + Synchronize: 1 + ParameterType: 4 + - name: Attack + NameHash: 1080829965 + Synchronize: 1 + ParameterType: 9 + - name: WeaponType + NameHash: -1936256502 + Synchronize: 1 + ParameterType: 3 + - name: Weapon + NameHash: 1855955664 + Synchronize: 1 + ParameterType: 3 + - name: ExcludeFromSync + NameHash: -1993481368 + Synchronize: 0 + ParameterType: 1 + AnimatorParametersExpanded: 1 --- !u!1 &4033769488171516769 GameObject: m_ObjectHideFlags: 0 @@ -699,13 +903,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4033769488171516769} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -0.514} m_LocalScale: {x: 0.1, y: 0.1, z: 0.1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7635925556881204871} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &6303887162499072930 MeshFilter: @@ -732,6 +936,9 @@ MeshRenderer: m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -765,9 +972,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4033769488171516769} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 1} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &4988239203845480744 @@ -796,13 +1011,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4988239203845480744} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0.505} m_LocalScale: {x: 0.1, y: 0.1, z: 0.1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7635925556881204871} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &6969857922933789850 MeshFilter: @@ -829,6 +1044,9 @@ MeshRenderer: m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -862,9 +1080,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4988239203845480744} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 1} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &8225275286638841897 @@ -898,7 +1124,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 3214090169133903717} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -978,7 +1203,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 3214090169133903717} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -1053,6 +1277,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8656650671630400802} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 10, y: 10, z: 10} @@ -1061,7 +1286,6 @@ Transform: - {fileID: 4145180303732600870} - {fileID: 1514700077442365156} m_Father: {fileID: 3604323723684300772} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &4441112472850329336 MeshFilter: @@ -1088,6 +1312,9 @@ MeshRenderer: m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -1121,9 +1348,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8656650671630400802} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000002, y: 1, z: 1.0000002} m_Center: {x: 0.0000019073493, y: 0.000000007450581, z: -0.000001907349} --- !u!1 &8778627084610397836 @@ -1152,13 +1387,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8778627084610397836} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -0.514} m_LocalScale: {x: 0.1, y: 0.1, z: 0.1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 2505406658094773853} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &5462393744046406360 MeshFilter: @@ -1185,6 +1420,9 @@ MeshRenderer: m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -1218,9 +1456,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8778627084610397836} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 1} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &9079811648883086489 @@ -1254,7 +1500,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 3214090169133903717} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} diff --git a/testproject/Assets/Tests/Runtime/Animation/AnimatorTestHelper.cs b/testproject/Assets/Tests/Runtime/Animation/AnimatorTestHelper.cs index e8130c68b4..6f5b60b227 100644 --- a/testproject/Assets/Tests/Runtime/Animation/AnimatorTestHelper.cs +++ b/testproject/Assets/Tests/Runtime/Animation/AnimatorTestHelper.cs @@ -108,6 +108,16 @@ public void UpdateParameters(ParameterValues parameterValues) m_Animator.SetBool("TestBool", parameterValues.BoolValue); } + public void UpdateExcludedParameter(float value) + { + m_Animator.SetFloat("ExcludeFromSync", value); + } + + public float GetExcludedParameter() + { + return m_Animator.GetFloat("ExcludeFromSync"); + } + public bool GetCurrentTriggerState() { return m_Animator.GetBool("TestTrigger"); diff --git a/testproject/Assets/Tests/Runtime/Animation/NetworkAnimatorTests.cs b/testproject/Assets/Tests/Runtime/Animation/NetworkAnimatorTests.cs index df4124e5eb..474c9dabbe 100644 --- a/testproject/Assets/Tests/Runtime/Animation/NetworkAnimatorTests.cs +++ b/testproject/Assets/Tests/Runtime/Animation/NetworkAnimatorTests.cs @@ -157,6 +157,39 @@ private bool ParameterValuesMatch(OwnerShipMode ownerShipMode, AuthoritativeMode return true; } + private bool ExcludedParameterValuesDoNotMatch() + { + var objectToUpdate = AnimatorTestHelper.ServerSideInstance; + var excludedParameterValue = objectToUpdate.GetExcludedParameter(); + if (m_AuthoritativeMode == AuthoritativeMode.OwnerAuth) + { + objectToUpdate = m_OwnerShipMode == OwnerShipMode.ClientOwner ? AnimatorTestHelper.ClientSideInstances[m_ClientNetworkManagers[0].LocalClientId] : AnimatorTestHelper.ServerSideInstance; + excludedParameterValue = objectToUpdate.GetExcludedParameter(); + if (m_OwnerShipMode == OwnerShipMode.ClientOwner) + { + if (excludedParameterValue == AnimatorTestHelper.ServerSideInstance.GetExcludedParameter()) + { + return false; + } + } + } + + foreach (var animatorTestHelper in AnimatorTestHelper.ClientSideInstances) + { + if (objectToUpdate == animatorTestHelper.Value) + { + continue; + } + var clientExcludedParameter = animatorTestHelper.Value.GetExcludedParameter(); + if (clientExcludedParameter == excludedParameterValue) + { + return false; + } + } + return true; + } + + public enum OwnerShipMode { ServerOwner, @@ -268,6 +301,42 @@ public void ParameterUpdateTests() VerboseDebug($" ------------------ Parameter Test [{m_OwnerShipMode}] Stopping ------------------ "); } + [Test] + public void ParameterExcludedTests() + { + VerboseDebug($" ++++++++++++++++++ Parameter Excluded Test [{m_OwnerShipMode}] Starting ++++++++++++++++++ "); + + // Spawn our test animator object + var objectInstance = SpawnPrefab(m_OwnerShipMode == OwnerShipMode.ClientOwner, m_AuthoritativeMode); + + // Wait for it to spawn server-side + var success = WaitForConditionOrTimeOutWithTimeTravel(() => AnimatorTestHelper.ServerSideInstance != null); + Assert.True(success, $"Timed out waiting for the server-side instance of {GetNetworkAnimatorName(m_AuthoritativeMode)} to be spawned!"); + + // Wait for it to spawn client-side + success = WaitForConditionOrTimeOutWithTimeTravel(() => AnimatorTestHelper.ClientSideInstances.ContainsKey(m_ClientNetworkManagers[0].LocalClientId)); + Assert.True(success, $"Timed out waiting for the client-side instance of {GetNetworkAnimatorName(m_AuthoritativeMode)} to be spawned!"); + + if (m_AuthoritativeMode == AuthoritativeMode.OwnerAuth) + { + var objectToUpdate = m_OwnerShipMode == OwnerShipMode.ClientOwner ? AnimatorTestHelper.ClientSideInstances[m_ClientNetworkManagers[0].LocalClientId] : AnimatorTestHelper.ServerSideInstance; + // Set the excluded parameter value via the owner instance + objectToUpdate.UpdateExcludedParameter(Random.Range(1.5f, 100.0f)); + } + else + { + // Set the excluded parameter value via the server instance + AnimatorTestHelper.ServerSideInstance.UpdateExcludedParameter(Random.Range(1.5f, 100.0f)); + } + + TimeTravel(0.5, 60); + // Wait for the client side to update to the new parameter values + success = WaitForConditionOrTimeOutWithTimeTravel(ExcludedParameterValuesDoNotMatch); + Assert.True(success, $"The excluded parameter was synchronized!"); + VerboseDebug($" ------------------ Parameter Test [{m_OwnerShipMode}] Stopping ------------------ "); + } + + private bool AllTriggersDetected(OwnerShipMode ownerShipMode) { diff --git a/testproject/Assets/Tests/Runtime/Animation/Resources/AnimatorObject.prefab b/testproject/Assets/Tests/Runtime/Animation/Resources/AnimatorObject.prefab index 9ef650701f..e7caee94cc 100644 --- a/testproject/Assets/Tests/Runtime/Animation/Resources/AnimatorObject.prefab +++ b/testproject/Assets/Tests/Runtime/Animation/Resources/AnimatorObject.prefab @@ -386,6 +386,8 @@ MonoBehaviour: SpawnWithObservers: 1 DontDestroyWithOwner: 0 AutoObjectParentSync: 1 + SyncOwnerTransformWhenParented: 1 + AllowOwnerToParent: 0 --- !u!95 &6515743261518512780 Animator: serializedVersion: 7 @@ -420,6 +422,10 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: e8d0727d5ae3244e3b569694d3912374, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 + NetworkAnimatorExpanded: 0 + AuthorityMode: 0 + m_Animator: {fileID: 6515743261518512780} TransitionStateInfoList: - IsCrossFadeExit: 0 Layer: 0 @@ -463,7 +469,53 @@ MonoBehaviour: TransitionDuration: 0.25 TriggerNameHash: 1080829965 TransitionIndex: 2 - m_Animator: {fileID: 6515743261518512780} + AnimatorParameterEntries: + ParameterEntries: + - name: Rotate + NameHash: 807753530 + Synchronize: 1 + ParameterType: 4 + - name: Pulse + NameHash: -623385122 + Synchronize: 1 + ParameterType: 9 + - name: TestFloat + NameHash: -758535706 + Synchronize: 1 + ParameterType: 1 + - name: TestInt + NameHash: -1682086748 + Synchronize: 1 + ParameterType: 3 + - name: TestBool + NameHash: 953368263 + Synchronize: 1 + ParameterType: 4 + - name: TestTrigger + NameHash: 1033918907 + Synchronize: 1 + ParameterType: 9 + - name: LateJoinTest + NameHash: -1545343146 + Synchronize: 1 + ParameterType: 4 + - name: Attack + NameHash: 1080829965 + Synchronize: 1 + ParameterType: 9 + - name: WeaponType + NameHash: -1936256502 + Synchronize: 1 + ParameterType: 3 + - name: Weapon + NameHash: 1855955664 + Synchronize: 1 + ParameterType: 3 + - name: ExcludeFromSync + NameHash: -1993481368 + Synchronize: 0 + ParameterType: 1 + AnimatorParametersExpanded: 0 --- !u!114 &-8876216387850298050 MonoBehaviour: m_ObjectHideFlags: 0 @@ -476,6 +528,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 48b31fae64dc3c14c98e3c5d09cb1269, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 --- !u!114 &5194088446451573146 MonoBehaviour: m_ObjectHideFlags: 0 @@ -488,3 +541,4 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: a915cfb2e4f748e4f9526a8bf5ee84f2, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 diff --git a/testproject/Assets/Tests/Runtime/Animation/Resources/OwnerAnimatorObject.prefab b/testproject/Assets/Tests/Runtime/Animation/Resources/OwnerAnimatorObject.prefab index 56b679ebb2..d737209912 100644 --- a/testproject/Assets/Tests/Runtime/Animation/Resources/OwnerAnimatorObject.prefab +++ b/testproject/Assets/Tests/Runtime/Animation/Resources/OwnerAnimatorObject.prefab @@ -386,6 +386,8 @@ MonoBehaviour: SpawnWithObservers: 1 DontDestroyWithOwner: 0 AutoObjectParentSync: 1 + SyncOwnerTransformWhenParented: 1 + AllowOwnerToParent: 0 --- !u!95 &6515743261518512780 Animator: serializedVersion: 7 @@ -420,6 +422,10 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: f6a3556fb5f3bee4e8a0fb88acff87ff, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 + NetworkAnimatorExpanded: 0 + AuthorityMode: 1 + m_Animator: {fileID: 6515743261518512780} TransitionStateInfoList: - IsCrossFadeExit: 0 Layer: 0 @@ -463,7 +469,53 @@ MonoBehaviour: TransitionDuration: 0.25 TriggerNameHash: 1080829965 TransitionIndex: 2 - m_Animator: {fileID: 6515743261518512780} + AnimatorParameterEntries: + ParameterEntries: + - name: Rotate + NameHash: 807753530 + Synchronize: 1 + ParameterType: 4 + - name: Pulse + NameHash: -623385122 + Synchronize: 1 + ParameterType: 9 + - name: TestFloat + NameHash: -758535706 + Synchronize: 1 + ParameterType: 1 + - name: TestInt + NameHash: -1682086748 + Synchronize: 1 + ParameterType: 3 + - name: TestBool + NameHash: 953368263 + Synchronize: 1 + ParameterType: 4 + - name: TestTrigger + NameHash: 1033918907 + Synchronize: 1 + ParameterType: 9 + - name: LateJoinTest + NameHash: -1545343146 + Synchronize: 1 + ParameterType: 4 + - name: Attack + NameHash: 1080829965 + Synchronize: 1 + ParameterType: 9 + - name: WeaponType + NameHash: -1936256502 + Synchronize: 1 + ParameterType: 3 + - name: Weapon + NameHash: 1855955664 + Synchronize: 1 + ParameterType: 3 + - name: ExcludeFromSync + NameHash: -1993481368 + Synchronize: 0 + ParameterType: 1 + AnimatorParametersExpanded: 0 --- !u!114 &-8876216387850298050 MonoBehaviour: m_ObjectHideFlags: 0 @@ -476,6 +528,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 48b31fae64dc3c14c98e3c5d09cb1269, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 --- !u!114 &5194088446451573146 MonoBehaviour: m_ObjectHideFlags: 0 @@ -488,3 +541,4 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: a915cfb2e4f748e4f9526a8bf5ee84f2, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 diff --git a/testproject/Assets/Tests/Runtime/Animation/Resources/PlayerCheerOwnerAuth.prefab b/testproject/Assets/Tests/Runtime/Animation/Resources/PlayerCheerOwnerAuth.prefab index c243ddc5b4..273adf7194 100644 --- a/testproject/Assets/Tests/Runtime/Animation/Resources/PlayerCheerOwnerAuth.prefab +++ b/testproject/Assets/Tests/Runtime/Animation/Resources/PlayerCheerOwnerAuth.prefab @@ -377,6 +377,8 @@ MonoBehaviour: SpawnWithObservers: 1 DontDestroyWithOwner: 0 AutoObjectParentSync: 1 + SyncOwnerTransformWhenParented: 1 + AllowOwnerToParent: 0 --- !u!114 &2033189052978358381 MonoBehaviour: m_ObjectHideFlags: 0 @@ -389,6 +391,9 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: f6a3556fb5f3bee4e8a0fb88acff87ff, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 + NetworkAnimatorExpanded: 0 + AuthorityMode: 0 TransitionStateInfoList: - IsCrossFadeExit: 0 Layer: 0 @@ -404,6 +409,15 @@ MonoBehaviour: TransitionDuration: 0.25 TriggerNameHash: 2081823275 TransitionIndex: 0 + AnimatorParameterEntries: + - Synchronize: 1 + NameHash: -813790127 + Name: Cheer + ParameterType: 9 + - Synchronize: 1 + NameHash: 2081823275 + Name: Idle + ParameterType: 9 m_Animator: {fileID: 4217478864398958195} --- !u!114 &6813822210364307126 MonoBehaviour: @@ -417,6 +431,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 77114a4761fbbf04ebafd6a884d3d312, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 --- !u!114 &-126005763244726388 MonoBehaviour: m_ObjectHideFlags: 0 @@ -429,6 +444,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: a915cfb2e4f748e4f9526a8bf5ee84f2, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 --- !u!114 &8698027682878108555 MonoBehaviour: m_ObjectHideFlags: 0 @@ -441,3 +457,4 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 48b31fae64dc3c14c98e3c5d09cb1269, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 diff --git a/testproject/Assets/Tests/Runtime/Animation/Resources/PlayerCheerServerAuth.prefab b/testproject/Assets/Tests/Runtime/Animation/Resources/PlayerCheerServerAuth.prefab index 3b8fc9dd68..6cb3dde6ae 100644 --- a/testproject/Assets/Tests/Runtime/Animation/Resources/PlayerCheerServerAuth.prefab +++ b/testproject/Assets/Tests/Runtime/Animation/Resources/PlayerCheerServerAuth.prefab @@ -269,6 +269,8 @@ MonoBehaviour: SpawnWithObservers: 1 DontDestroyWithOwner: 0 AutoObjectParentSync: 1 + SyncOwnerTransformWhenParented: 1 + AllowOwnerToParent: 0 --- !u!114 &6975563805585028340 MonoBehaviour: m_ObjectHideFlags: 0 @@ -281,6 +283,9 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: e8d0727d5ae3244e3b569694d3912374, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 + NetworkAnimatorExpanded: 0 + AuthorityMode: 0 TransitionStateInfoList: - IsCrossFadeExit: 0 Layer: 0 @@ -296,6 +301,15 @@ MonoBehaviour: TransitionDuration: 0.25 TriggerNameHash: 2081823275 TransitionIndex: 0 + AnimatorParameterEntries: + - Synchronize: 1 + NameHash: -813790127 + Name: Cheer + ParameterType: 9 + - Synchronize: 1 + NameHash: 2081823275 + Name: Idle + ParameterType: 9 m_Animator: {fileID: 7612458143371274009} --- !u!114 &9047519417049631294 MonoBehaviour: @@ -309,6 +323,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 77114a4761fbbf04ebafd6a884d3d312, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 --- !u!114 &-967079540368916851 MonoBehaviour: m_ObjectHideFlags: 0 @@ -321,6 +336,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: a915cfb2e4f748e4f9526a8bf5ee84f2, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 --- !u!114 &707713165077007192 MonoBehaviour: m_ObjectHideFlags: 0 @@ -333,6 +349,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 48b31fae64dc3c14c98e3c5d09cb1269, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 --- !u!1 &8445200702028649601 GameObject: m_ObjectHideFlags: 0