using FishNet.Utility.Extension;
using GameKit.Utilities;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace FishNet.Object.Prediction
{
internal class SetInterpolationSmoother
{
#if PREDICTION_V2
#region Private.
///
/// How quickly to move towards goal values.
///
private MoveRates _moveRates;
///
/// Local values of the graphical object when this was initialized.
///
private TransformProperties _graphicalInitializedLocalValues;
///
/// Values of the graphical object during PreTick or PreReplay.
///
private TransformProperties _graphicalPreSimulateWorldValues;
///
/// SmoothingData to use.
///
private SetInterpolationSmootherData _smoothingData;
#endregion
///
/// Initializes this smoother; should only be completed once.
///
///
internal void InitializeOnce(SetInterpolationSmootherData data)
{
_smoothingData = data;
SetGraphicalObject(data.GraphicalObject);
_moveRates = new MoveRates(MoveRates.UNSET_VALUE);
_graphicalPreSimulateWorldValues = _smoothingData.GraphicalObject.GetWorldProperties();
}
///
/// Sets GraphicalObject; can be changed at runtime.
///
///
internal void SetGraphicalObject(Transform value)
{
_smoothingData.GraphicalObject = value;
_graphicalInitializedLocalValues.Update(value.localPosition, value.localRotation, value.localScale);
}
///
/// Sets the interpolation value to use when the owner of this object.
///
/// New interpolation value.
internal void SetInterpolation(byte value)
{
if (value < 1)
value = 1;
_smoothingData.Interpolation = value;
}
///
/// Called every frame.
///
internal void Update()
{
if (CanSmooth())
MoveToTarget();
}
///
/// Called when the TimeManager invokes OnPreTick.
///
internal void OnPreTick()
{
if (CanSmooth())
{
/* Only snap to destination if interpolation is 1.
* This ensures the graphics will be at the proper location
* before the next movement rates are calculated. */
if (_smoothingData.Interpolation == 1)
ResetGraphicalToInitializedLocalOffsets(true, true);
_graphicalPreSimulateWorldValues = _smoothingData.GraphicalObject.GetWorldProperties();
}
}
///
/// Called when TimeManager invokes OnPostTick.
///
internal void OnPostTick()
{
if (CanSmooth())
{
_smoothingData.GraphicalObject.SetPositionAndRotation(_graphicalPreSimulateWorldValues.Position, _graphicalPreSimulateWorldValues.Rotation);
SetGraphicalMoveRates();
}
}
///
/// Returns if prediction can be used on this rigidbody.
///
///
private bool CanSmooth()
{
NetworkObject nob = _smoothingData.NetworkObject;
if (nob == null)
return false;
if (nob.IsServerOnly)
return false;
if (!nob.IsHost && nob.SpectatorAdaptiveInterpolation && !nob.IsOwner)
return false;
return true;
}
///
/// Moves transform to target values.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void MoveToTarget()
{
//No rates are set.
if (!_moveRates.AnySet)
return;
Vector3 posGoal = GetGraphicalGoalPosition();
Quaternion rotGoal = GetGraphicalGoalRotation();
/* Only try to update properties if they have a valid move rate.
* Properties may have 0f move rate if they did not change. */
Transform t = _smoothingData.GraphicalObject;
float delta = Time.deltaTime;
//Position.
if (_smoothingData.SmoothPosition)
{
if (_moveRates.InstantPosition)
ResetGraphicalToInitializedLocalOffsets(true, false);
else if (_moveRates.PositionSet)
t.localPosition = Vector3.MoveTowards(t.localPosition, posGoal, _moveRates.Position * delta);
}
//Rotation.
if (_smoothingData.SmoothRotation)
{
if (_moveRates.InstantRotation)
ResetGraphicalToInitializedLocalOffsets(false, true);
else if (_moveRates.RotationSet)
t.localRotation = Quaternion.RotateTowards(t.localRotation, rotGoal, _moveRates.Rotation * delta);
}
if (GraphicalObjectMatchesLocalValues(posGoal, rotGoal))
_moveRates.Update(MoveRates.UNSET_VALUE);
}
///
/// Returns if this transform matches arguments.
///
///
private bool GraphicalObjectMatchesLocalValues(Vector3 position, Quaternion rotation)
{
bool positionMatches = (!_smoothingData.SmoothPosition || (_smoothingData.GraphicalObject.localPosition == position));
bool rotationMatches = (!_smoothingData.SmoothRotation || (_smoothingData.GraphicalObject.localRotation == rotation));
return (positionMatches && rotationMatches);
}
///
/// Sets Position and Rotation move rates to reach Target datas.
///
private void SetGraphicalMoveRates()
{
uint interval = _smoothingData.Interpolation;
float delta = (float)_smoothingData.NetworkObject?.TimeManager.TickDelta;
float rate;
float distance;
Transform t = _smoothingData.GraphicalObject;
/* Position. */
rate = t.localPosition.GetRate(_graphicalInitializedLocalValues.Position, delta, out distance, interval);
//If qualifies for teleporting.
if (_smoothingData.TeleportThreshold != MoveRates.UNSET_VALUE && distance >= _smoothingData.TeleportThreshold)
{
_moveRates.Update(MoveRates.INSTANT_VALUE);
}
//Smoothing.
else
{
float positionRate = rate.SetIfUnderTolerance(0.0001f, MoveRates.INSTANT_VALUE);
rate = t.localRotation.GetRate(_graphicalInitializedLocalValues.Rotation, delta, out _, interval);
float rotationRate = rate.SetIfUnderTolerance(0.2f, MoveRates.INSTANT_VALUE);
_moveRates.Update(positionRate, rotationRate, MoveRates.UNSET_VALUE);
}
}
///
/// Gets a goal position for the graphical object.
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector3 GetGraphicalGoalPosition()
{
if (_smoothingData.SmoothPosition)
return _graphicalInitializedLocalValues.Position;
else
return _smoothingData.GraphicalObject.position;
}
///
/// Gets a goal rotation for the graphical object.
///
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Quaternion GetGraphicalGoalRotation()
{
if (_smoothingData.SmoothRotation)
return _graphicalInitializedLocalValues.Rotation;
else
return _smoothingData.GraphicalObject.rotation;
}
///
/// Resets the graphical object to it's transform offsets during instantiation.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ResetGraphicalToInitializedLocalOffsets(bool position, bool rotation)
{
Transform graphical = _smoothingData.GraphicalObject;
if (position)
graphical.localPosition = GetGraphicalGoalPosition();
if (rotation)
graphical.localRotation = GetGraphicalGoalRotation();
}
#endif
}
}