299 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			299 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using FishNet.Utility.Extension;
 | |
| using FishNet.Object;
 | |
| using System.Runtime.CompilerServices;
 | |
| using UnityEngine;
 | |
| 
 | |
| namespace FishNet.Component.Prediction
 | |
| {
 | |
|     internal class PredictedObjectOwnerSmoother
 | |
|     {
 | |
|         #region Serialized.
 | |
|         /// <summary>
 | |
|         /// Transform which holds the graphical features of this object. This transform will be smoothed when desynchronizations occur.
 | |
|         /// </summary>
 | |
|         private Transform _graphicalObject;
 | |
|         /// <summary>
 | |
|         /// Sets GraphicalObject.
 | |
|         /// </summary>
 | |
|         /// <param name="value"></param>
 | |
|         public void SetGraphicalObject(Transform value)
 | |
|         {
 | |
|             _graphicalObject = value;
 | |
|             _networkBehaviour.transform.SetTransformOffsets(value, ref _graphicalInstantiatedOffsetPosition, ref _graphicalInstantiatedOffsetRotation);
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// NetworkBehaviour which is using this object.
 | |
|         /// </summary>
 | |
|         private NetworkBehaviour _networkBehaviour;
 | |
|         /// <summary>
 | |
|         /// How far the transform must travel in a single update to cause a teleport rather than smoothing. Using 0f will teleport every update.
 | |
|         /// </summary>
 | |
|         private float _teleportThreshold = 1f;
 | |
|         /// <summary>
 | |
|         /// How far in the past to keep the graphical object when owner.
 | |
|         /// </summary>
 | |
|         private byte _interpolation = 1;
 | |
|         /// <summary>
 | |
|         /// Sets the interpolation value to use when the owner of this object.
 | |
|         /// </summary>
 | |
|         /// <param name="value"></param>
 | |
|         public void SetInterpolation(byte value) => _interpolation = value;
 | |
|         #endregion
 | |
| 
 | |
|         #region Private.
 | |
|         /// <summary>
 | |
|         /// World position before transform was predicted or reset.
 | |
|         /// </summary>
 | |
|         private Vector3 _graphicalStartPosition;
 | |
|         /// <summary>
 | |
|         /// World rotation before transform was predicted or reset.
 | |
|         /// </summary>
 | |
|         private Quaternion _graphicalStartRotation;
 | |
|         /// <summary>
 | |
|         /// GraphicalObject position difference from the PredictedObject when this is initialized.
 | |
|         /// </summary>
 | |
|         private Vector3 _graphicalInstantiatedOffsetPosition;
 | |
|         /// <summary>
 | |
|         /// How quickly to move towards TargetPosition.
 | |
|         /// </summary>
 | |
|         private float _positionMoveRate = -2;
 | |
|         /// <summary>
 | |
|         /// GraphicalObject rotation difference from the PredictedObject when this is initialized.
 | |
|         /// </summary>
 | |
|         private Quaternion _graphicalInstantiatedOffsetRotation;
 | |
|         /// <summary>
 | |
|         /// How quickly to move towards TargetRotation.
 | |
|         /// </summary>
 | |
|         private float _rotationMoveRate = -2;
 | |
|         /// <summary>
 | |
|         /// True if OnPreTick was received this frame.
 | |
|         /// </summary>
 | |
|         private bool _preTickReceived;
 | |
|         /// <summary>
 | |
|         /// True to move towards position goals.
 | |
|         /// </summary>
 | |
|         private bool _smoothPosition;
 | |
|         /// <summary>
 | |
|         /// True to move towards rotation goals.
 | |
|         /// </summary>
 | |
|         private bool _smoothRotation;
 | |
|         #endregion
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Initializes this script for use.
 | |
|         /// </summary>
 | |
|         public void Initialize(NetworkBehaviour nb, Vector3 instantiatedOffsetPosition, Quaternion instantiatedOffsetRotation, Transform graphicalObject
 | |
|               , bool smoothPosition, bool smoothRotation, byte interpolation, float teleportThreshold)
 | |
|         {
 | |
|             _networkBehaviour = nb;
 | |
|             _graphicalInstantiatedOffsetPosition = instantiatedOffsetPosition;
 | |
|             _graphicalInstantiatedOffsetRotation = instantiatedOffsetRotation;
 | |
|             _graphicalObject = graphicalObject;
 | |
| 
 | |
|             _smoothPosition = smoothPosition;
 | |
|             _smoothRotation = smoothRotation;
 | |
| 
 | |
|             _interpolation = interpolation;
 | |
|             _teleportThreshold = teleportThreshold;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Called every frame.
 | |
|         /// </summary>
 | |
|         public void ManualUpdate()
 | |
|         {
 | |
|             MoveToTarget();
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Called when the TimeManager invokes OnPreTick.
 | |
|         /// </summary>
 | |
|         public void OnPreTick()
 | |
|         {
 | |
|             if (CanSmooth())
 | |
|             {
 | |
|                 _preTickReceived = true;
 | |
|                 /* 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 (_interpolation == 1)
 | |
|                     ResetGraphicalToInstantiatedProperties(true, true);
 | |
| 
 | |
|                 SetGraphicalPreviousProperties();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public void OnPostTick()
 | |
|         {
 | |
|             if (CanSmooth() && _preTickReceived)
 | |
|             {
 | |
|                 _preTickReceived = false;
 | |
|                 ResetGraphicalToPreviousProperties();
 | |
|                 SetGraphicalMoveRates();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns if prediction can be used on this rigidbody.
 | |
|         /// </summary>
 | |
|         /// <returns></returns>
 | |
|         private bool CanSmooth()
 | |
|         {
 | |
|             if (_interpolation == 0)
 | |
|                 return false;
 | |
|             //Only owner needs smoothing.
 | |
|             if (!_networkBehaviour.IsOwner && !_networkBehaviour.IsHost)
 | |
|                 return false;
 | |
| 
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Moves transform to target values.
 | |
|         /// </summary>
 | |
|         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | |
|         private void MoveToTarget()
 | |
|         {
 | |
|             //Not set, meaning movement doesnt need to happen or completed.
 | |
|             if (_positionMoveRate == -2f && _rotationMoveRate == -2f)
 | |
|                 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 = _graphicalObject;
 | |
|             float delta = Time.deltaTime;
 | |
| 
 | |
|             //Position.
 | |
|             if (SmoothPosition())
 | |
|             {
 | |
|                 if (_positionMoveRate == -1f)
 | |
|                     ResetGraphicalToInstantiatedProperties(true, false);
 | |
|                 else if (_positionMoveRate > 0f)
 | |
|                     t.position = Vector3.MoveTowards(t.position, posGoal, _positionMoveRate * delta);
 | |
|             }
 | |
| 
 | |
|             //Rotation.
 | |
|             if (SmoothRotation())
 | |
|             {
 | |
|                 if (_rotationMoveRate == -1f)
 | |
|                     ResetGraphicalToInstantiatedProperties(false, true);
 | |
|                 else if (_rotationMoveRate > 0f)
 | |
|                     t.rotation = Quaternion.RotateTowards(t.rotation, rotGoal, _rotationMoveRate * delta);
 | |
|             }
 | |
| 
 | |
|             if (GraphicalObjectMatches(posGoal, rotGoal))
 | |
|             {
 | |
|                 _positionMoveRate = -2f;
 | |
|                 _rotationMoveRate = -2f;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns if this transform matches arguments.
 | |
|         /// </summary>
 | |
|         /// <returns></returns>
 | |
|         private bool GraphicalObjectMatches(Vector3 position, Quaternion rotation)
 | |
|         {
 | |
|             bool positionMatches = (!_smoothPosition || (_graphicalObject.position == position));
 | |
|             bool rotationMatches = (!_smoothRotation || (_graphicalObject.rotation == rotation));
 | |
|             return (positionMatches && rotationMatches);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// True to smooth position. When false the graphicalObjects property will not be updated.
 | |
|         /// </summary>
 | |
|         /// <returns></returns>
 | |
|         private bool SmoothPosition() => (_smoothPosition && (_networkBehaviour.IsOwner || _networkBehaviour.IsHost));
 | |
|         /// <summary>
 | |
|         /// True to smooth rotation. When false the graphicalObjects property will not be updated.
 | |
|         /// </summary>
 | |
|         /// <returns></returns>
 | |
|         private bool SmoothRotation() => (_smoothRotation && (_networkBehaviour.IsOwner || _networkBehaviour.IsHost));
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Sets Position and Rotation move rates to reach Target datas.
 | |
|         /// </summary>
 | |
|         private void SetGraphicalMoveRates()
 | |
|         {
 | |
|             float delta = ((float)_networkBehaviour.TimeManager.TickDelta * _interpolation);
 | |
| 
 | |
|             float distance;
 | |
|             distance = Vector3.Distance(_graphicalObject.position, GetGraphicalGoalPosition());
 | |
|             //If qualifies for teleporting.
 | |
|             if (_teleportThreshold != -1f && distance >= _teleportThreshold)
 | |
|             {
 | |
|                 _positionMoveRate = -1f;
 | |
|                 _rotationMoveRate = -1f;
 | |
|             }
 | |
|             //Smoothing.
 | |
|             else
 | |
|             {
 | |
|                 _positionMoveRate = (distance / delta);
 | |
|                 distance = Quaternion.Angle(_graphicalObject.rotation, GetGraphicalGoalRotation());
 | |
|                 if (distance > 0f)
 | |
|                     _rotationMoveRate = (distance / delta);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets a goal position for the graphical object.
 | |
|         /// </summary>
 | |
|         /// <returns></returns>
 | |
|         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | |
|         private Vector3 GetGraphicalGoalPosition()
 | |
|         {
 | |
|             if (SmoothPosition())
 | |
|                 return (_networkBehaviour.transform.position + _graphicalInstantiatedOffsetPosition);
 | |
|             else
 | |
|                 return _graphicalObject.position;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets a goal rotation for the graphical object.
 | |
|         /// </summary>
 | |
|         /// <returns></returns>
 | |
|         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | |
|         private Quaternion GetGraphicalGoalRotation()
 | |
|         {
 | |
|             if (SmoothRotation())
 | |
|                 return (_graphicalInstantiatedOffsetRotation * _networkBehaviour.transform.rotation);
 | |
|             else
 | |
|                 return _graphicalObject.rotation;
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Caches the graphical object' current position and rotation.
 | |
|         /// </summary>
 | |
|         private void SetGraphicalPreviousProperties()
 | |
|         {
 | |
|             _graphicalStartPosition = _graphicalObject.position;
 | |
|             _graphicalStartRotation = _graphicalObject.rotation;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Resets the graphical object to cached position and rotation of the transform.
 | |
|         /// </summary>
 | |
|         private void ResetGraphicalToPreviousProperties()
 | |
|         {
 | |
|             _graphicalObject.SetPositionAndRotation(_graphicalStartPosition, _graphicalStartRotation);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Resets the graphical object to it's transform offsets during instantiation.
 | |
|         /// </summary>
 | |
|         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | |
|         private void ResetGraphicalToInstantiatedProperties(bool position, bool rotation)
 | |
|         {
 | |
|             if (position)
 | |
|                 _graphicalObject.position = GetGraphicalGoalPosition();
 | |
|             if (rotation)
 | |
|                 _graphicalObject.rotation = GetGraphicalGoalRotation();
 | |
|         }
 | |
| 
 | |
| 
 | |
|     }
 | |
| 
 | |
| 
 | |
| } |