mirror of
https://github.com/nothke/quality-control.git
synced 2025-09-01 08:03:43 +00:00
Added KCC
This commit is contained in:
@@ -0,0 +1,294 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace KinematicCharacterController
|
||||
{
|
||||
/// <summary>
|
||||
/// The system that manages the simulation of KinematicCharacterMotor and PhysicsMover
|
||||
/// </summary>
|
||||
[DefaultExecutionOrder(-100)]
|
||||
public class KinematicCharacterSystem : MonoBehaviour
|
||||
{
|
||||
private static KinematicCharacterSystem _instance;
|
||||
|
||||
public static List<KinematicCharacterMotor> CharacterMotors = new List<KinematicCharacterMotor>();
|
||||
public static List<PhysicsMover> PhysicsMovers = new List<PhysicsMover>();
|
||||
|
||||
private static float _lastCustomInterpolationStartTime = -1f;
|
||||
private static float _lastCustomInterpolationDeltaTime = -1f;
|
||||
|
||||
public static KCCSettings Settings;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a KinematicCharacterSystem instance if there isn't already one
|
||||
/// </summary>
|
||||
public static void EnsureCreation()
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
GameObject systemGameObject = new GameObject("KinematicCharacterSystem");
|
||||
_instance = systemGameObject.AddComponent<KinematicCharacterSystem>();
|
||||
|
||||
systemGameObject.hideFlags = HideFlags.NotEditable;
|
||||
_instance.hideFlags = HideFlags.NotEditable;
|
||||
|
||||
Settings = ScriptableObject.CreateInstance<KCCSettings>();
|
||||
|
||||
GameObject.DontDestroyOnLoad(systemGameObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the KinematicCharacterSystem instance if any
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static KinematicCharacterSystem GetInstance()
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the maximum capacity of the character motors list, to prevent allocations when adding characters
|
||||
/// </summary>
|
||||
/// <param name="capacity"></param>
|
||||
public static void SetCharacterMotorsCapacity(int capacity)
|
||||
{
|
||||
if (capacity < CharacterMotors.Count)
|
||||
{
|
||||
capacity = CharacterMotors.Count;
|
||||
}
|
||||
CharacterMotors.Capacity = capacity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a KinematicCharacterMotor into the system
|
||||
/// </summary>
|
||||
public static void RegisterCharacterMotor(KinematicCharacterMotor motor)
|
||||
{
|
||||
CharacterMotors.Add(motor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a KinematicCharacterMotor from the system
|
||||
/// </summary>
|
||||
public static void UnregisterCharacterMotor(KinematicCharacterMotor motor)
|
||||
{
|
||||
CharacterMotors.Remove(motor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the maximum capacity of the physics movers list, to prevent allocations when adding movers
|
||||
/// </summary>
|
||||
/// <param name="capacity"></param>
|
||||
public static void SetPhysicsMoversCapacity(int capacity)
|
||||
{
|
||||
if (capacity < PhysicsMovers.Count)
|
||||
{
|
||||
capacity = PhysicsMovers.Count;
|
||||
}
|
||||
PhysicsMovers.Capacity = capacity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a PhysicsMover into the system
|
||||
/// </summary>
|
||||
public static void RegisterPhysicsMover(PhysicsMover mover)
|
||||
{
|
||||
PhysicsMovers.Add(mover);
|
||||
|
||||
mover.Rigidbody.interpolation = RigidbodyInterpolation.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a PhysicsMover from the system
|
||||
/// </summary>
|
||||
public static void UnregisterPhysicsMover(PhysicsMover mover)
|
||||
{
|
||||
PhysicsMovers.Remove(mover);
|
||||
}
|
||||
|
||||
// This is to prevent duplicating the singleton gameobject on script recompiles
|
||||
private void OnDisable()
|
||||
{
|
||||
Destroy(this.gameObject);
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_instance = this;
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
if (Settings.AutoSimulation)
|
||||
{
|
||||
float deltaTime = Time.deltaTime;
|
||||
|
||||
if (Settings.Interpolate)
|
||||
{
|
||||
PreSimulationInterpolationUpdate(deltaTime);
|
||||
}
|
||||
|
||||
Simulate(deltaTime, CharacterMotors, PhysicsMovers);
|
||||
|
||||
if (Settings.Interpolate)
|
||||
{
|
||||
PostSimulationInterpolationUpdate(deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (Settings.Interpolate)
|
||||
{
|
||||
CustomInterpolationUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remembers the point to interpolate from for KinematicCharacterMotors and PhysicsMovers
|
||||
/// </summary>
|
||||
public static void PreSimulationInterpolationUpdate(float deltaTime)
|
||||
{
|
||||
// Save pre-simulation poses and place transform at transient pose
|
||||
for (int i = 0; i < CharacterMotors.Count; i++)
|
||||
{
|
||||
KinematicCharacterMotor motor = CharacterMotors[i];
|
||||
|
||||
motor.InitialTickPosition = motor.TransientPosition;
|
||||
motor.InitialTickRotation = motor.TransientRotation;
|
||||
|
||||
motor.Transform.SetPositionAndRotation(motor.TransientPosition, motor.TransientRotation);
|
||||
}
|
||||
|
||||
for (int i = 0; i < PhysicsMovers.Count; i++)
|
||||
{
|
||||
PhysicsMover mover = PhysicsMovers[i];
|
||||
|
||||
mover.InitialTickPosition = mover.TransientPosition;
|
||||
mover.InitialTickRotation = mover.TransientRotation;
|
||||
|
||||
mover.Transform.SetPositionAndRotation(mover.TransientPosition, mover.TransientRotation);
|
||||
mover.Rigidbody.position = mover.TransientPosition;
|
||||
mover.Rigidbody.rotation = mover.TransientRotation;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ticks characters and/or movers
|
||||
/// </summary>
|
||||
public static void Simulate(float deltaTime, List<KinematicCharacterMotor> motors, List<PhysicsMover> movers)
|
||||
{
|
||||
int characterMotorsCount = motors.Count;
|
||||
int physicsMoversCount = movers.Count;
|
||||
|
||||
#pragma warning disable 0162
|
||||
// Update PhysicsMover velocities
|
||||
for (int i = 0; i < physicsMoversCount; i++)
|
||||
{
|
||||
movers[i].VelocityUpdate(deltaTime);
|
||||
}
|
||||
|
||||
// Character controller update phase 1
|
||||
for (int i = 0; i < characterMotorsCount; i++)
|
||||
{
|
||||
motors[i].UpdatePhase1(deltaTime);
|
||||
}
|
||||
|
||||
// Simulate PhysicsMover displacement
|
||||
for (int i = 0; i < physicsMoversCount; i++)
|
||||
{
|
||||
PhysicsMover mover = movers[i];
|
||||
|
||||
mover.Transform.SetPositionAndRotation(mover.TransientPosition, mover.TransientRotation);
|
||||
mover.Rigidbody.position = mover.TransientPosition;
|
||||
mover.Rigidbody.rotation = mover.TransientRotation;
|
||||
}
|
||||
|
||||
// Character controller update phase 2 and move
|
||||
for (int i = 0; i < characterMotorsCount; i++)
|
||||
{
|
||||
KinematicCharacterMotor motor = motors[i];
|
||||
|
||||
motor.UpdatePhase2(deltaTime);
|
||||
|
||||
motor.Transform.SetPositionAndRotation(motor.TransientPosition, motor.TransientRotation);
|
||||
}
|
||||
#pragma warning restore 0162
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initiates the interpolation for KinematicCharacterMotors and PhysicsMovers
|
||||
/// </summary>
|
||||
public static void PostSimulationInterpolationUpdate(float deltaTime)
|
||||
{
|
||||
_lastCustomInterpolationStartTime = Time.time;
|
||||
_lastCustomInterpolationDeltaTime = deltaTime;
|
||||
|
||||
// Return interpolated roots to their initial poses
|
||||
for (int i = 0; i < CharacterMotors.Count; i++)
|
||||
{
|
||||
KinematicCharacterMotor motor = CharacterMotors[i];
|
||||
|
||||
motor.Transform.SetPositionAndRotation(motor.InitialTickPosition, motor.InitialTickRotation);
|
||||
}
|
||||
|
||||
for (int i = 0; i < PhysicsMovers.Count; i++)
|
||||
{
|
||||
PhysicsMover mover = PhysicsMovers[i];
|
||||
|
||||
if (mover.MoveWithPhysics)
|
||||
{
|
||||
mover.Rigidbody.position = mover.InitialTickPosition;
|
||||
mover.Rigidbody.rotation = mover.InitialTickRotation;
|
||||
|
||||
mover.Rigidbody.MovePosition(mover.TransientPosition);
|
||||
mover.Rigidbody.MoveRotation(mover.TransientRotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
mover.Rigidbody.position = (mover.TransientPosition);
|
||||
mover.Rigidbody.rotation = (mover.TransientRotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles per-frame interpolation
|
||||
/// </summary>
|
||||
private static void CustomInterpolationUpdate()
|
||||
{
|
||||
float interpolationFactor = Mathf.Clamp01((Time.time - _lastCustomInterpolationStartTime) / _lastCustomInterpolationDeltaTime);
|
||||
|
||||
// Handle characters interpolation
|
||||
for (int i = 0; i < CharacterMotors.Count; i++)
|
||||
{
|
||||
KinematicCharacterMotor motor = CharacterMotors[i];
|
||||
|
||||
motor.Transform.SetPositionAndRotation(
|
||||
Vector3.Lerp(motor.InitialTickPosition, motor.TransientPosition, interpolationFactor),
|
||||
Quaternion.Slerp(motor.InitialTickRotation, motor.TransientRotation, interpolationFactor));
|
||||
}
|
||||
|
||||
// Handle PhysicsMovers interpolation
|
||||
for (int i = 0; i < PhysicsMovers.Count; i++)
|
||||
{
|
||||
PhysicsMover mover = PhysicsMovers[i];
|
||||
|
||||
mover.Transform.SetPositionAndRotation(
|
||||
Vector3.Lerp(mover.InitialTickPosition, mover.TransientPosition, interpolationFactor),
|
||||
Quaternion.Slerp(mover.InitialTickRotation, mover.TransientRotation, interpolationFactor));
|
||||
|
||||
Vector3 newPos = mover.Transform.position;
|
||||
Quaternion newRot = mover.Transform.rotation;
|
||||
mover.PositionDeltaFromInterpolation = newPos - mover.LatestInterpolationPosition;
|
||||
mover.RotationDeltaFromInterpolation = Quaternion.Inverse(mover.LatestInterpolationRotation) * newRot;
|
||||
mover.LatestInterpolationPosition = newPos;
|
||||
mover.LatestInterpolationRotation = newRot;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user