//Shady using System; using System.Collections.Generic; using Unity.Profiling; using UnityEngine; namespace Fusion.Addons.SimpleKCC { [DisallowMultipleComponent] [RequireComponent(typeof(Rigidbody))] public sealed class KCCOffline : MonoBehaviour { internal const int CACHE_SIZE = 64; internal const int HISTORY_SIZE = 60; [SerializeField] private KCCSettings _settings = new(); private Transform _transform; private Rigidbody _rigidbody; private bool _isSpawned; private KCCCollider _collider = new(); private KCCData _fixedData = new(); private KCCData[] _historyData = new KCCData[60]; private KCCSettings _defaultSettings = new(); private KCCOverlapInfo _extendedOverlapInfo = new(64); private KCCOverlapInfo _trackOverlapInfo = new(64); private List _childColliders = new List(); private RaycastHit[] _raycastHits = new RaycastHit[64]; private Collider[] _hitColliders = new Collider[64]; private Collider[] _addColliders = new Collider[64]; private Collider[] _removeColliders = new Collider[64]; private KCCResolver _resolver = new(64); private Vector3 _lastAntiJitterPosition; private Vector3 _predictionError; private static ProfilerMarker _fixedUpdateMarker = new ProfilerMarker("KCC.FixedUpdate"); public Func ResolveCollision; internal bool IsSpawned => _isSpawned; public bool IsGrounded => Data.IsGrounded; public Vector3 RealVelocity => Data.RealVelocity; public Vector3 Position => Data.TargetPosition; public Quaternion LookRotation => Data.LookRotation; public Vector3 LookDirection => Data.LookDirection; public Quaternion TransformRotation => Data.TransformRotation; public Vector3 TransformDirection => Data.TransformDirection; public float RealSpeed => Data.RealSpeed; public bool HasJumped => Data.HasJumped; public bool IsActive => Data.IsActive; internal KCCData Data => _fixedData; internal KCCData FixedData => _fixedData; public KCCSettings Settings => _settings; public Transform Transform => _transform; public CapsuleCollider Collider => _collider.Collider; public Rigidbody Rigidbody => _rigidbody; internal bool IsInFixedUpdate => true; internal Vector3 PredictionError => _predictionError; private event Action _onSpawn; internal void SynchronizeTransform(bool synchronizePosition, bool synchronizeRotation, bool allowAntiJitter = true) { if (IsInFixedUpdate) { allowAntiJitter = false; } SynchronizeTransform(Data, synchronizePosition, synchronizeRotation, allowAntiJitter); } public void RefreshChildColliders() { _childColliders.Clear(); GetComponentsInChildren(includeInactive: true, _childColliders); int num = 0; int num2 = _childColliders.Count - 1; while (num <= num2) { Collider collider = _childColliders[num]; if (collider.isTrigger || collider == _collider.Collider) { _childColliders[num] = _childColliders[num2]; _childColliders.RemoveAt(num2); num2--; } else { num++; } } } internal KCCData GetHistoryData(int tick) { if (tick < 0) { return null; } KCCData kCCData = _historyData[tick % 60]; if (kCCData != null && kCCData.Tick == tick) { return kCCData; } return null; } internal void InvokeOnSpawn(Action callback) { if (_isSpawned) { callback(this); return; } _onSpawn -= callback; _onSpawn += callback; } internal void ManualFixedUpdate() { if (_isSpawned) { _fixedUpdateMarker.Begin(); OnFixedUpdateInternal(); _fixedUpdateMarker.End(); } } private void Awake() { _transform = base.transform; _rigidbody = GetComponent(); _rigidbody.isKinematic = true; RefreshCollider(); Spawned(); } private void OnDestroy() { SetDefaults(cleanup: true); } private void OnDrawGizmosSelected() { if (_settings != null) { float num = Mathf.Max(0.01f, _settings.Radius); float num2 = Mathf.Max(num * 2f, _settings.Height); Vector3 position = base.transform.position; Color color = Gizmos.color; Vector3 vector = position + Vector3.up * num; Vector3 vector2 = position + Vector3.up * (num2 - num); Vector3 vector3 = Vector3.forward * num; Vector3 vector4 = Vector3.back * num; Vector3 vector5 = Vector3.left * num; Vector3 vector6 = Vector3.right * num; Gizmos.color = Color.green; Gizmos.DrawWireSphere(vector, num); Gizmos.DrawWireSphere(vector2, num); Gizmos.DrawLine(vector + vector3, vector2 + vector3); Gizmos.DrawLine(vector + vector4, vector2 + vector4); Gizmos.DrawLine(vector + vector5, vector2 + vector5); Gizmos.DrawLine(vector + vector6, vector2 + vector6); if (_settings.Extent > 0f) { float radius = num + _settings.Extent; Gizmos.color = Color.yellow; Gizmos.DrawWireSphere(vector, radius); Gizmos.DrawWireSphere(vector2, radius); } Gizmos.color = color; } } public void Spawned() { if (_isSpawned) { throw new InvalidOperationException("[" + base.name + "] KCC is already spawned!"); } _defaultSettings.CopyFromOther(_settings); SetDefaults(cleanup: false); _isSpawned = true; // _isInFixedUpdate = true; KCCUtility.GetClampedLookRotationAngles(_transform.rotation, out var pitch, out var yaw); _fixedData = new(); _fixedData.Frame = Time.frameCount; // _fixedData.Tick = base.Runner.Tick.Raw; // _fixedData.Time = base.Runner.SimulationTime; _fixedData.DeltaTime = Time.deltaTime; _fixedData.UpdateDeltaTime = _fixedData.DeltaTime; _fixedData.Gravity = Physics.gravity; _fixedData.MaxGroundAngle = 60f; _fixedData.MaxWallAngle = 5f; _fixedData.MaxHangAngle = 30f; _fixedData.BasePosition = _transform.position; _fixedData.DesiredPosition = _transform.position; _fixedData.TargetPosition = _transform.position; _fixedData.LookPitch = pitch; _fixedData.LookYaw = yaw; // if (!base.Object.HasStateAuthority) // { // ReadNetworkData(); SynchronizeTransform(_fixedData, synchronizePosition: true, synchronizeRotation: true, allowAntiJitter: false); // } // _renderData = new(); // _renderData.CopyFromOther(_fixedData); // _lastRenderPosition = _renderData.TargetPosition; // _lastAntiJitterPosition = _renderData.TargetPosition; RefreshCollider(); RefreshChildColliders(); if (this._onSpawn != null) { try { this._onSpawn(this); } catch (Exception exception) { Debug.LogException(exception); } this._onSpawn = null; } } private void OnFixedUpdateInternal() { if (!IsInFixedUpdate) { throw new InvalidOperationException("[" + base.name + "] KCC fixed update called from render update! This is not allowed."); } RefreshCollider(); MovePredicted(_fixedData); if (_fixedData.IsActive) { SynchronizeTransform(_fixedData, synchronizePosition: true, synchronizeRotation: true, allowAntiJitter: false); } } private void MovePredicted(KCCData data) { float time = data.Time; float deltaTime = data.DeltaTime; Vector3 targetPosition = data.TargetPosition; Vector3 targetPosition2 = data.TargetPosition; bool isGrounded = data.IsGrounded; bool isSteppingUp = data.IsSteppingUp; bool isSnappingToGround = data.IsSnappingToGround; data.DeltaTime = deltaTime; data.BasePosition = targetPosition; data.DesiredPosition = targetPosition2; if (!data.IsActive) { data.ClearTransientProperties(); ForceRemoveAllHits(data); return; } SetBaseProperties(data); if (!data.IsActive) { data.ClearTransientProperties(); ForceRemoveAllHits(data); return; } deltaTime = data.DeltaTime; targetPosition = data.BasePosition; if (deltaTime < KCCSettings.ExtrapolationDeltaTimeThreshold) { Vector3 vector = data.DesiredVelocity; if (data.RealVelocity.sqrMagnitude <= vector.sqrMagnitude) { vector = data.RealVelocity; } targetPosition2 = targetPosition + vector * deltaTime; data.BasePosition = targetPosition; data.DesiredPosition = targetPosition2; data.TargetPosition = targetPosition2; return; } EnvironmentProcessor.SetDynamicVelocity(this, data); ForceRemoveAllHits(data); float num = Mathf.Clamp01(deltaTime); Vector3 vector2 = data.DesiredVelocity * num + data.ExternalDelta; targetPosition2 = (data.DesiredPosition = data.BasePosition + vector2); data.TargetPosition = data.BasePosition; data.ExternalDelta = default(Vector3); bool flag = false; float num2 = Mathf.Clamp(_settings.CCDRadiusMultiplier, 0.25f, 0.75f); float num3 = _settings.Radius * (num2 + 0.1f); float num4 = _settings.Radius * num2; Vector3 targetPosition3 = data.TargetPosition; while (!flag && !data.HasTeleported) { data.BasePosition = data.TargetPosition; float num5 = num; Vector3 vector3 = vector2; float magnitude = vector3.magnitude; if (magnitude > num3) { float num6 = num4 / magnitude; num5 *= num6; vector3 *= num6; } else { flag = true; } num -= num5; vector2 -= vector3; if (num <= 0f) { num = 0f; } data.Time = time - num; data.DeltaTime = num5; data.DesiredPosition = data.BasePosition + vector3; data.TargetPosition = data.DesiredPosition; data.WasGrounded = data.IsGrounded; data.WasSteppingUp = data.IsSteppingUp; data.WasSnappingToGround = data.IsSnappingToGround; ProcessMoveStep(data); if (!data.HasTeleported) { targetPosition3 = data.TargetPosition; } if (data.HasTeleported) { UpdateHits(data, null, EKCCHitsOverlapQuery.New); } if (flag && !data.ExternalDelta.IsZero()) { vector2 += data.ExternalDelta; data.ExternalDelta = default(Vector3); flag = false; } } data.Time = time; data.DeltaTime = deltaTime; data.BasePosition = targetPosition; data.DesiredPosition = targetPosition2; data.WasGrounded = isGrounded; data.WasSteppingUp = isSteppingUp; data.WasSnappingToGround = isSnappingToGround; data.RealVelocity = (targetPosition3 - data.BasePosition) / data.DeltaTime; data.RealSpeed = data.RealVelocity.magnitude; Vector3 targetPosition4 = data.TargetPosition; if (!data.TargetPosition.IsEqual(targetPosition4)) { UpdateHits(data, null, EKCCHitsOverlapQuery.New); } targetPosition4 = data.TargetPosition; if (!data.TargetPosition.IsEqual(targetPosition4)) { UpdateHits(data, null, EKCCHitsOverlapQuery.New); } } private void SetBaseProperties(KCCData data) { data.HasTeleported = false; data.MaxPenetrationSteps = _settings.MaxPenetrationSteps; if (data.Frame == _fixedData.Frame) { data.JumpFrames = 0; } } private void ProcessMoveStep(KCCData data) { data.IsGrounded = false; data.IsSteppingUp = false; data.IsSnappingToGround = false; data.GroundNormal = default(Vector3); data.GroundTangent = default(Vector3); data.GroundPosition = default(Vector3); data.GroundDistance = 0f; data.GroundAngle = 0f; ForceRemoveAllHits(data); bool flag = data.JumpFrames > 0; if ((int)_settings.CollisionLayerMask != 0 && _collider.IsSpawned) { float radius = _settings.Radius; EKCCHitsOverlapQuery overlapQuery = EKCCHitsOverlapQuery.Default; CapsuleOverlap(_extendedOverlapInfo, data, data.TargetPosition, _settings.Radius, _settings.Height, radius, _settings.CollisionLayerMask, QueryTriggerInteraction.Collide); data.TargetPosition = ResolvePenetration(_extendedOverlapInfo, data, data.BasePosition, data.TargetPosition, !flag, data.MaxPenetrationSteps, 3, resolveTriggers: true); UpdateHits(data, _extendedOverlapInfo, overlapQuery); } if (flag) { data.IsGrounded = false; } EnvironmentProcessor.AfterMoveStep(this, data); StepUpProcessor.AfterMoveStep(this, data, _extendedOverlapInfo); GroundSnapProcessor.AfterMoveStep(this, data, _extendedOverlapInfo); } private void SynchronizeTransform(KCCData data, bool synchronizePosition, bool synchronizeRotation, bool allowAntiJitter) { if (synchronizePosition) { Vector3 vector = data.TargetPosition; _rigidbody.position = vector; if (allowAntiJitter && !_settings.AntiJitterDistance.IsZero()) { Vector3 vector2 = vector - _lastAntiJitterPosition; if (vector2.sqrMagnitude < _settings.TeleportThreshold * _settings.TeleportThreshold) { vector = _lastAntiJitterPosition; float num = Mathf.Abs(vector2.y); if (num > 1E-06f) { vector.y += vector2.y * Mathf.Clamp01((num - _settings.AntiJitterDistance.y) / num); } Vector3 vector3 = vector2.OnlyXZ(); float num2 = Vector3.Magnitude(vector3); if (num2 > 1E-06f) { vector += vector3 * Mathf.Clamp01((num2 - _settings.AntiJitterDistance.x) / num2); } } _lastAntiJitterPosition = vector; } if (synchronizeRotation) { _transform.SetPositionAndRotation(vector, data.TransformRotation); } else { _transform.position = vector; } } else if (synchronizeRotation) { _transform.rotation = data.TransformRotation; } } private void RefreshCollider() { if (_settings.Shape == EKCCShape.None) { _collider.Destroy(); return; } _settings.Radius = Mathf.Max(0.01f, _settings.Radius); _settings.Height = Mathf.Max(_settings.Radius * 2f, _settings.Height); _settings.Extent = Mathf.Max(0f, _settings.Extent); _collider.Update(this); } private void SetDefaults(bool cleanup) { _fixedData.Clear(); // _renderData.Clear(); Array.Clear(_historyData, 0, _historyData.Length); _extendedOverlapInfo.Reset(deep: true); _trackOverlapInfo.Reset(deep: true); _childColliders.Clear(); Array.Clear(_raycastHits, 0, _raycastHits.Length); Array.Clear(_hitColliders, 0, _hitColliders.Length); Array.Clear(_addColliders, 0, _addColliders.Length); Array.Clear(_removeColliders, 0, _removeColliders.Length); // _raycastHits.Clear(); // _hitColliders.Clear(); // _addColliders.Clear(); // _removeColliders.Clear(); _rigidbody.isKinematic = true; // _lastRenderTime = 0f; // _lastRenderPosition = default(Vector3); _lastAntiJitterPosition = default(Vector3); _predictionError = default(Vector3); if (cleanup) { _isSpawned = false; // _isInFixedUpdate = false; _settings.CopyFromOther(_defaultSettings); _collider.Destroy(); this._onSpawn = null; ResolveCollision = null; } } internal Vector3 ResolvePenetration(KCCOverlapInfo overlapInfo, KCCData data, Vector3 basePosition, Vector3 targetPosition, bool probeGrounding, int maxSteps, int resolverIterations, bool resolveTriggers) { if (_settings.SuppressConvexMeshColliders) { overlapInfo.ToggleConvexMeshColliders(convex: false); } if (overlapInfo.ColliderHitCount == 1) { targetPosition = DepenetrateSingle(overlapInfo, data, basePosition, targetPosition, probeGrounding, maxSteps); } else if (overlapInfo.ColliderHitCount > 1) { targetPosition = DepenetrateMultiple(overlapInfo, data, basePosition, targetPosition, probeGrounding, maxSteps, resolverIterations); } RecalculateGroundProperties(data); if (resolveTriggers) { for (int i = 0; i < overlapInfo.TriggerHitCount; i++) { KCCOverlapHit kCCOverlapHit = overlapInfo.TriggerHits[i]; kCCOverlapHit.Transform.GetPositionAndRotation(out kCCOverlapHit.CachedPosition, out kCCOverlapHit.CachedRotation); kCCOverlapHit.CollisionType = ((kCCOverlapHit.IsWithinExtent = (kCCOverlapHit.HasPenetration = Physics.ComputePenetration(_collider.Collider, data.TargetPosition, Quaternion.identity, kCCOverlapHit.Collider, kCCOverlapHit.CachedPosition, kCCOverlapHit.CachedRotation, out var _, out var distance))) ? ECollisionType.Trigger : ECollisionType.None); if (distance > kCCOverlapHit.MaxPenetration) { kCCOverlapHit.MaxPenetration = distance; } } } if (_settings.SuppressConvexMeshColliders) { overlapInfo.ToggleConvexMeshColliders(convex: true); } return targetPosition; } private Vector3 DepenetrateSingle(KCCOverlapInfo overlapInfo, KCCData data, Vector3 basePosition, Vector3 targetPosition, bool probeGrounding, int maxSteps) { float num = Mathf.Cos(Mathf.Clamp(data.MaxGroundAngle, 0f, 90f) * (MathF.PI / 180f)); float num2 = 0f - Mathf.Cos(Mathf.Clamp(90f - data.MaxWallAngle, 0f, 90f) * (MathF.PI / 180f)); float num3 = 0f - Mathf.Cos(Mathf.Clamp(90f - data.MaxHangAngle, 0f, 90f) * (MathF.PI / 180f)); Vector3 vector = Vector3.up; float num4 = 0f; KCCOverlapHit kCCOverlapHit = overlapInfo.ColliderHits[0]; kCCOverlapHit.UpDirectionDot = float.MinValue; kCCOverlapHit.Transform.GetPositionAndRotation(out kCCOverlapHit.CachedPosition, out kCCOverlapHit.CachedRotation); if (maxSteps > 1) { float num5 = 0.001f; float num6 = Vector3.Distance(basePosition, targetPosition); if (num6 < (float)maxSteps * num5) { maxSteps = Mathf.Max(1, (int)(num6 / num5)); } } if (maxSteps <= 1) { kCCOverlapHit.HasPenetration = Physics.ComputePenetration(_collider.Collider, targetPosition, Quaternion.identity, kCCOverlapHit.Collider, kCCOverlapHit.CachedPosition, kCCOverlapHit.CachedRotation, out var direction, out var distance); if (kCCOverlapHit.HasPenetration) { kCCOverlapHit.IsWithinExtent = true; if (distance > kCCOverlapHit.MaxPenetration) { kCCOverlapHit.MaxPenetration = distance; } float num7 = Vector3.Dot(direction, Vector3.up); if (num7 > kCCOverlapHit.UpDirectionDot) { kCCOverlapHit.UpDirectionDot = num7; if (num7 >= num) { kCCOverlapHit.CollisionType = ECollisionType.Ground; data.IsGrounded = true; vector = direction; } else if (num7 > 0f - num2) { kCCOverlapHit.CollisionType = ECollisionType.Slope; } else if (num7 >= num2) { kCCOverlapHit.CollisionType = ECollisionType.Wall; } else if (num7 >= num3) { kCCOverlapHit.CollisionType = ECollisionType.Hang; } else { kCCOverlapHit.CollisionType = ECollisionType.Top; } } if (num7 > 0f && num7 < num && distance >= 1E-06f && data.DynamicVelocity.y <= 0f && Vector3.Dot((targetPosition - basePosition).OnlyXZ(), direction.OnlyXZ()) < 0f) { KCCPhysicsUtility.ProjectVerticalPenetration(ref direction, ref distance); } targetPosition += direction * distance; } } else { Vector3 vector2 = (targetPosition - basePosition) / maxSteps; Vector3 vector3 = basePosition; int num8 = maxSteps; while (num8 > 0) { num8--; vector3 += vector2; kCCOverlapHit.HasPenetration = Physics.ComputePenetration(_collider.Collider, vector3, Quaternion.identity, kCCOverlapHit.Collider, kCCOverlapHit.CachedPosition, kCCOverlapHit.CachedRotation, out var direction2, out var distance2); if (!kCCOverlapHit.HasPenetration) { continue; } kCCOverlapHit.IsWithinExtent = true; if (distance2 > kCCOverlapHit.MaxPenetration) { kCCOverlapHit.MaxPenetration = distance2; } float num9 = Vector3.Dot(direction2, Vector3.up); if (num9 > kCCOverlapHit.UpDirectionDot) { kCCOverlapHit.UpDirectionDot = num9; if (num9 >= num) { kCCOverlapHit.CollisionType = ECollisionType.Ground; data.IsGrounded = true; vector = direction2; } else if (num9 > 0f - num2) { kCCOverlapHit.CollisionType = ECollisionType.Slope; } else if (num9 >= num2) { kCCOverlapHit.CollisionType = ECollisionType.Wall; } else if (num9 >= num3) { kCCOverlapHit.CollisionType = ECollisionType.Hang; } else { kCCOverlapHit.CollisionType = ECollisionType.Top; } } if (num9 > 0f && num9 < num && distance2 >= 1E-06f && data.DynamicVelocity.y <= 0f && Vector3.Dot(vector2.OnlyXZ(), direction2.OnlyXZ()) < 0f) { KCCPhysicsUtility.ProjectVerticalPenetration(ref direction2, ref distance2); } vector3 += direction2 * distance2; } targetPosition = vector3; } if (kCCOverlapHit.UpDirectionDot == float.MinValue) { kCCOverlapHit.UpDirectionDot = 0f; } if (probeGrounding && !data.IsGrounded) { if (KCCPhysicsUtility.CheckGround(_collider.Collider, targetPosition, kCCOverlapHit.Collider, kCCOverlapHit.CachedPosition, kCCOverlapHit.CachedRotation, _settings.Radius, _settings.Height, _settings.Extent, num, out var groundNormal, out var groundDistance, out var isWithinExtent)) { data.IsGrounded = true; vector = groundNormal; num4 = groundDistance; kCCOverlapHit.IsWithinExtent = true; kCCOverlapHit.CollisionType = ECollisionType.Ground; } else if (isWithinExtent) { kCCOverlapHit.IsWithinExtent = true; if (kCCOverlapHit.CollisionType == ECollisionType.None) { kCCOverlapHit.CollisionType = ECollisionType.Slope; } } } if (data.IsGrounded) { data.GroundNormal = vector; data.GroundAngle = Vector3.Angle(vector, Vector3.up); data.GroundPosition = targetPosition + new Vector3(0f, _settings.Radius, 0f) - vector * (_settings.Radius + num4); data.GroundDistance = num4; } return targetPosition; } private Vector3 DepenetrateMultiple(KCCOverlapInfo overlapInfo, KCCData data, Vector3 basePosition, Vector3 targetPosition, bool probeGrounding, int maxSteps, int resolverIterations) { float num = Mathf.Cos(Mathf.Clamp(data.MaxGroundAngle, 0f, 90f) * (MathF.PI / 180f)); float num2 = 0f - Mathf.Cos(Mathf.Clamp(90f - data.MaxWallAngle, 0f, 90f) * (MathF.PI / 180f)); float num3 = 0f - Mathf.Cos(Mathf.Clamp(90f - data.MaxHangAngle, 0f, 90f) * (MathF.PI / 180f)); float num4 = 0f; float num5 = 0f; Vector3 other = default(Vector3); Vector3 vector = default(Vector3); Vector3 lhs = (targetPosition - basePosition).OnlyXZ(); for (int i = 0; i < overlapInfo.ColliderHitCount; i++) { KCCOverlapHit kCCOverlapHit = overlapInfo.ColliderHits[i]; kCCOverlapHit.UpDirectionDot = float.MinValue; kCCOverlapHit.Transform.GetPositionAndRotation(out kCCOverlapHit.CachedPosition, out kCCOverlapHit.CachedRotation); } if (maxSteps > 1) { float num6 = 0.001f; float num7 = Vector3.Distance(basePosition, targetPosition); if (num7 < (float)maxSteps * num6) { maxSteps = Mathf.Max(1, (int)(num7 / num6)); } } if (maxSteps <= 1) { _resolver.Reset(); for (int j = 0; j < overlapInfo.ColliderHitCount; j++) { KCCOverlapHit kCCOverlapHit2 = overlapInfo.ColliderHits[j]; kCCOverlapHit2.HasPenetration = Physics.ComputePenetration(_collider.Collider, targetPosition, Quaternion.identity, kCCOverlapHit2.Collider, kCCOverlapHit2.CachedPosition, kCCOverlapHit2.CachedRotation, out var direction, out var distance); if (!kCCOverlapHit2.HasPenetration) { continue; } kCCOverlapHit2.IsWithinExtent = true; if (distance > kCCOverlapHit2.MaxPenetration) { kCCOverlapHit2.MaxPenetration = distance; } float num8 = Vector3.Dot(direction, Vector3.up); if (num8 > kCCOverlapHit2.UpDirectionDot) { kCCOverlapHit2.UpDirectionDot = num8; if (num8 >= num) { kCCOverlapHit2.CollisionType = ECollisionType.Ground; data.IsGrounded = true; if (num8 >= num5) { num5 = num8; other = direction; } vector += direction * num8; } else if (num8 > 0f - num2) { kCCOverlapHit2.CollisionType = ECollisionType.Slope; } else if (num8 >= num2) { kCCOverlapHit2.CollisionType = ECollisionType.Wall; } else if (num8 >= num3) { kCCOverlapHit2.CollisionType = ECollisionType.Hang; } else { kCCOverlapHit2.CollisionType = ECollisionType.Top; } } if (num8 > 0f && num8 < num && distance >= 1E-06f && data.DynamicVelocity.y <= 0f && Vector3.Dot(lhs, direction.OnlyXZ()) < 0f) { KCCPhysicsUtility.ProjectVerticalPenetration(ref direction, ref distance); } _resolver.AddCorrection(direction, distance); } int num9 = Mathf.Max(0, resolverIterations); float num10 = 1f - (float)Mathf.Min(num9, 2) * 0.25f; if (_resolver.Size == 2) { _resolver.GetCorrection(0, out var direction2); _resolver.GetCorrection(1, out var direction3); if (Vector3.Dot(direction2, direction3) >= 0f) { targetPosition += _resolver.CalculateMinMax() * num10; } else { targetPosition += _resolver.CalculateBinary() * num10; } } else { targetPosition += _resolver.CalculateGradientDescent(12, 0.0001f) * num10; } while (num9 > 0) { num9--; _resolver.Reset(); for (int k = 0; k < overlapInfo.ColliderHitCount; k++) { KCCOverlapHit kCCOverlapHit3 = overlapInfo.ColliderHits[k]; if (!Physics.ComputePenetration(_collider.Collider, targetPosition, Quaternion.identity, kCCOverlapHit3.Collider, kCCOverlapHit3.CachedPosition, kCCOverlapHit3.CachedRotation, out var direction4, out var distance2)) { continue; } kCCOverlapHit3.IsWithinExtent = true; kCCOverlapHit3.HasPenetration = true; if (distance2 > kCCOverlapHit3.MaxPenetration) { kCCOverlapHit3.MaxPenetration = distance2; } float num11 = Vector3.Dot(direction4, Vector3.up); if (num11 > kCCOverlapHit3.UpDirectionDot) { kCCOverlapHit3.UpDirectionDot = num11; if (num11 >= num) { kCCOverlapHit3.CollisionType = ECollisionType.Ground; data.IsGrounded = true; if (num11 >= num5) { num5 = num11; other = direction4; } vector += direction4 * num11; } else if (num11 > 0f - num2) { kCCOverlapHit3.CollisionType = ECollisionType.Slope; } else if (num11 >= num2) { kCCOverlapHit3.CollisionType = ECollisionType.Wall; } else if (num11 >= num3) { kCCOverlapHit3.CollisionType = ECollisionType.Hang; } else { kCCOverlapHit3.CollisionType = ECollisionType.Top; } } if (num11 > 0f && num11 < num && distance2 >= 1E-06f && data.DynamicVelocity.y <= 0f && Vector3.Dot(lhs, direction4.OnlyXZ()) < 0f) { KCCPhysicsUtility.ProjectVerticalPenetration(ref direction4, ref distance2); } _resolver.AddCorrection(direction4, distance2); } if (_resolver.Size == 0) { break; } switch (num9) { case 0: if (_resolver.Size == 2) { _resolver.GetCorrection(0, out var direction5); _resolver.GetCorrection(1, out var direction6); if (Vector3.Dot(direction5, direction6) >= 0f) { targetPosition += _resolver.CalculateGradientDescent(12, 0.0001f); } else { targetPosition += _resolver.CalculateBinary(); } } else { targetPosition += _resolver.CalculateGradientDescent(12, 0.0001f); } break; case 1: targetPosition += _resolver.CalculateMinMax() * 0.75f; break; default: targetPosition += _resolver.CalculateMinMax() * 0.5f; break; } } } else { Vector3 vector2 = (targetPosition - basePosition) / maxSteps; Vector3 vector3 = basePosition; int num12 = maxSteps; while (num12 > 1) { num12--; vector3 += vector2; _resolver.Reset(); for (int l = 0; l < overlapInfo.ColliderHitCount; l++) { KCCOverlapHit kCCOverlapHit4 = overlapInfo.ColliderHits[l]; kCCOverlapHit4.HasPenetration = Physics.ComputePenetration(_collider.Collider, vector3, Quaternion.identity, kCCOverlapHit4.Collider, kCCOverlapHit4.CachedPosition, kCCOverlapHit4.CachedRotation, out var direction7, out var distance3); if (!kCCOverlapHit4.HasPenetration) { continue; } kCCOverlapHit4.IsWithinExtent = true; if (distance3 > kCCOverlapHit4.MaxPenetration) { kCCOverlapHit4.MaxPenetration = distance3; } float num13 = Vector3.Dot(direction7, Vector3.up); if (num13 > kCCOverlapHit4.UpDirectionDot) { kCCOverlapHit4.UpDirectionDot = num13; if (num13 >= num) { kCCOverlapHit4.CollisionType = ECollisionType.Ground; data.IsGrounded = true; if (num13 >= num5) { num5 = num13; other = direction7; } vector += direction7 * num13; } else if (num13 > 0f - num2) { kCCOverlapHit4.CollisionType = ECollisionType.Slope; } else if (num13 >= num2) { kCCOverlapHit4.CollisionType = ECollisionType.Wall; } else if (num13 >= num3) { kCCOverlapHit4.CollisionType = ECollisionType.Hang; } else { kCCOverlapHit4.CollisionType = ECollisionType.Top; } } if (num13 > 0f && num13 < num && distance3 >= 1E-06f && data.DynamicVelocity.y <= 0f && Vector3.Dot(vector2.OnlyXZ(), direction7.OnlyXZ()) < 0f) { KCCPhysicsUtility.ProjectVerticalPenetration(ref direction7, ref distance3); } _resolver.AddCorrection(direction7, distance3); } if (_resolver.Size == 2) { _resolver.GetCorrection(0, out var direction8); _resolver.GetCorrection(1, out var direction9); if (Vector3.Dot(direction8, direction9) >= 0f) { vector3 += _resolver.CalculateMinMax(); } else { vector3 += _resolver.CalculateBinary(); } } else { vector3 += _resolver.CalculateMinMax(); } } num12--; vector3 += vector2; _resolver.Reset(); for (int m = 0; m < overlapInfo.ColliderHitCount; m++) { KCCOverlapHit kCCOverlapHit5 = overlapInfo.ColliderHits[m]; kCCOverlapHit5.HasPenetration = Physics.ComputePenetration(_collider.Collider, vector3, Quaternion.identity, kCCOverlapHit5.Collider, kCCOverlapHit5.CachedPosition, kCCOverlapHit5.CachedRotation, out var direction10, out var distance4); if (!kCCOverlapHit5.HasPenetration) { continue; } kCCOverlapHit5.IsWithinExtent = true; if (distance4 > kCCOverlapHit5.MaxPenetration) { kCCOverlapHit5.MaxPenetration = distance4; } float num14 = Vector3.Dot(direction10, Vector3.up); if (num14 > kCCOverlapHit5.UpDirectionDot) { kCCOverlapHit5.UpDirectionDot = num14; if (num14 >= num) { kCCOverlapHit5.CollisionType = ECollisionType.Ground; data.IsGrounded = true; if (num14 >= num5) { num5 = num14; other = direction10; } vector += direction10 * num14; } else if (num14 > 0f - num2) { kCCOverlapHit5.CollisionType = ECollisionType.Slope; } else if (num14 >= num2) { kCCOverlapHit5.CollisionType = ECollisionType.Wall; } else if (num14 >= num3) { kCCOverlapHit5.CollisionType = ECollisionType.Hang; } else { kCCOverlapHit5.CollisionType = ECollisionType.Top; } } if (num14 > 0f && num14 < num && distance4 >= 1E-06f && data.DynamicVelocity.y <= 0f && Vector3.Dot(vector2.OnlyXZ(), direction10.OnlyXZ()) < 0f) { KCCPhysicsUtility.ProjectVerticalPenetration(ref direction10, ref distance4); } _resolver.AddCorrection(direction10, distance4); } if (_resolver.Size == 2) { _resolver.GetCorrection(0, out var direction11); _resolver.GetCorrection(1, out var direction12); if (Vector3.Dot(direction11, direction12) >= 0f) { vector3 += _resolver.CalculateMinMax(); } else { vector3 += _resolver.CalculateBinary(); } } else { vector3 += _resolver.CalculateGradientDescent(12, 0.0001f); } targetPosition = vector3; } for (int n = 0; n < overlapInfo.ColliderHitCount; n++) { KCCOverlapHit kCCOverlapHit6 = overlapInfo.ColliderHits[n]; if (kCCOverlapHit6.UpDirectionDot == float.MinValue) { kCCOverlapHit6.UpDirectionDot = 0f; } } if (probeGrounding && !data.IsGrounded) { Vector3 vector4 = Vector3.up; float num15 = 1000f; for (int num16 = 0; num16 < overlapInfo.ColliderHitCount; num16++) { KCCOverlapHit kCCOverlapHit7 = overlapInfo.ColliderHits[num16]; if (KCCPhysicsUtility.CheckGround(_collider.Collider, targetPosition, kCCOverlapHit7.Collider, kCCOverlapHit7.CachedPosition, kCCOverlapHit7.CachedRotation, _settings.Radius, _settings.Height, _settings.Extent, num, out var groundNormal, out var groundDistance, out var isWithinExtent)) { data.IsGrounded = true; if (groundDistance < num15) { vector4 = groundNormal; num15 = groundDistance; } kCCOverlapHit7.IsWithinExtent = true; kCCOverlapHit7.CollisionType = ECollisionType.Ground; } else if (isWithinExtent) { kCCOverlapHit7.IsWithinExtent = true; if (kCCOverlapHit7.CollisionType == ECollisionType.None) { kCCOverlapHit7.CollisionType = ECollisionType.Slope; } } } if (data.IsGrounded) { other = vector4; vector = vector4; num4 = num15; } } if (data.IsGrounded) { if (!vector.IsEqual(other)) { vector.Normalize(); } data.GroundNormal = vector; data.GroundAngle = Vector3.Angle(data.GroundNormal, Vector3.up); data.GroundPosition = targetPosition + new Vector3(0f, _settings.Radius, 0f) - data.GroundNormal * (_settings.Radius + num4); data.GroundDistance = num4; } return targetPosition; } private static void RecalculateGroundProperties(KCCData data) { if (data.IsGrounded) { Vector3 projectedVector2; if (KCCPhysicsUtility.ProjectOnGround(data.GroundNormal, data.GroundNormal.OnlyXZ(), out var projectedVector)) { data.GroundTangent = projectedVector.normalized; } else if (KCCPhysicsUtility.ProjectOnGround(data.GroundNormal, data.DesiredVelocity.OnlyXZ(), out projectedVector2)) { data.GroundTangent = projectedVector2.normalized; } else { data.GroundTangent = data.TransformDirection; } } } public void SetActive(bool isActive) => _fixedData.IsActive = isActive; public Vector2 GetLookRotation(bool pitch = true, bool yaw = true) => Data.GetLookRotation(pitch, yaw); public void AddLookRotation(float pitchDelta, float yawDelta) { KCCData kCCData = _fixedData; kCCData.AddLookRotation(pitchDelta, yawDelta); SynchronizeTransform(kCCData, synchronizePosition: false, synchronizeRotation: true, allowAntiJitter: false); } public void AddLookRotation(float pitchDelta, float yawDelta, float minPitch, float maxPitch) { KCCData kCCData = _fixedData; kCCData.AddLookRotation(pitchDelta, yawDelta, minPitch, maxPitch); SynchronizeTransform(kCCData, synchronizePosition: false, synchronizeRotation: true, allowAntiJitter: false); } public void AddLookRotation(Vector2 lookRotationDelta) { AddLookRotation(lookRotationDelta.x, lookRotationDelta.y); } public void AddLookRotation(Vector2 lookRotationDelta, float minPitch, float maxPitch) { AddLookRotation(lookRotationDelta.x, lookRotationDelta.y, minPitch, maxPitch); } public void SetLookRotation(float pitch, float yaw) { KCCData kCCData = _fixedData; kCCData.SetLookRotation(pitch, yaw); SynchronizeTransform(kCCData, synchronizePosition: false, synchronizeRotation: true, allowAntiJitter: false); } public void SetLookRotation(Vector2 lookRotation) => SetLookRotation(lookRotation.x, lookRotation.y); public void SetLookRotation(Quaternion lookRotation, bool preservePitch = false, bool preserveYaw = false) { KCCData kCCData = _fixedData; kCCData.SetLookRotation(lookRotation, preservePitch, preserveYaw); SynchronizeTransform(kCCData, synchronizePosition: false, synchronizeRotation: true, allowAntiJitter: false); } public void SetPosition(Vector3 position) { KCCData kCCData = _fixedData; kCCData.BasePosition = position; kCCData.DesiredPosition = position; kCCData.TargetPosition = position; kCCData.HasTeleported = true; kCCData.IsSteppingUp = false; kCCData.IsSnappingToGround = false; SynchronizeTransform(kCCData, synchronizePosition: true, synchronizeRotation: false, allowAntiJitter: false); } public void SetShape(EKCCShape shape, float radius = 0f, float height = 0f) { _settings.Shape = shape; if (radius > 0f) { _settings.Radius = radius; } if (height > 0f) { _settings.Height = height; } RefreshCollider(); } public void SetTrigger(bool isTrigger) { _settings.IsTrigger = isTrigger; RefreshCollider(); } public void SetRadius(float radius) { if (!(radius <= 0f)) { _settings.Radius = radius; RefreshCollider(); } } public void SetHeight(float height) { if (!(height <= 0f)) { _settings.Height = height; RefreshCollider(); } } public void SetColliderLayer(int layer) { _settings.ColliderLayer = layer; RefreshCollider(); } public void SetCollisionLayerMask(LayerMask layerMask) { _settings.CollisionLayerMask = layerMask; } private void RestoreHistoryData(KCCData historyData) { if (_fixedData.IsGrounded) { _fixedData.IsGrounded = historyData.IsGrounded; _fixedData.WasGrounded = historyData.WasGrounded; } } internal bool SphereOverlap(KCCOverlapInfo overlapInfo, Vector3 position, float radius, QueryTriggerInteraction triggerInteraction) { return SphereOverlap(overlapInfo, Data, position, radius, 0f, _settings.CollisionLayerMask, triggerInteraction); } internal bool CapsuleOverlap(KCCOverlapInfo overlapInfo, Vector3 position, float radius, float height, QueryTriggerInteraction triggerInteraction) { return CapsuleOverlap(overlapInfo, Data, position, radius, height, 0f, _settings.CollisionLayerMask, triggerInteraction); } internal bool RayCast(KCCShapeCastInfo shapeCastInfo, Vector3 position, Vector3 direction, float maxDistance, QueryTriggerInteraction triggerInteraction) { return RayCast(shapeCastInfo, Data, position, direction, maxDistance, _settings.CollisionLayerMask, triggerInteraction); } internal bool SphereCast(KCCShapeCastInfo shapeCastInfo, Vector3 position, float radius, Vector3 direction, float maxDistance, QueryTriggerInteraction triggerInteraction, bool trackInitialOverlaps = true) { return SphereCast(shapeCastInfo, Data, position, radius, 0f, direction, maxDistance, _settings.CollisionLayerMask, triggerInteraction, trackInitialOverlaps); } internal bool CapsuleCast(KCCShapeCastInfo shapeCastInfo, Vector3 position, float radius, float height, Vector3 direction, float maxDistance, QueryTriggerInteraction triggerInteraction, bool trackInitialOverlaps = true) { return CapsuleCast(shapeCastInfo, Data, position, radius, height, 0f, direction, maxDistance, _settings.CollisionLayerMask, triggerInteraction, trackInitialOverlaps); } internal void UpdateHits(KCCOverlapInfo baseOverlapInfo, EKCCHitsOverlapQuery overlapQuery) { UpdateHits(Data, baseOverlapInfo, overlapQuery); } internal bool IsValidHitCollider(Collider hitCollider) { if (hitCollider == null) { return false; } return IsValidHitCollider(Data, hitCollider); } private bool SphereOverlap(KCCOverlapInfo overlapInfo, KCCData data, Vector3 position, float radius, float extent, LayerMask layerMask, QueryTriggerInteraction triggerInteraction) { overlapInfo.Reset(deep: false); overlapInfo.Position = position; overlapInfo.Radius = radius; overlapInfo.Height = 0f; overlapInfo.Extent = extent; overlapInfo.LayerMask = layerMask; overlapInfo.TriggerInteraction = triggerInteraction; // Collider[] hitColliders = _hitColliders; Collider[] hitColliders = Physics.OverlapSphere(position, radius + extent, layerMask, triggerInteraction); for (int i = 0; i < hitColliders.Length; i++) { Collider overlapCollider = hitColliders[i]; if (IsValidHitColliderUnsafe(data, overlapCollider)) { overlapInfo.AddHit(overlapCollider); } } return overlapInfo.AllHitCount > 0; } private bool CapsuleOverlap(KCCOverlapInfo overlapInfo, KCCData data, Vector3 position, float radius, float height, float extent, LayerMask layerMask, QueryTriggerInteraction triggerInteraction) { overlapInfo.Reset(deep: false); overlapInfo.Position = position; overlapInfo.Radius = radius; overlapInfo.Height = height; overlapInfo.Extent = extent; overlapInfo.LayerMask = layerMask; overlapInfo.TriggerInteraction = triggerInteraction; Vector3 point = position + new Vector3(0f, height - radius, 0f); Vector3 point2 = position + new Vector3(0f, radius, 0f); // Collider[] hitColliders = _hitColliders; Collider[] hitColliders = Physics.OverlapCapsule(point2, point, radius + extent, layerMask, triggerInteraction); for (int i = 0; i < hitColliders.Length; i++) { Collider overlapCollider = hitColliders[i]; if (IsValidHitColliderUnsafe(data, overlapCollider)) { overlapInfo.AddHit(overlapCollider); } } return overlapInfo.AllHitCount > 0; } private bool RayCast(KCCShapeCastInfo shapeCastInfo, KCCData data, Vector3 position, Vector3 direction, float maxDistance, LayerMask layerMask, QueryTriggerInteraction triggerInteraction) { shapeCastInfo.Reset(deep: false); shapeCastInfo.Position = position; shapeCastInfo.Direction = direction; shapeCastInfo.MaxDistance = maxDistance; shapeCastInfo.LayerMask = layerMask; shapeCastInfo.TriggerInteraction = triggerInteraction; // RaycastHit[] raycastHits = _raycastHits; // if(Physics.Raycast(position, direction, out RaycastHit hit, maxDistance, layerMask, triggerInteraction)) // // int num = base.Runner.GetPhysicsScene().Raycast(position, direction, raycastHits, maxDistance, layerMask, triggerInteraction); // for (int i = 0; i < hit.; i++) // { // RaycastHit raycastHit = raycastHits[i]; // if (IsValidHitColliderUnsafe(data, raycastHit.collider)) // { // shapeCastInfo.AddHit(raycastHit); // } // } // return shapeCastInfo.AllHitCount > 0; return Physics.Raycast(position, direction, maxDistance, layerMask, triggerInteraction); } private bool SphereCast(KCCShapeCastInfo shapeCastInfo, KCCData data, Vector3 position, float radius, float extent, Vector3 direction, float maxDistance, LayerMask layerMask, QueryTriggerInteraction triggerInteraction, bool trackInitialOverlaps) { shapeCastInfo.Reset(deep: false); shapeCastInfo.Position = position; shapeCastInfo.Radius = radius; shapeCastInfo.Extent = extent; shapeCastInfo.Direction = direction; shapeCastInfo.MaxDistance = maxDistance; shapeCastInfo.LayerMask = layerMask; shapeCastInfo.TriggerInteraction = triggerInteraction; RaycastHit[] raycastHits = _raycastHits; return Physics.SphereCast(position, radius + extent, direction, out RaycastHit info, maxDistance, layerMask, triggerInteraction); // int num = base.Runner.GetPhysicsScene().SphereCast(position, radius + extent, direction, raycastHits, maxDistance, layerMask, triggerInteraction); // for (int i = 0; i < num; i++) // { // RaycastHit raycastHit = raycastHits[i]; // if ((trackInitialOverlaps || !(raycastHit.distance <= 0f) || !raycastHit.point.Equals(default(Vector3))) && IsValidHitColliderUnsafe(data, raycastHit.collider)) // { // shapeCastInfo.AddHit(raycastHit); // } // } // return shapeCastInfo.AllHitCount > 0; } private bool CapsuleCast(KCCShapeCastInfo shapeCastInfo, KCCData data, Vector3 position, float radius, float height, float extent, Vector3 direction, float maxDistance, LayerMask layerMask, QueryTriggerInteraction triggerInteraction, bool trackInitialOverlaps) { shapeCastInfo.Reset(deep: false); shapeCastInfo.Position = position; shapeCastInfo.Radius = radius; shapeCastInfo.Height = height; shapeCastInfo.Extent = extent; shapeCastInfo.Position = position; shapeCastInfo.Direction = direction; shapeCastInfo.MaxDistance = maxDistance; shapeCastInfo.LayerMask = layerMask; shapeCastInfo.TriggerInteraction = triggerInteraction; Vector3 point = position + new Vector3(0f, height - radius, 0f); Vector3 point2 = position + new Vector3(0f, radius, 0f); // RaycastHit[] raycastHits = _raycastHits; return Physics.CapsuleCast(point2, point, radius + extent, direction, out RaycastHit info, maxDistance, layerMask, triggerInteraction); // int num = base.Runner.GetPhysicsScene().CapsuleCast(point2, point, radius + extent, direction, raycastHits, maxDistance, layerMask, triggerInteraction); // for (int i = 0; i < num; i++) // { // RaycastHit raycastHit = raycastHits[i]; // if ((trackInitialOverlaps || !(raycastHit.distance <= 0f) || !raycastHit.point.Equals(default(Vector3))) && IsValidHitColliderUnsafe(data, raycastHit.collider)) // { // shapeCastInfo.AddHit(raycastHit); // } // } // return shapeCastInfo.AllHitCount > 0; } private void UpdateHits(KCCData data, KCCOverlapInfo baseOverlapInfo, EKCCHitsOverlapQuery overlapQuery) { if (overlapQuery switch { EKCCHitsOverlapQuery.Default => baseOverlapInfo?.AllHitsWithinExtent() ?? false, EKCCHitsOverlapQuery.Reuse => baseOverlapInfo != null, EKCCHitsOverlapQuery.New => false, _ => throw new NotImplementedException("overlapQuery"), }) { _trackOverlapInfo.CopyFromOther(baseOverlapInfo); } else { CapsuleOverlap(_trackOverlapInfo, data, data.TargetPosition, _settings.Radius, _settings.Height, _settings.Extent, _settings.CollisionLayerMask, QueryTriggerInteraction.Collide); if (baseOverlapInfo != null) { for (int i = 0; i < _trackOverlapInfo.AllHitCount; i++) { KCCOverlapHit kCCOverlapHit = _trackOverlapInfo.AllHits[i]; for (int j = 0; j < baseOverlapInfo.AllHitCount; j++) { KCCOverlapHit kCCOverlapHit2 = baseOverlapInfo.AllHits[j]; if ((object)kCCOverlapHit.Collider == kCCOverlapHit2.Collider) { kCCOverlapHit.CopyFromOther(kCCOverlapHit2); } } } } } data.Hits.Clear(); int k = 0; for (int allHitCount = _trackOverlapInfo.AllHitCount; k < allHitCount; k++) { data.Hits.Add(_trackOverlapInfo.AllHits[k]); } } private void ForceRemoveAllHits(KCCData data) { _trackOverlapInfo.Reset(deep: false); _extendedOverlapInfo.Reset(deep: false); data.Hits.Clear(); } private bool IsValidHitCollider(KCCData data, Collider hitCollider) { if (hitCollider == _collider.Collider) { return false; } int i = 0; for (int count = _childColliders.Count; i < count; i++) { if (hitCollider == _childColliders[i]) { return false; } } int num = 1 << hitCollider.gameObject.layer; if (((int)_settings.CollisionLayerMask & num) != num) { return false; } if (ResolveCollision != null) { try { return ResolveCollision(this, hitCollider); } catch (Exception exception) { Debug.LogException(exception); } } return true; } private bool IsValidHitColliderUnsafe(KCCData data, Collider overlapCollider) { if ((object)overlapCollider == _collider.Collider) { return false; } int i = 0; for (int count = _childColliders.Count; i < count; i++) { if ((object)overlapCollider == _childColliders[i]) { return false; } } if (ResolveCollision != null) { try { return ResolveCollision(this, overlapCollider); } catch (Exception exception) { Debug.LogException(exception); } } return true; } public void SetMaxGroundAngle(float maxGroundAngle) { if (IsSpawned) { Data.MaxGroundAngle = maxGroundAngle; } } public void SetGravity(float gravity) { if (IsSpawned) { Data.Gravity = Vector3.up * gravity; } } public void Move(Vector3 kinematicVelocity = default(Vector3), float jumpImpulse = 0f) { if (IsSpawned) { if (!IsInFixedUpdate) { throw new InvalidOperationException("[" + base.name + "] Move can be invoked only from within fixed update!"); } FixedData.KinematicVelocity = kinematicVelocity; FixedData.JumpImpulse += Vector3.up * jumpImpulse; ManualFixedUpdate(); } } public bool ProjectOnGround(Vector3 vector, out Vector3 projectedVector) { KCCData data = Data; if (data.IsGrounded) { return KCCPhysicsUtility.ProjectOnGround(data.GroundNormal, vector, out projectedVector); } projectedVector = default(Vector3); return false; } } }