using System.Runtime.CompilerServices; using Unity.Mathematics; using UnityEngine; namespace Unity.Netcode.Components { /// /// Used to synchromnize delta position when half float precision is enabled /// public struct NetworkDeltaPosition : INetworkSerializable { internal const float MaxDeltaBeforeAdjustment = 64f; /// /// The HalfVector3 used to synchronize the delta in position /// public HalfVector3 HalfVector3; internal Vector3 CurrentBasePosition; internal Vector3 PrecisionLossDelta; internal Vector3 HalfDeltaConvertedBack; internal Vector3 PreviousPosition; internal Vector3 DeltaPosition; internal int NetworkTick; /// /// The serialization implementation of /// public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter { HalfVector3.NetworkSerialize(serializer); } /// /// Gets the full precision value of Vector3 position while also potentially updating the current base position. /// /// Use the current network tick value. /// The full position as a . [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector3 ToVector3(int networkTick) { // When synchronizing, it is possible to have a state update arrive // for the same synchronization network tick. Under this scenario, // we only want to return the existing CurrentBasePosition + DeltaPosition // values and not process the X, Y, or Z values. // (See the constructors below) if (networkTick == NetworkTick) { return CurrentBasePosition + DeltaPosition; } for (int i = 0; i < HalfVector3.Length; i++) { if (HalfVector3.AxisToSynchronize[i]) { DeltaPosition[i] = Mathf.HalfToFloat(HalfVector3.Axis[i].value); // If we exceed or are equal to the maximum delta value then we need to // apply the delta to the CurrentBasePosition value and reset the delta // position for the axis. if (Mathf.Abs(DeltaPosition[i]) >= MaxDeltaBeforeAdjustment) { CurrentBasePosition[i] += DeltaPosition[i]; DeltaPosition[i] = 0.0f; HalfVector3.Axis[i] = half.zero; } } } return CurrentBasePosition + DeltaPosition; } /// /// Returns the current base position (excluding the delta position offset). /// /// The current base position as a [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector3 GetCurrentBasePosition() { return CurrentBasePosition; } /// /// Returns the full position which includes the delta offset position. /// /// The full position as a . [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector3 GetFullPosition() { return CurrentBasePosition + DeltaPosition; } /// /// The half float vector3 version of the current delta position. /// /// /// Only applies to the authoritative side for instances. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector3 GetConvertedDelta() { return HalfDeltaConvertedBack; } /// /// The full precision current delta position. /// /// /// Authoritative: Will have no precision loss /// Non-Authoritative: Has the current network tick's loss of precision. /// Precision loss adjustments are one network tick behind on the /// non-authoritative side. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector3 GetDeltaPosition() { return DeltaPosition; } /// /// Updates the position delta based off of the current base position. /// /// The full precision value to (converted to half floats) used to determine the delta offset positon. /// Set the current network tick value when updating. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void UpdateFrom(ref Vector3 vector3, int networkTick) { NetworkTick = networkTick; DeltaPosition = (vector3 + PrecisionLossDelta) - CurrentBasePosition; for (int i = 0; i < HalfVector3.Length; i++) { if (HalfVector3.AxisToSynchronize[i]) { HalfVector3.Axis[i] = math.half(DeltaPosition[i]); HalfDeltaConvertedBack[i] = Mathf.HalfToFloat(HalfVector3.Axis[i].value); PrecisionLossDelta[i] = DeltaPosition[i] - HalfDeltaConvertedBack[i]; if (Mathf.Abs(HalfDeltaConvertedBack[i]) >= MaxDeltaBeforeAdjustment) { CurrentBasePosition[i] += HalfDeltaConvertedBack[i]; HalfDeltaConvertedBack[i] = 0.0f; DeltaPosition[i] = 0.0f; } } } for (int i = 0; i < HalfVector3.Length; i++) { if (HalfVector3.AxisToSynchronize[i]) { PreviousPosition[i] = vector3[i]; } } } /// /// Constructor /// /// The initial axial values (converted to half floats) when instantiated. /// Set the network tick value to the current network tick when instantiating. /// The axis to be synchronized. public NetworkDeltaPosition(Vector3 vector3, int networkTick, bool3 axisToSynchronize) { NetworkTick = networkTick; CurrentBasePosition = vector3; PreviousPosition = vector3; PrecisionLossDelta = Vector3.zero; DeltaPosition = Vector3.zero; HalfDeltaConvertedBack = Vector3.zero; HalfVector3 = new HalfVector3(vector3, axisToSynchronize); UpdateFrom(ref vector3, networkTick); } /// /// Constructor that defaults to all axis being synchronized. /// /// The initial axial values (converted to half floats) when instantiated. /// Set the network tick value to the current network tick when instantiating. public NetworkDeltaPosition(Vector3 vector3, int networkTick) : this(vector3, networkTick, math.bool3(true)) { } /// /// Constructor /// /// The initial x axis (converted to half float) value when instantiated. /// The initial y axis (converted to half float) value when instantiated. /// The initial z axis (converted to half float) value when instantiated. /// Set the network tick value to the current network tick when instantiating. /// The axis to be synchronized. public NetworkDeltaPosition(float x, float y, float z, int networkTick, bool3 axisToSynchronize) : this(new Vector3(x, y, z), networkTick, axisToSynchronize) { } /// /// Constructor /// /// The initial x axis (converted to half float) value when instantiated. /// The initial y axis (converted to half float) value when instantiated. /// The initial z axis (converted to half float) value when instantiated. /// Set the network tick value to the current network tick when instantiating. public NetworkDeltaPosition(float x, float y, float z, int networkTick) : this(new Vector3(x, y, z), networkTick, math.bool3(true)) { } } }