quality-control/Assets/Plugins/KinematicCharacterController/Core/PhysicsMover.cs

261 lines
8.4 KiB
C#
Raw Permalink Normal View History

2024-08-16 22:50:47 +00:00
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace KinematicCharacterController
{
/// <summary>
/// Represents the entire state of a PhysicsMover that is pertinent for simulation.
/// Use this to save state or revert to past state
/// </summary>
[System.Serializable]
public struct PhysicsMoverState
{
public Vector3 Position;
public Quaternion Rotation;
public Vector3 Velocity;
public Vector3 AngularVelocity;
}
/// <summary>
/// Component that manages the movement of moving kinematic rigidbodies for
/// proper interaction with characters
/// </summary>
[RequireComponent(typeof(Rigidbody))]
public class PhysicsMover : MonoBehaviour
{
/// <summary>
/// The mover's Rigidbody
/// </summary>
[ReadOnly]
public Rigidbody Rigidbody;
/// <summary>
/// Determines if the platform moves with rigidbody.MovePosition (when true), or with rigidbody.position (when false)
/// </summary>
public bool MoveWithPhysics = true;
/// <summary>
/// Index of this motor in KinematicCharacterSystem arrays
/// </summary>
[NonSerialized]
public IMoverController MoverController;
/// <summary>
/// Remembers latest position in interpolation
/// </summary>
[NonSerialized]
public Vector3 LatestInterpolationPosition;
/// <summary>
/// Remembers latest rotation in interpolation
/// </summary>
[NonSerialized]
public Quaternion LatestInterpolationRotation;
/// <summary>
/// The latest movement made by interpolation
/// </summary>
[NonSerialized]
public Vector3 PositionDeltaFromInterpolation;
/// <summary>
/// The latest rotation made by interpolation
/// </summary>
[NonSerialized]
public Quaternion RotationDeltaFromInterpolation;
/// <summary>
/// Index of this motor in KinematicCharacterSystem arrays
/// </summary>
public int IndexInCharacterSystem { get; set; }
/// <summary>
/// Remembers initial position before all simulation are done
/// </summary>
public Vector3 Velocity { get; protected set; }
/// <summary>
/// Remembers initial position before all simulation are done
/// </summary>
public Vector3 AngularVelocity { get; protected set; }
/// <summary>
/// Remembers initial position before all simulation are done
/// </summary>
public Vector3 InitialTickPosition { get; set; }
/// <summary>
/// Remembers initial rotation before all simulation are done
/// </summary>
public Quaternion InitialTickRotation { get; set; }
/// <summary>
/// The mover's Transform
/// </summary>
public Transform Transform { get; private set; }
/// <summary>
/// The character's position before the movement calculations began
/// </summary>
public Vector3 InitialSimulationPosition { get; private set; }
/// <summary>
/// The character's rotation before the movement calculations began
/// </summary>
public Quaternion InitialSimulationRotation { get; private set; }
private Vector3 _internalTransientPosition;
/// <summary>
/// The mover's rotation (always up-to-date during the character update phase)
/// </summary>
public Vector3 TransientPosition
{
get
{
return _internalTransientPosition;
}
private set
{
_internalTransientPosition = value;
}
}
private Quaternion _internalTransientRotation;
/// <summary>
/// The mover's rotation (always up-to-date during the character update phase)
/// </summary>
public Quaternion TransientRotation
{
get
{
return _internalTransientRotation;
}
private set
{
_internalTransientRotation = value;
}
}
private void Reset()
{
ValidateData();
}
private void OnValidate()
{
ValidateData();
}
/// <summary>
/// Handle validating all required values
/// </summary>
public void ValidateData()
{
Rigidbody = gameObject.GetComponent<Rigidbody>();
Rigidbody.centerOfMass = Vector3.zero;
Rigidbody.maxAngularVelocity = Mathf.Infinity;
Rigidbody.maxDepenetrationVelocity = Mathf.Infinity;
Rigidbody.isKinematic = true;
Rigidbody.interpolation = RigidbodyInterpolation.None;
}
private void OnEnable()
{
KinematicCharacterSystem.EnsureCreation();
KinematicCharacterSystem.RegisterPhysicsMover(this);
}
private void OnDisable()
{
KinematicCharacterSystem.UnregisterPhysicsMover(this);
}
private void Awake()
{
Transform = this.transform;
ValidateData();
TransientPosition = Rigidbody.position;
TransientRotation = Rigidbody.rotation;
InitialSimulationPosition = Rigidbody.position;
InitialSimulationRotation = Rigidbody.rotation;
LatestInterpolationPosition = Transform.position;
LatestInterpolationRotation = Transform.rotation;
}
/// <summary>
/// Sets the mover's position directly
/// </summary>
public void SetPosition(Vector3 position)
{
Transform.position = position;
Rigidbody.position = position;
InitialSimulationPosition = position;
TransientPosition = position;
}
/// <summary>
/// Sets the mover's rotation directly
/// </summary>
public void SetRotation(Quaternion rotation)
{
Transform.rotation = rotation;
Rigidbody.rotation = rotation;
InitialSimulationRotation = rotation;
TransientRotation = rotation;
}
/// <summary>
/// Sets the mover's position and rotation directly
/// </summary>
public void SetPositionAndRotation(Vector3 position, Quaternion rotation)
{
Transform.SetPositionAndRotation(position, rotation);
Rigidbody.position = position;
Rigidbody.rotation = rotation;
InitialSimulationPosition = position;
InitialSimulationRotation = rotation;
TransientPosition = position;
TransientRotation = rotation;
}
/// <summary>
/// Returns all the state information of the mover that is pertinent for simulation
/// </summary>
public PhysicsMoverState GetState()
{
PhysicsMoverState state = new PhysicsMoverState();
state.Position = TransientPosition;
state.Rotation = TransientRotation;
state.Velocity = Velocity;
state.AngularVelocity = AngularVelocity;
return state;
}
/// <summary>
/// Applies a mover state instantly
/// </summary>
public void ApplyState(PhysicsMoverState state)
{
SetPositionAndRotation(state.Position, state.Rotation);
Velocity = state.Velocity;
AngularVelocity = state.AngularVelocity;
}
/// <summary>
/// Caches velocity values based on deltatime and target position/rotations
/// </summary>
public void VelocityUpdate(float deltaTime)
{
InitialSimulationPosition = TransientPosition;
InitialSimulationRotation = TransientRotation;
MoverController.UpdateMovement(out _internalTransientPosition, out _internalTransientRotation, deltaTime);
if (deltaTime > 0f)
{
Velocity = (TransientPosition - InitialSimulationPosition) / deltaTime;
Quaternion rotationFromCurrentToGoal = TransientRotation * (Quaternion.Inverse(InitialSimulationRotation));
AngularVelocity = (Mathf.Deg2Rad * rotationFromCurrentToGoal.eulerAngles) / deltaTime;
}
}
}
}