using GameKit.Dependencies.Utilities; using UnityEngine; using UnityEngine.Scripting; namespace FishNet.Object.Prediction { /// /// Used to make calculations and perform actions in moving transforms over time. /// [Preserve] public struct MoveRates { /// /// Rate at which to move Position. /// public float Position; /// /// Rate at which to move Rotation. /// public float Rotation; /// /// Rate at which to move Scale. /// public float Scale; /// /// Time remaining until the move is complete. /// public float TimeRemaining; /// /// Value used when data is not set. /// public const float UNSET_VALUE = float.NegativeInfinity; /// /// Value used when move rate should be instant. /// public const float INSTANT_VALUE = float.PositiveInfinity; /// /// True if any data is set. Once set, this will remain true until ResetState is called. /// public bool IsValid { get; private set; } public MoveRates(float value) : this() { Position = value; Rotation = value; Scale = value; IsValid = true; } public MoveRates(float position, float rotation) : this() { Position = position; Rotation = rotation; Scale = INSTANT_VALUE; IsValid = true; } public MoveRates(float position, float rotation, float scale) : this() { Position = position; Rotation = rotation; Scale = scale; IsValid = true; } public MoveRates(float position, float rotation, float scale, float timeRemaining) { Position = position; Rotation = rotation; Scale = scale; TimeRemaining = timeRemaining; IsValid = true; } /// /// True if a positional move rate is set. /// public bool IsPositionSet => (Position != UNSET_VALUE); /// /// True if rotation move rate is set. /// public bool IsRotationSet => (Rotation != UNSET_VALUE); /// /// True if a scale move rate is set. /// public bool IsScaleSet => (Scale != UNSET_VALUE); /// /// True if position move rate should be instant. /// public bool IsPositionInstantValue => (Position == INSTANT_VALUE); /// /// True if rotation move rate should be instant. /// public bool IsRotationInstantValue => (Rotation == INSTANT_VALUE); /// /// True if scale move rate should be instant. /// public bool IsScaleInstantValue => (Scale == INSTANT_VALUE); /// /// Sets all rates to instant. /// public void SetInstantRates() => Update(INSTANT_VALUE); /// /// Sets all rates to the same value. /// public void Update(float value) => Update(value, value, value); /// /// Sets rates for each property. /// public void Update(float position, float rotation, float scale) { Position = position; Rotation = rotation; Scale = scale; IsValid = true; } /// /// Sets rates for each property. /// public void Update(float position, float rotation, float scale, float timeRemaining) { Position = position; Rotation = rotation; Scale = scale; TimeRemaining = timeRemaining; IsValid = true; } /// /// Updates to new values. /// public void Update(MoveRates moveRates) => Update(moveRates.Position, moveRates.Rotation, moveRates.Scale, moveRates.TimeRemaining); /// /// Updates to new values. /// public void Update(MoveRatesCls moveRates) => Update(moveRates.Position, moveRates.Rotation, moveRates.Scale, moveRates.TimeRemaining); /// /// Resets to unset values. /// public void ResetState() { Update(MoveRates.UNSET_VALUE, MoveRates.UNSET_VALUE, MoveRates.UNSET_VALUE, timeRemaining: 0f); IsValid = false; } /// /// Returns a new MoveRates based on previous values, and a transforms current position. /// public static MoveRates GetWorldMoveRates(Transform from, Transform to, float duration, float teleportThreshold) { return GetMoveRates(from.position, to.position, from.rotation, to.rotation, from.localScale, to.localScale, duration, teleportThreshold); } /// /// Returns a new MoveRates based on previous values, and a transforms current position. /// public static MoveRates GetLocalMoveRates(Transform from, Transform to, float duration, float teleportThreshold) { return GetMoveRates(from.localPosition, to.localPosition, from.localRotation, to.localRotation, from.localScale, to.localScale, duration, teleportThreshold); } /// /// Returns a new MoveRates based on previous values, and a transforms current position. /// public static MoveRates GetWorldMoveRates(TransformProperties prevValues, Transform t, float duration, float teleportThreshold) { return GetMoveRates(prevValues.Position, t.position, prevValues.Rotation, t.rotation, prevValues.Scale, t.localScale, duration, teleportThreshold); } /// /// Returns a new MoveRates based on previous values, and a transforms current position. /// public static MoveRates GetLocalMoveRates(TransformProperties prevValues, Transform t, float duration, float teleportThreshold) { return GetMoveRates(prevValues.Position, t.localPosition, prevValues.Rotation, t.localRotation, prevValues.Scale, t.localScale, duration, teleportThreshold); } /// /// Returns a new MoveRates based on previous values, and a transforms current position. /// public static MoveRates GetMoveRates(TransformProperties prevValues, TransformProperties nextValues, float duration, float teleportThreshold) { return GetMoveRates(prevValues.Position, nextValues.Position, prevValues.Rotation, nextValues.Rotation, prevValues.Scale, nextValues.Scale, duration, teleportThreshold); } /// /// Returns a new MoveRates based on previous values, and a transforms current position. /// public static MoveRates GetMoveRates(Vector3 fromPosition, Vector3 toPosition, Quaternion fromRotation, Quaternion toRotation, Vector3 fromScale, Vector3 toScale, float duration, float teleportThreshold) { float rate; /* Position. */ rate = toPosition.GetRate(fromPosition, duration, out float distance); //Basic teleport check. if (teleportThreshold != UNSET_VALUE && distance > teleportThreshold) return new(INSTANT_VALUE, INSTANT_VALUE, INSTANT_VALUE, duration); //Smoothing. float positionRate = rate.SetIfUnderTolerance(0.0001f, INSTANT_VALUE); rate = toRotation.GetRate(fromRotation, duration, out _); float rotationRate = rate.SetIfUnderTolerance(0.2f, INSTANT_VALUE); rate = toScale.GetRate(fromScale, duration, out _); float scaleRate = rate.SetIfUnderTolerance(0.0001f, INSTANT_VALUE); return new(positionRate, rotationRate, scaleRate, duration); } /// /// Gets a move rate for two Vector3s. /// public static float GetMoveRate(Vector3 fromPosition, Vector3 toPosition, float duration, float teleportThreshold) { float rate; float distance; /* Position. */ rate = toPosition.GetRate(fromPosition, duration, out distance); //Basic teleport check. if (teleportThreshold != UNSET_VALUE && distance > teleportThreshold) { return INSTANT_VALUE; } //Smoothing. else { float positionRate = rate.SetIfUnderTolerance(0.0001f, INSTANT_VALUE); return positionRate; } } /// /// Gets a move rate for two Quaternions. /// public static float GetMoveRate(Quaternion fromRotation, Quaternion toRotation, float duration) { float rate = toRotation.GetRate(fromRotation, duration, out _); float rotationRate = rate.SetIfUnderTolerance(0.2f, INSTANT_VALUE); return rotationRate; } /// /// Moves transform to target values. /// public void Move(Transform movingTransform, TransformProperties goalProperties, float delta, bool useWorldSpace) { if (!IsValid) return; Move(movingTransform, TransformPropertiesFlag.Everything, goalProperties.Position, Position, goalProperties.Rotation, Rotation, goalProperties.Scale, Scale, delta, useWorldSpace); TimeRemaining -= delta; } /// /// Moves transform to target values. /// public void Move(Transform movingTransform, TransformProperties goalProperties, TransformPropertiesFlag movedProperties, float delta, bool useWorldSpace) { if (!IsValid) return; Move(movingTransform, movedProperties, goalProperties.Position, Position, goalProperties.Rotation, Rotation, goalProperties.Scale, Scale, delta, useWorldSpace); TimeRemaining -= delta; } /// /// Moves transform to target values. /// public static void Move(Transform movingTransform, TransformPropertiesFlag movedProperties, Vector3 posGoal, float posRate, Quaternion rotGoal, float rotRate, Vector3 scaleGoal, float scaleRate, float delta, bool useWorldSpace) { Transform t = movingTransform; bool containsPosition = movedProperties.FastContains(TransformPropertiesFlag.Position); bool containsRotation = movedProperties.FastContains(TransformPropertiesFlag.Rotation); bool containsScale = movedProperties.FastContains(TransformPropertiesFlag.Scale); //World space. if (useWorldSpace) { if (containsPosition) { if (posRate == INSTANT_VALUE) t.position = posGoal; else if (posRate == UNSET_VALUE) { } else t.position = Vector3.MoveTowards(t.position, posGoal, posRate * delta); } if (containsRotation) { if (rotRate == INSTANT_VALUE) t.rotation = rotGoal; else if (rotRate == UNSET_VALUE) { } else t.rotation = Quaternion.RotateTowards(t.rotation, rotGoal, rotRate * delta); } } //Local space. else { if (containsPosition) { if (posRate == INSTANT_VALUE) t.localPosition = posGoal; else if (posRate == UNSET_VALUE) { } else t.localPosition = Vector3.MoveTowards(t.localPosition, posGoal, posRate * delta); } if (containsRotation) { if (rotRate == INSTANT_VALUE) t.localRotation = rotGoal; else if (rotRate == UNSET_VALUE) { } else t.localRotation = Quaternion.RotateTowards(t.localRotation, rotGoal, rotRate * delta); } } //Scale always uses local. if (containsScale) { if (scaleRate == INSTANT_VALUE) t.localScale = scaleGoal; else if (scaleRate == UNSET_VALUE) { } else t.localScale = Vector3.MoveTowards(t.localScale, scaleGoal, scaleRate * delta); } } } /// /// Used to make calculations and perform actions in moving transforms over time. /// /// This acts as a wrapper for MoveRates struct. public class MoveRatesCls : IResettable { /// /// Container of all move rate information. /// private MoveRates _moveRates = new(); /// /// Rate at which to move Position. /// public float Position => _moveRates.Position; /// /// Rate at which to move Rotation. /// public float Rotation => _moveRates.Rotation; /// /// Rate at which to move Scale. /// public float Scale => _moveRates.Scale; /// /// Time remaining until the move is complete. /// public float TimeRemaining => _moveRates.TimeRemaining; /// /// True if position move rate should be instant. /// public bool IsPositionInstantValue => _moveRates.IsPositionInstantValue; /// /// True if rotation move rate should be instant. /// public bool IsRotationInstantValue => _moveRates.IsRotationInstantValue; /// /// True if scale move rate should be instant. /// public bool IsScaleInstantValue => _moveRates.IsScaleInstantValue; /// /// True if any data is set. /// public bool IsValid => _moveRates.IsValid; public MoveRatesCls(float value) => _moveRates = new MoveRates(value); public MoveRatesCls(float position, float rotation) => _moveRates = new MoveRates(position, rotation); public MoveRatesCls(float position, float rotation, float scale) => _moveRates = new MoveRates(position, rotation, scale); public MoveRatesCls(float position, float rotation, float scale, float timeRemaining) => _moveRates = new MoveRates(position, rotation, scale, timeRemaining); public MoveRatesCls() => _moveRates.ResetState(); /// /// Sets all rates to instant. /// public void SetInstantRates() => _moveRates.SetInstantRates(); /// /// Sets all rates to the same value. /// public void Update(float value) => _moveRates.Update(value); /// /// Updates values. /// public void Update(float position, float rotation, float scale) => _moveRates.Update(position, rotation, scale); /// /// Updates values. /// public void Update(float position, float rotation, float scale, float timeRemaining) => _moveRates.Update(position, rotation, scale, timeRemaining); /// /// Updaes values. /// public void Update(MoveRatesCls mr) => _moveRates.Update(mr.Position, mr.Rotation, mr.Scale); /// /// Moves transform to target values. /// public void Move(Transform movingTransform, TransformProperties goalProperties, float delta, bool useWorldSpace) => _moveRates.Move(movingTransform, goalProperties, delta, useWorldSpace); /// /// Moves transform to target values. /// public void Move(Transform movingTransform, TransformProperties goalProperties, TransformPropertiesFlag movedProperties, float delta, bool useWorldSpace) => _moveRates.Move(movingTransform, goalProperties, movedProperties, delta, useWorldSpace); public void ResetState() => _moveRates.ResetState(); public void InitializeState() { } } }