1162 lines
34 KiB
C#
1162 lines
34 KiB
C#
|
namespace Fusion.Addons.KCC
|
||
|
{
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using Unity.Profiling;
|
||
|
using UnityEngine;
|
||
|
|
||
|
using ReadOnlyProcessors = System.Collections.ObjectModel.ReadOnlyCollection<IKCCProcessor>;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Fusion kinematic character controller component.
|
||
|
/// </summary>
|
||
|
[DisallowMultipleComponent]
|
||
|
[RequireComponent(typeof(Rigidbody))]
|
||
|
public sealed partial class KCC : NetworkTRSP, IAfterSpawned, IAfterClientPredictionReset, IBeforeTick, IAfterTick
|
||
|
{
|
||
|
// CONSTANTS
|
||
|
|
||
|
public const int CACHE_SIZE = 64;
|
||
|
public const int HISTORY_SIZE = 60;
|
||
|
public const string TRACING_SCRIPT_DEFINE = "KCC_TRACE";
|
||
|
|
||
|
// PRIVATE MEMBERS
|
||
|
|
||
|
[SerializeField]
|
||
|
private KCCSettings _settings = new KCCSettings();
|
||
|
|
||
|
private Transform _transform;
|
||
|
private Rigidbody _rigidbody;
|
||
|
private bool _isSpawned;
|
||
|
private bool _isInFixedUpdate;
|
||
|
private bool _hasManualUpdate;
|
||
|
private KCCDebug _debug = new KCCDebug();
|
||
|
private KCCCollider _collider = new KCCCollider();
|
||
|
private KCCData _fixedData = new KCCData();
|
||
|
private KCCData _renderData = new KCCData();
|
||
|
private KCCData[] _historyData = new KCCData[HISTORY_SIZE];
|
||
|
private KCCSettings _defaultSettings = new KCCSettings();
|
||
|
private KCCOverlapInfo _extendedOverlapInfo = new KCCOverlapInfo(CACHE_SIZE);
|
||
|
private KCCOverlapInfo _trackOverlapInfo = new KCCOverlapInfo(CACHE_SIZE);
|
||
|
private List<Collider> _childColliders = new List<Collider>();
|
||
|
private RaycastHit[] _raycastHits = new RaycastHit[CACHE_SIZE];
|
||
|
private Collider[] _hitColliders = new Collider[CACHE_SIZE];
|
||
|
private Collider[] _addColliders = new Collider[CACHE_SIZE];
|
||
|
private Collider[] _removeColliders = new Collider[CACHE_SIZE];
|
||
|
private KCCCollision[] _removeCollisions = new KCCCollision[CACHE_SIZE];
|
||
|
private KCCResolver _resolver = new KCCResolver(CACHE_SIZE);
|
||
|
private EKCCFeatures _activeFeatures = EKCCFeatures.None;
|
||
|
private List<KCCStageInfo> _activeStages = new List<KCCStageInfo>();
|
||
|
private IKCCProcessor[] _cachedProcessors = new IKCCProcessor[CACHE_SIZE * 2];
|
||
|
private int _cachedProcessorCount = 0;
|
||
|
private IKCCProcessor[] _stageProcessors = new IKCCProcessor[CACHE_SIZE * 2];
|
||
|
private int _stageProcessorCount = 0;
|
||
|
private BeginMove _beginMove = new BeginMove();
|
||
|
private PrepareData _prepareData = new PrepareData();
|
||
|
private AfterMoveStep _afterMoveStep = new AfterMoveStep();
|
||
|
private EndMove _endMove = new EndMove();
|
||
|
private List<IKCCProcessor> _localProcessors = new List<IKCCProcessor>();
|
||
|
private ReadOnlyProcessors _localROProcessors;
|
||
|
private int _lastPredictedFixedTick;
|
||
|
private int _lastPredictedRenderFrame;
|
||
|
private int _lastPredictedLookRotationFrame;
|
||
|
private float _lastRenderTime;
|
||
|
private Vector3 _lastRenderPosition;
|
||
|
private Vector3 _lastAntiJitterPosition;
|
||
|
private Collider _lastNonNetworkedCollider;
|
||
|
private Vector3 _predictionError;
|
||
|
|
||
|
private event Action<KCC> _onSpawn;
|
||
|
|
||
|
private static ProfilerMarker _fixedUpdateMarker = new ProfilerMarker("KCC.FixedUpdate");
|
||
|
private static ProfilerMarker _renderUpdateMarker = new ProfilerMarker("KCC.RenderUpdate");
|
||
|
private static ProfilerMarker _restoreStateMarker = new ProfilerMarker("KCC.RestoreState");
|
||
|
private static ProfilerMarker _afterTickMarker = new ProfilerMarker("KCC.AfterTick");
|
||
|
private static ProfilerMarker _simulatedMoveMarker = new ProfilerMarker("Simulated Move");
|
||
|
private static ProfilerMarker _interpolationMarker = new ProfilerMarker("Interpolation");
|
||
|
|
||
|
// PUBLIC METHODS
|
||
|
|
||
|
/// <summary>
|
||
|
/// Immediately synchronize Transform and Rigidbody based on current state.
|
||
|
/// </summary>
|
||
|
/// <param name="synchronizePosition">Propagates KCCData.TargetPosition to Transform and Rigidbody components.</param>
|
||
|
/// <param name="synchronizeRotation">Propagates KCCData.TransformRotation to Transform component.</param>
|
||
|
/// <param name="allowAntiJitter">Allows anti-jitter feature. This has effect only in render.</param>
|
||
|
/// <param name="moveRigidbody">Use Rigidbody.MovePosition() instead of setting Rigidbody.position directly.</param>
|
||
|
public void SynchronizeTransform(bool synchronizePosition, bool synchronizeRotation, bool allowAntiJitter = true, bool moveRigidbody = false)
|
||
|
{
|
||
|
if (IsInFixedUpdate == true)
|
||
|
{
|
||
|
allowAntiJitter = false;
|
||
|
}
|
||
|
|
||
|
SynchronizeTransform(Data, synchronizePosition, synchronizeRotation, allowAntiJitter, moveRigidbody);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Refresh child colliders list, used for collision filtering.
|
||
|
/// Child colliders are ignored completely, triggers are treated as valid collision.
|
||
|
/// </summary>
|
||
|
public void RefreshChildColliders()
|
||
|
{
|
||
|
_childColliders.Clear();
|
||
|
|
||
|
GetComponentsInChildren(true, _childColliders);
|
||
|
|
||
|
int currentIndex = 0;
|
||
|
int lastIndex = _childColliders.Count - 1;
|
||
|
|
||
|
while (currentIndex <= lastIndex)
|
||
|
{
|
||
|
Collider childCollider = _childColliders[currentIndex];
|
||
|
if (childCollider.isTrigger == true || childCollider == _collider.Collider)
|
||
|
{
|
||
|
_childColliders[currentIndex] = _childColliders[lastIndex];
|
||
|
_childColliders.RemoveAt(lastIndex);
|
||
|
|
||
|
--lastIndex;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
++currentIndex;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns fixed data for specific tick in history. Default history size is 60 ticks.
|
||
|
/// </summary>
|
||
|
public KCCData GetHistoryData(int tick)
|
||
|
{
|
||
|
if (tick < 0)
|
||
|
return null;
|
||
|
|
||
|
KCCData data = _historyData[tick % HISTORY_SIZE];
|
||
|
if (data != null && data.Tick == tick)
|
||
|
return data;
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Controls whether update methods are driven by default Fusion methods or called manually using <c>ManualFixedUpdate()</c> and <c>ManualRenderUpdate()</c>.
|
||
|
/// </summary>
|
||
|
public void SetManualUpdate(bool hasManualUpdate)
|
||
|
{
|
||
|
_hasManualUpdate = hasManualUpdate;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Invokes callback when the KCC is spawned.
|
||
|
/// If the KCC is already spawned, the callback is invoked immediately.
|
||
|
/// </summary>
|
||
|
public void InvokeOnSpawn(Action<KCC> callback)
|
||
|
{
|
||
|
if (_isSpawned == true)
|
||
|
{
|
||
|
callback(this);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_onSpawn -= callback;
|
||
|
_onSpawn += callback;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Manual fixed update execution, <c>SetManualUpdate(true)</c> must be called prior usage.
|
||
|
/// </summary>
|
||
|
public void ManualFixedUpdate()
|
||
|
{
|
||
|
if (_isSpawned == false)
|
||
|
return;
|
||
|
if (_hasManualUpdate == false)
|
||
|
throw new InvalidOperationException($"[{name}] Manual update is not set!");
|
||
|
|
||
|
_fixedUpdateMarker.Begin();
|
||
|
OnFixedUpdateInternal();
|
||
|
_fixedUpdateMarker.End();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Manual render update execution, <c>SetManualUpdate(true)</c> must be called prior usage.
|
||
|
/// </summary>
|
||
|
public void ManualRenderUpdate()
|
||
|
{
|
||
|
if (_isSpawned == false)
|
||
|
return;
|
||
|
if (_hasManualUpdate == false)
|
||
|
throw new InvalidOperationException($"[{name}] Manual update is not set!");
|
||
|
|
||
|
_renderUpdateMarker.Begin();
|
||
|
OnRenderUpdateInternal();
|
||
|
_renderUpdateMarker.End();
|
||
|
}
|
||
|
|
||
|
// MonoBehaviour INTERFACE
|
||
|
|
||
|
private void Awake()
|
||
|
{
|
||
|
_transform = transform;
|
||
|
|
||
|
_rigidbody = GetComponent<Rigidbody>();
|
||
|
_rigidbody.isKinematic = true;
|
||
|
|
||
|
_localROProcessors = new ReadOnlyProcessors(_localProcessors);
|
||
|
|
||
|
RefreshCollider(true);
|
||
|
}
|
||
|
|
||
|
private void OnDestroy()
|
||
|
{
|
||
|
SetDefaults(true);
|
||
|
}
|
||
|
|
||
|
private void OnDrawGizmosSelected()
|
||
|
{
|
||
|
if (_settings == null)
|
||
|
return;
|
||
|
|
||
|
float radius = Mathf.Max(0.01f, _settings.Radius);
|
||
|
float height = Mathf.Max(radius * 2.0f, _settings.Height);
|
||
|
|
||
|
Vector3 basePosition = transform.position;
|
||
|
|
||
|
Color gizmosColor = Gizmos.color;
|
||
|
|
||
|
Vector3 baseLow = basePosition + Vector3.up * radius;
|
||
|
Vector3 baseHigh = basePosition + Vector3.up * (height - radius);
|
||
|
Vector3 offsetFront = Vector3.forward * radius;
|
||
|
Vector3 offsetBack = Vector3.back * radius;
|
||
|
Vector3 offsetLeft = Vector3.left * radius;
|
||
|
Vector3 offsetRight = Vector3.right * radius;
|
||
|
|
||
|
Gizmos.color = Color.green;
|
||
|
|
||
|
Gizmos.DrawWireSphere(baseLow, radius);
|
||
|
Gizmos.DrawWireSphere(baseHigh, radius);
|
||
|
|
||
|
Gizmos.DrawLine(baseLow + offsetFront, baseHigh + offsetFront);
|
||
|
Gizmos.DrawLine(baseLow + offsetBack, baseHigh + offsetBack);
|
||
|
Gizmos.DrawLine(baseLow + offsetLeft, baseHigh + offsetLeft);
|
||
|
Gizmos.DrawLine(baseLow + offsetRight, baseHigh + offsetRight);
|
||
|
|
||
|
if (_settings.Extent > 0.0f)
|
||
|
{
|
||
|
float extendedRadius = radius + _settings.Extent;
|
||
|
|
||
|
Gizmos.color = Color.yellow;
|
||
|
|
||
|
Gizmos.DrawWireSphere(baseLow, extendedRadius);
|
||
|
Gizmos.DrawWireSphere(baseHigh, extendedRadius);
|
||
|
}
|
||
|
|
||
|
Gizmos.color = gizmosColor;
|
||
|
}
|
||
|
|
||
|
// NetworkBehaviour INTERFACE
|
||
|
|
||
|
public override int? DynamicWordCount => GetNetworkDataWordCount();
|
||
|
|
||
|
public override void Spawned()
|
||
|
{
|
||
|
Trace(nameof(Spawned));
|
||
|
|
||
|
if (_isSpawned == true)
|
||
|
throw new InvalidOperationException($"[{name}] KCC is already spawned!");
|
||
|
|
||
|
_defaultSettings.CopyFromOther(_settings);
|
||
|
|
||
|
SetDefaults(false);
|
||
|
|
||
|
_isSpawned = true;
|
||
|
_isInFixedUpdate = true;
|
||
|
|
||
|
KCCUtility.GetClampedLookRotationAngles(_transform.rotation, out float lookPitch, out float lookYaw);
|
||
|
|
||
|
_fixedData = new KCCData();
|
||
|
_fixedData.Frame = Time.frameCount;
|
||
|
_fixedData.Tick = Runner.Tick.Raw;
|
||
|
_fixedData.Time = Runner.SimulationTime;
|
||
|
_fixedData.DeltaTime = Runner.DeltaTime;
|
||
|
_fixedData.UpdateDeltaTime = _fixedData.DeltaTime;
|
||
|
_fixedData.Gravity = Physics.gravity;
|
||
|
_fixedData.MaxGroundAngle = 60.0f;
|
||
|
_fixedData.MaxWallAngle = 5.0f;
|
||
|
_fixedData.MaxHangAngle = 30.0f;
|
||
|
_fixedData.BasePosition = _transform.position;
|
||
|
_fixedData.DesiredPosition = _transform.position;
|
||
|
_fixedData.TargetPosition = _transform.position;
|
||
|
_fixedData.LookPitch = lookPitch;
|
||
|
_fixedData.LookYaw = lookYaw;
|
||
|
|
||
|
_lastPredictedFixedTick = _fixedData.Tick;
|
||
|
_lastPredictedRenderFrame = _fixedData.Frame;
|
||
|
_lastPredictedLookRotationFrame = _fixedData.Frame;
|
||
|
|
||
|
if (Object.HasStateAuthority == false)
|
||
|
{
|
||
|
ReadNetworkData();
|
||
|
SynchronizeTransform(_fixedData, true, true, false, false);
|
||
|
}
|
||
|
|
||
|
_renderData = new KCCData();
|
||
|
_renderData.CopyFromOther(_fixedData);
|
||
|
|
||
|
_lastRenderPosition = _renderData.TargetPosition;
|
||
|
_lastAntiJitterPosition = _renderData.TargetPosition;
|
||
|
|
||
|
RefreshCollider(_fixedData.IsActive);
|
||
|
RefreshChildColliders();
|
||
|
|
||
|
UnityEngine.Object[] processors = _settings.Processors;
|
||
|
if (processors != null)
|
||
|
{
|
||
|
for (int i = 0, count = processors.Length; i < count; ++i)
|
||
|
{
|
||
|
UnityEngine.Object processorObject = processors[i];
|
||
|
if (processorObject == null)
|
||
|
{
|
||
|
KCCUtility.Log(this, this, default, EKCCLogType.Warning, $"Missing processor object - {nameof(KCCSettings)}.{nameof(KCCSettings.Processors)} at index {i}");
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (KCCUtility.ResolveProcessor(processorObject, out IKCCProcessor processor, out GameObject gameObject, out Component component, out ScriptableObject scriptableObject) == false)
|
||
|
{
|
||
|
KCCUtility.Log(this, processorObject, default, EKCCLogType.Error, $"Failed to resolve {nameof(IKCCProcessor)} in {processorObject.name} ({processorObject.GetType().FullName}) - {nameof(KCCSettings)}.{nameof(KCCSettings.Processors)} at index {i}");
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
AddLocalProcessor(processor);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (_onSpawn != null)
|
||
|
{
|
||
|
try { _onSpawn(this); } catch (Exception exception) { UnityEngine.Debug.LogException(exception); }
|
||
|
|
||
|
_onSpawn = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void Despawned(NetworkRunner runner, bool hasState)
|
||
|
{
|
||
|
if (_isSpawned == false)
|
||
|
return;
|
||
|
|
||
|
Trace(nameof(Despawned));
|
||
|
|
||
|
ForceRemoveAllCollisions(_fixedData);
|
||
|
ForceRemoveAllModifiers(_fixedData);
|
||
|
|
||
|
while (_localProcessors.Count > 0)
|
||
|
{
|
||
|
RemoveLocalProcessor(_localProcessors[_localProcessors.Count - 1]);
|
||
|
}
|
||
|
|
||
|
SetDefaults(true);
|
||
|
}
|
||
|
|
||
|
public override void FixedUpdateNetwork()
|
||
|
{
|
||
|
if (_hasManualUpdate == true)
|
||
|
return;
|
||
|
|
||
|
_fixedUpdateMarker.Begin();
|
||
|
OnFixedUpdateInternal();
|
||
|
_fixedUpdateMarker.End();
|
||
|
}
|
||
|
|
||
|
public override void Render()
|
||
|
{
|
||
|
if (_hasManualUpdate == true)
|
||
|
return;
|
||
|
|
||
|
_renderUpdateMarker.Begin();
|
||
|
OnRenderUpdateInternal();
|
||
|
_renderUpdateMarker.End();
|
||
|
}
|
||
|
|
||
|
// IAfterSpawned INTERFACE
|
||
|
|
||
|
void IAfterSpawned.AfterSpawned()
|
||
|
{
|
||
|
_renderData.CopyFromOther(_fixedData);
|
||
|
|
||
|
if (Object.IsInSimulation == true)
|
||
|
{
|
||
|
WriteNetworkData();
|
||
|
}
|
||
|
|
||
|
_isInFixedUpdate = false;
|
||
|
}
|
||
|
|
||
|
// IAfterClientPredictionReset INTERFACE
|
||
|
|
||
|
void IAfterClientPredictionReset.AfterClientPredictionReset()
|
||
|
{
|
||
|
int latestServerTick = Runner.LatestServerTick;
|
||
|
|
||
|
Trace(nameof(IAfterClientPredictionReset.AfterClientPredictionReset), $"Tick:{latestServerTick}");
|
||
|
|
||
|
_restoreStateMarker.Begin();
|
||
|
|
||
|
KCCData historyData = _historyData[latestServerTick % HISTORY_SIZE];
|
||
|
if (historyData != null && historyData.Tick == latestServerTick)
|
||
|
{
|
||
|
_fixedData.CopyFromOther(historyData);
|
||
|
_fixedData.Frame = Time.frameCount;
|
||
|
}
|
||
|
|
||
|
ReadNetworkData();
|
||
|
|
||
|
if (historyData != default)
|
||
|
{
|
||
|
RestoreHistoryData(historyData);
|
||
|
}
|
||
|
|
||
|
RefreshCollider(_fixedData.IsActive);
|
||
|
|
||
|
if (_fixedData.IsActive == true)
|
||
|
{
|
||
|
SynchronizeTransform(_fixedData, true, true, false, false);
|
||
|
}
|
||
|
|
||
|
_lastPredictedFixedTick = latestServerTick;
|
||
|
|
||
|
_restoreStateMarker.End();
|
||
|
}
|
||
|
|
||
|
// IBeforeTick INTERFACE
|
||
|
|
||
|
void IBeforeTick.BeforeTick()
|
||
|
{
|
||
|
_isInFixedUpdate = true;
|
||
|
|
||
|
_fixedData.Frame = Time.frameCount;
|
||
|
_fixedData.Tick = Runner.Tick.Raw;
|
||
|
_fixedData.Alpha = 0.0f;
|
||
|
_fixedData.Time = Runner.SimulationTime;
|
||
|
_fixedData.DeltaTime = Runner.DeltaTime;
|
||
|
_fixedData.UpdateDeltaTime = _fixedData.DeltaTime;
|
||
|
|
||
|
Trace(nameof(IBeforeTick.BeforeTick), "[Fixed Update Initialization]", $"Time:{_fixedData.Time:F6}", $"DeltaTime:{_fixedData.DeltaTime:F6}", $"Alpha:{_fixedData.Alpha:F4}", $"HasInputAuthority:{Object.HasInputAuthority}", $"HasStateAuthority:{Object.HasStateAuthority}");
|
||
|
}
|
||
|
|
||
|
// IAfterTick INTERFACE
|
||
|
|
||
|
void IAfterTick.AfterTick()
|
||
|
{
|
||
|
Trace(nameof(IAfterTick.AfterTick));
|
||
|
|
||
|
_afterTickMarker.Begin();
|
||
|
|
||
|
PublishFixedData(true, true);
|
||
|
WriteNetworkData();
|
||
|
|
||
|
_isInFixedUpdate = false;
|
||
|
|
||
|
_afterTickMarker.End();
|
||
|
}
|
||
|
|
||
|
// PRIVATE METHODS
|
||
|
|
||
|
private void OnFixedUpdateInternal()
|
||
|
{
|
||
|
if (IsInFixedUpdate == false)
|
||
|
throw new InvalidOperationException($"[{name}] KCC fixed update called from render update! This is not allowed.");
|
||
|
|
||
|
Trace(nameof(OnFixedUpdateInternal));
|
||
|
|
||
|
_debug.BeforePredictedFixedMove(this);
|
||
|
|
||
|
RefreshCollider(_fixedData.IsActive);
|
||
|
|
||
|
_simulatedMoveMarker.Begin();
|
||
|
|
||
|
MovePredicted(_fixedData);
|
||
|
|
||
|
if (_fixedData.IsActive == true)
|
||
|
{
|
||
|
SynchronizeTransform(_fixedData, true, true, false, true);
|
||
|
}
|
||
|
|
||
|
PublishFixedData(false, true);
|
||
|
|
||
|
_lastPredictedFixedTick = _fixedData.Tick;
|
||
|
|
||
|
_simulatedMoveMarker.End();
|
||
|
|
||
|
_debug.AfterPredictedFixedMove(this);
|
||
|
}
|
||
|
|
||
|
private void OnRenderUpdateInternal()
|
||
|
{
|
||
|
if (IsInFixedUpdate == true)
|
||
|
throw new InvalidOperationException($"[{name}] KCC render update called from fixed update! This is not allowed.");
|
||
|
|
||
|
Trace(nameof(OnRenderUpdateInternal));
|
||
|
|
||
|
int frame = Time.frameCount;
|
||
|
float deltaTime = Runner.DeltaTime;
|
||
|
bool isInSimulation = Object.IsInSimulation;
|
||
|
|
||
|
_renderData.Frame = frame;
|
||
|
|
||
|
if (isInSimulation == true && Object.RenderTimeframe != RenderTimeframe.Remote)
|
||
|
{
|
||
|
_simulatedMoveMarker.Begin();
|
||
|
|
||
|
_renderData.Tick = Runner.Tick;
|
||
|
_renderData.Alpha = Runner.LocalAlpha;
|
||
|
|
||
|
float previousTime = _renderData.Time;
|
||
|
|
||
|
_renderData.Time = Runner.SimulationTime + _renderData.Alpha * deltaTime;
|
||
|
|
||
|
if (IsInterpolatingInRenderUpdate == true)
|
||
|
{
|
||
|
_renderData.Tick -= 1;
|
||
|
_renderData.Time -= deltaTime;
|
||
|
|
||
|
if (_renderData.Frame == _fixedData.Frame)
|
||
|
{
|
||
|
previousTime -= deltaTime;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_renderData.DeltaTime = _renderData.Time - previousTime;
|
||
|
_renderData.UpdateDeltaTime = _renderData.DeltaTime;
|
||
|
|
||
|
UpdatePredictionError();
|
||
|
#if UNITY_EDITOR
|
||
|
if (_debug.ShowPath == true)
|
||
|
{
|
||
|
if (_renderData.Frame == _fixedData.Frame)
|
||
|
{
|
||
|
UnityEngine.Debug.DrawLine(_fixedData.TargetPosition, _renderData.TargetPosition, KCCDebug.FixedToRenderPathColor, _debug.DisplayTime);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
UnityEngine.Debug.DrawLine(_lastRenderPosition, _renderData.TargetPosition, KCCDebug.PredictionCorrectionColor, _debug.DisplayTime);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
if (IsPredictingInRenderUpdate == true)
|
||
|
{
|
||
|
MovePredicted(_renderData);
|
||
|
|
||
|
_lastPredictedRenderFrame = frame;
|
||
|
_lastPredictedLookRotationFrame = frame;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
MoveInterpolated(_renderData);
|
||
|
|
||
|
if (IsPredictingLookRotation == true)
|
||
|
{
|
||
|
_lastPredictedLookRotationFrame = frame;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (_renderData.IsActive == true)
|
||
|
{
|
||
|
SynchronizeTransform(_renderData, true, true, true, true);
|
||
|
}
|
||
|
|
||
|
_simulatedMoveMarker.End();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_interpolationMarker.Begin();
|
||
|
|
||
|
if (_settings.ProxyInterpolationMode == EKCCInterpolationMode.Transform)
|
||
|
{
|
||
|
InterpolateNetworkTransform();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (isInSimulation == false)
|
||
|
{
|
||
|
_fixedData.Frame = frame;
|
||
|
_fixedData.Tick = Object.LastReceiveTick;
|
||
|
_fixedData.Alpha = 1.0f;
|
||
|
_fixedData.Time = _fixedData.Tick * deltaTime;
|
||
|
_fixedData.DeltaTime = deltaTime;
|
||
|
_fixedData.UpdateDeltaTime = deltaTime;
|
||
|
|
||
|
ReadNetworkData();
|
||
|
}
|
||
|
|
||
|
InterpolateNetworkData();
|
||
|
|
||
|
RefreshCollider(_renderData.IsActive);
|
||
|
|
||
|
if (_renderData.IsActive == true)
|
||
|
{
|
||
|
CacheProcessors(_renderData);
|
||
|
InvokeOnInterpolate(_renderData);
|
||
|
SynchronizeTransform(_renderData, true, true, false, true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_interpolationMarker.End();
|
||
|
}
|
||
|
|
||
|
_lastRenderPosition = _renderData.TargetPosition;
|
||
|
_lastRenderTime = _renderData.Time;
|
||
|
|
||
|
_debug.AfterRenderUpdate(this);
|
||
|
}
|
||
|
|
||
|
private void UpdatePredictionError()
|
||
|
{
|
||
|
if (_activeFeatures.Has(EKCCFeature.PredictionCorrection) == true && _renderData.Frame == _fixedData.Frame)
|
||
|
{
|
||
|
KCCData current = GetHistoryData(_renderData.Tick);
|
||
|
if (current != null && _lastRenderTime <= current.Time)
|
||
|
{
|
||
|
for (int i = 0; i < 5; ++i)
|
||
|
{
|
||
|
KCCData previous = GetHistoryData(current.Tick - 1);
|
||
|
if (previous == null)
|
||
|
break;
|
||
|
|
||
|
if (_lastRenderTime >= previous.Time)
|
||
|
{
|
||
|
if (current.HasTeleported == true || previous.HasTeleported == true)
|
||
|
{
|
||
|
_predictionError = default;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
float deltaTime = current.Time - previous.Time;
|
||
|
if (deltaTime <= 0.000001f)
|
||
|
{
|
||
|
_predictionError = default;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
float lastRenderAlpha = (_lastRenderTime - previous.Time) / deltaTime;
|
||
|
Vector3 expectedRenderPosition = Vector3.Lerp(previous.TargetPosition, current.TargetPosition, lastRenderAlpha);
|
||
|
#if UNITY_EDITOR
|
||
|
if (_debug.ShowPath == true)
|
||
|
{
|
||
|
UnityEngine.Debug.DrawLine(expectedRenderPosition, _lastRenderPosition, KCCDebug.PredictionErrorColor, _debug.DisplayTime);
|
||
|
}
|
||
|
#endif
|
||
|
_predictionError = _lastRenderPosition - expectedRenderPosition;
|
||
|
if (_predictionError.sqrMagnitude >= 1.0f)
|
||
|
{
|
||
|
_predictionError = default;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
_predictionError = Vector3.Lerp(_predictionError, Vector3.zero, _settings.PredictionCorrectionSpeed * Time.deltaTime);
|
||
|
|
||
|
_renderData.BasePosition += _predictionError;
|
||
|
_renderData.DesiredPosition += _predictionError;
|
||
|
_renderData.TargetPosition += _predictionError;
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
current = previous;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (_predictionError.IsAlmostZero(0.000001f) == true)
|
||
|
{
|
||
|
_predictionError = default;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_renderData.BasePosition -= _predictionError;
|
||
|
_renderData.DesiredPosition -= _predictionError;
|
||
|
_renderData.TargetPosition -= _predictionError;
|
||
|
|
||
|
_predictionError = Vector3.Lerp(_predictionError, Vector3.zero, _settings.PredictionCorrectionSpeed * Time.deltaTime);
|
||
|
|
||
|
_renderData.BasePosition += _predictionError;
|
||
|
_renderData.DesiredPosition += _predictionError;
|
||
|
_renderData.TargetPosition += _predictionError;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void MovePredicted(KCCData data)
|
||
|
{
|
||
|
_activeFeatures = _settings.Features;
|
||
|
|
||
|
float baseTime = data.Time;
|
||
|
float baseDeltaTime = data.DeltaTime;
|
||
|
Vector3 basePosition = data.TargetPosition;
|
||
|
Vector3 desiredPosition = data.TargetPosition;
|
||
|
bool wasGrounded = data.IsGrounded;
|
||
|
bool wasSteppingUp = data.IsSteppingUp;
|
||
|
bool wasSnappingToGround = data.IsSnappingToGround;
|
||
|
|
||
|
data.DeltaTime = baseDeltaTime;
|
||
|
data.BasePosition = basePosition;
|
||
|
data.DesiredPosition = desiredPosition;
|
||
|
|
||
|
SetBaseProperties(data);
|
||
|
|
||
|
if (data.IsActive == false)
|
||
|
{
|
||
|
data.ClearTransientProperties();
|
||
|
ForceRemoveAllCollisions(data);
|
||
|
ForceRemoveAllHits(data);
|
||
|
|
||
|
data.RealVelocity = default;
|
||
|
data.RealSpeed = default;
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
CacheProcessors(data);
|
||
|
|
||
|
ExecuteStageInternal<IBeginMove, BeginMove>(_beginMove, data);
|
||
|
|
||
|
if (data.IsActive == false)
|
||
|
{
|
||
|
data.ClearTransientProperties();
|
||
|
ForceRemoveAllCollisions(data);
|
||
|
ForceRemoveAllHits(data);
|
||
|
|
||
|
data.RealVelocity = default;
|
||
|
data.RealSpeed = default;
|
||
|
|
||
|
ExecuteStageInternal<IEndMove, EndMove>(_endMove, data);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
baseDeltaTime = data.DeltaTime;
|
||
|
basePosition = data.BasePosition;
|
||
|
|
||
|
if (baseDeltaTime < KCCSettings.ExtrapolationDeltaTimeThreshold)
|
||
|
{
|
||
|
Vector3 extrapolationVelocity = data.DesiredVelocity;
|
||
|
if (data.RealVelocity.sqrMagnitude <= extrapolationVelocity.sqrMagnitude)
|
||
|
{
|
||
|
extrapolationVelocity = data.RealVelocity;
|
||
|
}
|
||
|
|
||
|
desiredPosition = basePosition + extrapolationVelocity * baseDeltaTime;
|
||
|
|
||
|
data.BasePosition = basePosition;
|
||
|
data.DesiredPosition = desiredPosition;
|
||
|
data.TargetPosition = desiredPosition;
|
||
|
data.RealVelocity = extrapolationVelocity;
|
||
|
data.RealSpeed = data.RealVelocity.magnitude;
|
||
|
|
||
|
ExecuteStageInternal<IEndMove, EndMove>(_endMove, data);
|
||
|
|
||
|
InvokeOnStay(data);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ExecuteStageInternal<IPrepareData, PrepareData>(_prepareData, data);
|
||
|
|
||
|
ForceRemoveAllHits(data);
|
||
|
|
||
|
float pendingDeltaTime = Mathf.Clamp01(baseDeltaTime);
|
||
|
Vector3 pendingDeltaPosition = data.DesiredVelocity * pendingDeltaTime + data.ExternalDelta;
|
||
|
|
||
|
desiredPosition = data.BasePosition + pendingDeltaPosition;
|
||
|
|
||
|
data.DesiredPosition = desiredPosition;
|
||
|
data.TargetPosition = data.BasePosition;
|
||
|
data.ExternalDelta = default;
|
||
|
|
||
|
bool hasFinished = false;
|
||
|
float radiusMultiplier = Mathf.Clamp(_settings.CCDRadiusMultiplier, 0.1f, 0.9f);
|
||
|
float maxDeltaMagnitude = _settings.Radius * (radiusMultiplier + 0.1f);
|
||
|
float optimalDeltaMagnitude = _settings.Radius * radiusMultiplier;
|
||
|
Vector3 nonTeleportedPosition = data.TargetPosition;
|
||
|
|
||
|
while (hasFinished == false && data.HasTeleported == false)
|
||
|
{
|
||
|
data.BasePosition = data.TargetPosition;
|
||
|
|
||
|
float consumeDeltaTime = pendingDeltaTime;
|
||
|
Vector3 consumeDeltaPosition = pendingDeltaPosition;
|
||
|
|
||
|
if (_activeFeatures.Has(EKCCFeature.CCD) == true)
|
||
|
{
|
||
|
float consumeDeltaPositionMagnitude = consumeDeltaPosition.magnitude;
|
||
|
if (consumeDeltaPositionMagnitude > maxDeltaMagnitude)
|
||
|
{
|
||
|
float deltaRatio = optimalDeltaMagnitude / consumeDeltaPositionMagnitude;
|
||
|
|
||
|
consumeDeltaTime *= deltaRatio;
|
||
|
consumeDeltaPosition *= deltaRatio;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hasFinished = true;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hasFinished = true;
|
||
|
}
|
||
|
|
||
|
pendingDeltaTime -= consumeDeltaTime;
|
||
|
pendingDeltaPosition -= consumeDeltaPosition;
|
||
|
|
||
|
if (pendingDeltaTime <= 0.0f)
|
||
|
{
|
||
|
pendingDeltaTime = 0.0f;
|
||
|
}
|
||
|
|
||
|
data.Time = baseTime - pendingDeltaTime;
|
||
|
data.DeltaTime = consumeDeltaTime;
|
||
|
data.DesiredPosition = data.BasePosition + consumeDeltaPosition;
|
||
|
data.TargetPosition = data.DesiredPosition;
|
||
|
data.WasGrounded = data.IsGrounded;
|
||
|
data.WasSteppingUp = data.IsSteppingUp;
|
||
|
data.WasSnappingToGround = data.IsSnappingToGround;
|
||
|
|
||
|
ProcessMoveStep(data);
|
||
|
|
||
|
if (data.HasTeleported == false)
|
||
|
{
|
||
|
nonTeleportedPosition = data.TargetPosition;
|
||
|
}
|
||
|
|
||
|
UpdateCollisions(data, _trackOverlapInfo);
|
||
|
|
||
|
if (data.HasTeleported == true)
|
||
|
{
|
||
|
UpdateHits(data, null, EKCCHitsOverlapQuery.New);
|
||
|
UpdateCollisions(data, _trackOverlapInfo);
|
||
|
}
|
||
|
|
||
|
if (hasFinished == true && data.ExternalDelta.IsZero() == false)
|
||
|
{
|
||
|
pendingDeltaPosition += data.ExternalDelta;
|
||
|
data.ExternalDelta = default;
|
||
|
hasFinished = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
data.Time = baseTime;
|
||
|
data.DeltaTime = baseDeltaTime;
|
||
|
data.BasePosition = basePosition;
|
||
|
data.DesiredPosition = desiredPosition;
|
||
|
data.WasGrounded = wasGrounded;
|
||
|
data.WasSteppingUp = wasSteppingUp;
|
||
|
data.WasSnappingToGround = wasSnappingToGround;
|
||
|
data.RealVelocity = (nonTeleportedPosition - data.BasePosition) / data.DeltaTime;
|
||
|
data.RealSpeed = data.RealVelocity.magnitude;
|
||
|
|
||
|
Vector3 targetPosition = data.TargetPosition;
|
||
|
|
||
|
ExecuteStageInternal<IEndMove, EndMove>(_endMove, data);
|
||
|
|
||
|
if (data.TargetPosition.IsEqual(targetPosition) == false)
|
||
|
{
|
||
|
UpdateHits(data, null, EKCCHitsOverlapQuery.New);
|
||
|
UpdateCollisions(data, _trackOverlapInfo);
|
||
|
}
|
||
|
|
||
|
targetPosition = data.TargetPosition;
|
||
|
|
||
|
InvokeOnStay(data);
|
||
|
|
||
|
if (data.TargetPosition.IsEqual(targetPosition) == false)
|
||
|
{
|
||
|
UpdateHits(data, null, EKCCHitsOverlapQuery.New);
|
||
|
UpdateCollisions(data, _trackOverlapInfo);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void MoveInterpolated(KCCData data)
|
||
|
{
|
||
|
if (data.IsActive == false)
|
||
|
return;
|
||
|
|
||
|
KCCData currentFixedData = _fixedData;
|
||
|
if (currentFixedData.HasTeleported == true)
|
||
|
{
|
||
|
data.BasePosition = currentFixedData.BasePosition;
|
||
|
data.DesiredPosition = currentFixedData.DesiredPosition;
|
||
|
data.TargetPosition = currentFixedData.TargetPosition;
|
||
|
data.LookPitch = currentFixedData.LookPitch;
|
||
|
data.LookYaw = currentFixedData.LookYaw;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
KCCData previousFixedData = GetHistoryData(currentFixedData.Tick - 1);
|
||
|
if (previousFixedData != null)
|
||
|
{
|
||
|
float alpha = data.Alpha;
|
||
|
|
||
|
data.BasePosition = previousFixedData.TargetPosition + _predictionError;
|
||
|
data.DesiredPosition = currentFixedData.TargetPosition + _predictionError;
|
||
|
data.TargetPosition = Vector3.Lerp(previousFixedData.TargetPosition, currentFixedData.TargetPosition, alpha) + _predictionError;
|
||
|
data.RealVelocity = Vector3.Lerp(previousFixedData.RealVelocity, currentFixedData.RealVelocity, alpha);
|
||
|
data.RealSpeed = Mathf.Lerp(previousFixedData.RealSpeed, currentFixedData.RealSpeed, alpha);
|
||
|
|
||
|
if (IsPredictingLookRotation == false)
|
||
|
{
|
||
|
data.LookPitch = Mathf.Lerp(previousFixedData.LookPitch, currentFixedData.LookPitch, alpha);
|
||
|
data.LookYaw = KCCUtility.InterpolateRange(previousFixedData.LookYaw, currentFixedData.LookYaw, -180.0f, 180.0f, alpha);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
data.HasTeleported = false;
|
||
|
int historyTeleportTick = currentFixedData.Tick;
|
||
|
while (historyTeleportTick > _networkContext.LastInterpolationTeleportCounter)
|
||
|
{
|
||
|
KCCData historyData = GetHistoryData(historyTeleportTick);
|
||
|
if (historyData == null)
|
||
|
break;
|
||
|
|
||
|
if (historyData.HasTeleported == true)
|
||
|
{
|
||
|
data.HasTeleported = true;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
--historyTeleportTick;
|
||
|
}
|
||
|
_networkContext.LastInterpolationTeleportCounter = currentFixedData.Tick;
|
||
|
|
||
|
data.JumpFrames = 0;
|
||
|
int historyJumpTick = currentFixedData.Tick;
|
||
|
while (historyJumpTick > _networkContext.LastInterpolationJumpCounter)
|
||
|
{
|
||
|
KCCData historyData = GetHistoryData(historyJumpTick);
|
||
|
if (historyData == null)
|
||
|
break;
|
||
|
|
||
|
if (historyData.HasJumped == true)
|
||
|
{
|
||
|
data.JumpFrames = 1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
--historyJumpTick;
|
||
|
}
|
||
|
_networkContext.LastInterpolationJumpCounter = currentFixedData.Tick;
|
||
|
|
||
|
CacheProcessors(data);
|
||
|
InvokeOnInterpolate(data);
|
||
|
}
|
||
|
|
||
|
private void SetBaseProperties(KCCData data)
|
||
|
{
|
||
|
data.HasTeleported = default;
|
||
|
data.MaxPenetrationSteps = _settings.MaxPenetrationSteps;
|
||
|
|
||
|
if (data.Frame == _fixedData.Frame)
|
||
|
{
|
||
|
data.JumpFrames = default;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void ProcessMoveStep(KCCData data)
|
||
|
{
|
||
|
data.IsGrounded = default;
|
||
|
data.IsSteppingUp = default;
|
||
|
data.IsSnappingToGround = default;
|
||
|
data.GroundNormal = default;
|
||
|
data.GroundTangent = default;
|
||
|
data.GroundPosition = default;
|
||
|
data.GroundDistance = default;
|
||
|
data.GroundAngle = default;
|
||
|
|
||
|
ForceRemoveAllHits(data);
|
||
|
|
||
|
bool hasJumped = data.JumpFrames > 0;
|
||
|
|
||
|
if (_settings.CollisionLayerMask != 0 && _collider.IsSpawned == true)
|
||
|
{
|
||
|
float baseOverlapQueryExtent = _settings.Radius;
|
||
|
EKCCHitsOverlapQuery baseHitsOverlapQuery = EKCCHitsOverlapQuery.Default;
|
||
|
|
||
|
if (_settings.ForceSingleOverlapQuery == true)
|
||
|
{
|
||
|
baseOverlapQueryExtent = _settings.Extent;
|
||
|
baseHitsOverlapQuery = EKCCHitsOverlapQuery.Reuse;
|
||
|
}
|
||
|
|
||
|
CapsuleOverlap(_extendedOverlapInfo, data, data.TargetPosition, _settings.Radius, _settings.Height, baseOverlapQueryExtent, _settings.CollisionLayerMask, QueryTriggerInteraction.Collide);
|
||
|
|
||
|
ResolvePenetration(_extendedOverlapInfo, data, data.MaxPenetrationSteps, hasJumped == false, true);
|
||
|
|
||
|
UpdateHits(data, _extendedOverlapInfo, baseHitsOverlapQuery);
|
||
|
}
|
||
|
|
||
|
if (hasJumped == true)
|
||
|
{
|
||
|
data.IsGrounded = false;
|
||
|
}
|
||
|
|
||
|
_afterMoveStep.OverlapInfo.CopyFromOther(_extendedOverlapInfo);
|
||
|
|
||
|
ExecuteStageInternal<IAfterMoveStep, AfterMoveStep>(_afterMoveStep, data);
|
||
|
}
|
||
|
|
||
|
[Obsolete("Use SynchronizeTransform(KCCData data, bool synchronizePosition, bool synchronizeRotation, bool allowAntiJitter, bool moveRigidBody)")]
|
||
|
private void SynchronizeTransform(KCCData data, bool synchronizePosition, bool synchronizeRotation, bool allowAntiJitter)
|
||
|
{
|
||
|
SynchronizeTransform(data, synchronizePosition, synchronizeRotation, allowAntiJitter, false);
|
||
|
}
|
||
|
|
||
|
private void SynchronizeTransform(KCCData data, bool synchronizePosition, bool synchronizeRotation, bool allowAntiJitter, bool moveRigidBody)
|
||
|
{
|
||
|
if (synchronizePosition == true)
|
||
|
{
|
||
|
Vector3 targetPosition = data.TargetPosition;
|
||
|
|
||
|
if (moveRigidBody == true)
|
||
|
{
|
||
|
_rigidbody.MovePosition(targetPosition);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_rigidbody.position = targetPosition;
|
||
|
}
|
||
|
|
||
|
if (allowAntiJitter == true && _activeFeatures.Has(EKCCFeature.AntiJitter) == true && _settings.AntiJitterDistance.IsZero() == false)
|
||
|
{
|
||
|
Vector3 targetDelta = targetPosition - _lastAntiJitterPosition;
|
||
|
if (targetDelta.sqrMagnitude < 1.0f)
|
||
|
{
|
||
|
targetPosition = _lastAntiJitterPosition;
|
||
|
|
||
|
float distanceY = Mathf.Abs(targetDelta.y);
|
||
|
if (distanceY > 0.000001f && distanceY > _settings.AntiJitterDistance.y)
|
||
|
{
|
||
|
targetPosition.y += targetDelta.y * Mathf.Clamp01((distanceY - _settings.AntiJitterDistance.y) / distanceY);
|
||
|
}
|
||
|
|
||
|
Vector3 targetDeltaXZ = targetDelta.OnlyXZ();
|
||
|
|
||
|
float distanceXZ = Vector3.Magnitude(targetDeltaXZ);
|
||
|
if (distanceXZ > 0.000001f && distanceXZ > _settings.AntiJitterDistance.x)
|
||
|
{
|
||
|
targetPosition += targetDeltaXZ * Mathf.Clamp01((distanceXZ - _settings.AntiJitterDistance.x) / distanceXZ);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_lastAntiJitterPosition = targetPosition;
|
||
|
}
|
||
|
|
||
|
if (synchronizeRotation == true)
|
||
|
{
|
||
|
_transform.SetPositionAndRotation(targetPosition, data.TransformRotation);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_transform.position = targetPosition;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (synchronizeRotation == true)
|
||
|
{
|
||
|
_transform.rotation = data.TransformRotation;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void RefreshCollider(bool isActive)
|
||
|
{
|
||
|
if (isActive == false || _settings.Shape == EKCCShape.None)
|
||
|
{
|
||
|
_collider.Destroy();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
_settings.Radius = Mathf.Max(0.01f, _settings.Radius);
|
||
|
_settings.Height = Mathf.Max(_settings.Radius * 2.0f, _settings.Height);
|
||
|
_settings.Extent = Mathf.Max(0.0f, _settings.Extent);
|
||
|
|
||
|
_collider.Update(this);
|
||
|
}
|
||
|
|
||
|
private void SetDefaults(bool cleanup)
|
||
|
{
|
||
|
_debug.SetDefaults();
|
||
|
|
||
|
_fixedData.Clear();
|
||
|
_renderData.Clear();
|
||
|
_historyData.Clear();
|
||
|
_afterMoveStep.OverlapInfo.Reset(true);
|
||
|
_extendedOverlapInfo.Reset(true);
|
||
|
_trackOverlapInfo.Reset(true);
|
||
|
_childColliders.Clear();
|
||
|
_raycastHits.Clear();
|
||
|
_hitColliders.Clear();
|
||
|
_addColliders.Clear();
|
||
|
_removeColliders.Clear();
|
||
|
_removeCollisions.Clear();
|
||
|
_activeStages.Clear();
|
||
|
_cachedProcessors.Clear();
|
||
|
_stageProcessors.Clear();
|
||
|
_localProcessors.Clear();
|
||
|
|
||
|
_cachedProcessorCount = default;
|
||
|
_stageProcessorCount = default;
|
||
|
|
||
|
_rigidbody.isKinematic = true;
|
||
|
|
||
|
_activeFeatures = default;
|
||
|
_lastPredictedFixedTick = default;
|
||
|
_lastPredictedRenderFrame = default;
|
||
|
_lastPredictedLookRotationFrame = default;
|
||
|
_lastRenderTime = default;
|
||
|
_lastRenderPosition = default;
|
||
|
_lastAntiJitterPosition = default;
|
||
|
_lastNonNetworkedCollider = default;
|
||
|
_predictionError = default;
|
||
|
|
||
|
if (cleanup == true)
|
||
|
{
|
||
|
_isSpawned = default;
|
||
|
_isInFixedUpdate = default;
|
||
|
_hasManualUpdate = default;
|
||
|
|
||
|
_settings.CopyFromOther(_defaultSettings);
|
||
|
|
||
|
_collider.Destroy();
|
||
|
|
||
|
_onSpawn = default;
|
||
|
|
||
|
ResolveCollision = default;
|
||
|
OnCollisionEnter = default;
|
||
|
OnCollisionExit = default;
|
||
|
GetExternalProcessors = default;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void PublishFixedData(bool render, bool history)
|
||
|
{
|
||
|
if (render == true)
|
||
|
{
|
||
|
_renderData.CopyFromOther(_fixedData);
|
||
|
}
|
||
|
|
||
|
if (history == true)
|
||
|
{
|
||
|
KCCData historyData = _historyData[_fixedData.Tick % HISTORY_SIZE];
|
||
|
if (historyData == null)
|
||
|
{
|
||
|
historyData = new KCCData();
|
||
|
_historyData[_fixedData.Tick % HISTORY_SIZE] = historyData;
|
||
|
}
|
||
|
|
||
|
historyData.CopyFromOther(_fixedData);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|