Article directory
Preface
This tutorial mainly talks about a very important component for synchronizing the position information of two playersNetworkTransform
, as well as the function and description of this component.
What is NetworkTransform
Synchronizing objects Transform
is one of the most common tasks in Netcode's multiplayer games today. The concept seems simple:
- Determine the transformation axis you want to synchronize.
- Serialize these values.
- Send the serialized value as a message to all other connected clients.
- Process the message and deserialize the value.
- Apply these values to the appropriate axis.
At first glance, the tasks listed above may seem relatively simple, but when you start implementing each task, almost any experienced Netcode software engineer will agree: it can get complicated quickly.
For example, the tasks listed above do not take into account the following:
- Who controls synchronization (i.e. each client, server, or possibly both depending on what is being synchronized)?
- At what frequency should these values be synchronized, and what should be the logic to determine when these values need to be synchronized?
- If you have a complex parent-child hierarchy (a parent transform with one or more child transforms), should you synchronize world space axis values or local space axis values?
- How to optimize the bandwidth cost of each transform update?
Fortunately, Netcode for GameObjects (NGO) provides an implementation of the NetworkTransform
component that handles some of the tricky aspects of transform synchronization and can be accessed via the inspector in the editor. Easily configure the properties accessible in .
Player movement script
Package Manager
--> Select the package next to the number +
in the upper left cornerUnity Registry
, search Cinemachine
download and import a>
MovePlayerMove
toScripts
and add the script on the prefabPlayer
NetworkTransform field explanation
Synchronizing (“Syncing”)
This is used to specify synchronization position, rotation, and scaling. Just check which values need to be synchronized.
Under normal circumstances, there is no need to synchronize all transformation values of GameObject. For example, if the GameObject's scaling never changes, it can be disabled at Syncing Scale
in the panel. Disabling synchronization saves CPU costs and network bandwidth.
Thresholds
You can use the threshold value to set a minimum threshold. This can be used to reduce the frequency of synchronized updates by only synchronizing changes greater than or equal to the threshold value (changes below the threshold will not be synchronized). For example:
IfNetworkTransform
interpolation is enabled (Interpolate
), you may find that you can reduce the resolution of the position threshold (increase the position threshold value) without Affects the "smoothness" of object motion, while also reducing the frequency of position updates (reducing the bandwidth cost per instance). Increasing the threshold resolution (lowering the position threshold value) increases the potential frequency with which an object's position is synchronized (potentially increasing the bandwidth cost per instance).
Threshold values are not synchronized but can be updated on
authoritative
instances. This should be kept in mind when using instances in Ownerauthoritative
mode, as changing ownership will use whatever value is currently set on the new owner instance. If you plan to change the threshold value at runtime and plan to change ownership, you may need to synchronize the threshold value.
Local space
By default, NetworkTransform
is transformed by 世界空间同步
the object. In Local Space
Configuration option allows you to synchronize transforms in local space instead. The child object's local space axis values (mainly position and rotation) are always offsets relative to the parent transform. The world space axis value of the child object includes the axis value of the parent object.
Using local space on a parented NetworkTransform
improves transform synchronization when the object is re-parented, because re-parenting does not change the object's local space transform. But it will change the world space position.
authoritative
instances do synchronize changes to LocalSpace properties. Therefore, you can adjust this property onauthoritative
instances at runtime, while non-authoritative
instances will update automatically.
Interpolation
Interpolation (·Interpolation·) is enabled by default, which is the recommended setting if you want smooth transitions between transform updates on nonauthoritative
instances. Interpolation buffers incoming state updates, which may introduce a slight delay between authoritative
instances and non-authoritative
instances. When the interpolation property is disabled, transformation changes are immediately applied to non-authoritative
instances, which may cause visual "jitter" or appear to "jump" when latency is high "Status updates to new applications.
Changing interpolated properties at runtime on an authoritative instance will be synchronized with all non-authoritative instances.
NetworkTransform
The component only interpolates on the client side. To achieve smoother movement on the host or server, users may also wish to implement interpolation on the server side. Although the server is not affected by network-induced jitter, it may still experience some stuttering locally (e.g., low physical update rate movement inFixedUpdate
).
Slerp Position
When this property is set and interpolation is also enabled (Interpolation
), non-authoritative
instances will passSlerp instead of Lerp interpolating towards their target positions. Typically this can be used when an object follows a circular and/or spline-based motion path to preserve the curvature of that path. Since "lerp" interpolation between two points produces a linear progression on the line between the two points, in some cases the frequency of position state updates may result in a loss of the object's motion curve.
Use Quaternion Synchronization
By default, Euler angle values are used to synchronize rotation increments. For many situations, using Euler angle values may be sufficient. However, there are situations where synchronizing Euler angle increments can produce undesirable results. One situation is when you have complex nested NetworkTransforms where the rotation is different between the parent and child transforms. When you combine interpolation together (remember that interpolation is buffered and there is an inherent delay between the non-authoritative current rotation and the target rotation), the adjustments that happen immediately in Quaternion handle more complex transformation related issues ( Such as Jimba lock, etc.).
When Quaternion synchronization is enabled, the authoritative instance still compares the Euler axis values against the threshold value to determine if the transformed rotation needs to be updated, but the entire Quaternion is updated, not just the Euler axis where the change was detected. This means that it is ensured that the correct rotation will be applied to non-authoritative instances, and that more complex issues that may arise when using Euler angles have been taken into account.
Quaternion synchronization comes at a cost. It increases the bandwidth cost by 16 bytes per instance to handle more complex rotation problems that are more common when using nested NetworkTransforms (one or more parent transforms and one or more child transforms). However, when you enable theUse Quaternion Synchronization
property, you will notice the Synchronize Axis Selection checkbox and a newUse Quaternion Compression
property appear:
When enabled
Use Quaternion Synchronization
, the Rotate synchronized axes checkbox is no longer available (because synchronously transformed quaternions will always update all rotation axes), whereasUse Quaternion Compression
Become a visible option.
Use Quaternion Compression
Since synchronizing quaternions may increaseNetworkTransform
rotate the bandwidth cost of state updates, there are two ways to reduce quaternion synchronization Total bandwidth cost of:
-
Quaternion compression (
Quaternion Compression
): This provides the highest compression ratio (reduced to 4 bytes per update), but the precision loss is slightly higher than half floating point precision. -
Half floating point precision (
Half Float Precision
): When quaternion compression is enabled and quaternion compression is disabled, this provides a moderate level of alternative compression (reduced to 8 bytes per update), Less precise than a full floating point value, but more precise than quaternion compression.
Quaternion compression is based on the least-triple algorithm and can be used when rotation accuracy is less important than bandwidth cost. You may have attached objects/projectiles that require some form of rotational synchronization, but don't need to be perfectly aligned in the overall scheme of the project.
If bandwidth cost and precision are both issues, the alternative recommended compression method is half-floating point precision. Additionally, it is recommended to try different compression options, you may find that the partial loss of accuracy is perfectly acceptable for your project's needs (and can reduce the overall bandwidth cost across all instances by up to 50% without using the full accuracy).
This attribute value can be updated while the authoritative instance is running and will be synchronized to all non-authoritative instances. Reminder: Updating this value at runtime on an authoritative
instance will result in a full synchronization of NetworkTransform
's interpolators for all non-authoritative
instances will be reset.
Use Half Float Precision
Enabling this property will convert any transform axis value from4-byte float to 2-byte half floating point number, but at the expense ofprecision loss. When this option is enabled, all transform axes marked as synchronized will use half floating point precision. However, there are some unique aspects to half-floating point precision regarding position and rotation.
Due to the loss of precision, position status updates only provide position deltas relative to the last known complete position. NetworkDeltaPosition
The serializable structure keeps track of the current delta between the last known complete position and the current delta offset from the last known complete position. Additionally, NetworkDeltaPosition
automatically corrects for precision loss when sending updates. The accuracy loss from the previous update will be included in the next location update. In other words, a non-authoritative
instance can potentially be associated with an authoritative
instance for the duration of 1 tick period or until the next transform state update is received. Score delta from each applied update. Additionally, NetworkDeltaPosition
fills the gap between the maximum half-float value and the maximum bounds of Unity's world space (global/project scaling dependent).
Recommended Unity world space units per second:
The maximum increment per update should not exceed 64 Unity world space units. If you use the default tick (30), then the object should not move at a speed equal to or exceeding 1920 Unity world space units per second (i.e. 30 x 64). For reference, the default camera's far clipping plane is 1000 Unity world space units, which means that an object moving at 1920 Unity world space units may not be visually detected in the rendering frustum, or may appear briefly in the rendering frustum. "Flashing" appears.
When enabledUse Quaternion Synchronization
and Use Half Float Precision
and disabledUse Quaternion Compression
, quaternion values are synchronized via the HalfVector4 serializable structure , where each axis value (x, y, z, and w) is stored as a half-floating point value. This means that each rotation update is reduced from 16 bytes at full precision to 8 bytes at a time. For rotations, using half floating point precision provides better accuracy than quaternion compression at twice the bandwidth cost, but only half the cost of full precision.
When enabled Use Quaternion Synchronization
, Use Half Float Precision
and Use Quaternion Compression
, quaternion compression will be used instead of half floating point precision of rotation.
When updates are made on an authoritative
instance, all of these properties will be synchronized to non-authoritative
instances.
Authority modes
Server Authoritative Mode
By default, NetworkTransform runs in serverauthoritative
mode. This means that the server side detects the transform axis to be synchronized (marked as synchronized) and pushes it to the connected client. This also means that any changes to the transform axis values will be overwritten by theauthoritative
state (in this case the server-side transform state).
There is another concept to keep in mind regarding the transformation values of axis synchronization and initial synchronization. Any axes not marked for synchronization will still be updated to the initial state of when NetworkObject
is generated or the client synchronizes for the first time. authoritative
For example:
Suppose you only marked the position and rotation axes for synchronization, but excluded all scaling on theNetworkTransform
component's network prefab axis. When you build an instance of a network prefab, the initial authoritative scaling value is synchronized at build time. From that point on, nonauthoritative
instances (client instances in this case) will keep the same scale axis values, even if they are no longer updated.
Owner Authoritative Mode
(aka: ClientNetworkTransform)
Server-side authoritativeNetworkTransform
provides a balance between synchronizing transformations and applying updates on all connected clients. However, sometimes you want a specificNetworkObject
(usually a player) to have its position updated immediately on the client. The owner authority of NetworkTransform
is determined by the NetworkTransform.OnIsServerAuthoritative
method, which is called when the NetworkTransform
component is first initialized. If the method returns true (the default), it will be initialized to the server's authoritative NetworkTransform
. If false is returned, it will be initialized to the owner's (also known as ). This can be accomplished by deriving from , overriding the virtual method, and returning false as in the following code example: authoritative
NetworkTransform
ClientNetworkTransform
NetworkTransform
OnIsServerAuthoritative
using Unity.Netcode.Components;
using UnityEngine;
namespace Unity.Multiplayer.Samples.Utilities.ClientAuthority
{
/// <summary>
/// 用于同步客户端端的变换更改。这包括主机。不支持纯服务器作为所有者,请使用NetworkTransform。
/// 用于那些始终由服务器拥有的Transform。
/// </summary>
[DisallowMultipleComponent]
public class ClientNetworkTransform : NetworkTransform
{
/// <summary>
/// Used to determine who can write to this transform. Owner client only.
/// This imposes state to the server. This is putting trust on your clients. Make sure no security-sensitive features use this transform.
/// </summary>
protected override bool OnIsServerAuthoritative()
{
return false;
}
}
}
ClientNetworkTransform
Example:
SelectWindow
--> Package Manager
to open the package manager.
Add(+)
--> 从git URL
Add...
Copy and paste the following Git URL:https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop.git?path=/Packages/ com.unity.multiplayer.samples.coop#main
or modify the project'smanifest.json
, add
"com.unity.multiplayer.samples.coop": "https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop.git?path=/Packages/com.unity.multiplayer.samples.coop#main"
This Transform synchronizes the owner client's position with the server and all other clients, enabling client authoritative
gameplay.
Additional Virtual Methods of Interest
NetworkTransform.OnAuthorityPushTransformState
: This virtual method is called when the authoritative instance is pushing a new NetworkTransformState
to a non-authoritative
instance. This can be used to more precisely determine updated values for nonauthoritative
instances for prediction-related tasks.
NetworkTransform.OnNetworkTransformStateUpdated
: This virtual method is called when a non-authoritative instance receives a pushed update from an authoritative
instance. This can be used to more precisely determine updated values for noninstances for prediction-related tasks. NetworkTransformState
authoritative
NetworkTransform.Awake
: To provide the ability to customize initialization, this method has been made virtual. If you override this method, it is recommended to call base.Awake()
first.
NetworkTransform.OnInitialize
: This virtual method is called when the related NetworkObject is first generated and when ownership changes.
NetworkTransform.Update
: To provide you with the ability to make any customizations to the derivedNetworkTransform
class, this method has been made virtual. If you override this method, it is required to be called for all non-authoritative
instances of base.Update()
, but not required for authoritative
instances.
Afterword
WithNetworkTransform
this component can save a lot of effort for location synchronization. The practical application ofNetworkTransform
will be discussed later
Official link
https://docs-multiplayer.unity3d.com/netcode/current/components/networktransform/