|
|
|
|
using UnityEngine;
|
|
|
|
|
using System.Collections;
|
|
|
|
|
|
|
|
|
|
namespace RootMotion.FinalIK {
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Grounding for LimbIK, CCD and/or FABRIK solvers.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[HelpURL("http://www.root-motion.com/finalikdox/html/page9.html")]
|
|
|
|
|
[AddComponentMenu("Scripts/RootMotion.FinalIK/Grounder/Grounder IK")]
|
|
|
|
|
public class GrounderIK: Grounder {
|
|
|
|
|
|
|
|
|
|
// Open the User Manual URL
|
|
|
|
|
[ContextMenu("User Manual")]
|
|
|
|
|
protected override void OpenUserManual() {
|
|
|
|
|
Application.OpenURL("http://www.root-motion.com/finalikdox/html/page9.html");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Open the Script Reference URL
|
|
|
|
|
[ContextMenu("Scrpt Reference")]
|
|
|
|
|
protected override void OpenScriptReference() {
|
|
|
|
|
Application.OpenURL("http://www.root-motion.com/finalikdox/html/class_root_motion_1_1_final_i_k_1_1_grounder_i_k.html");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region Main Interface
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The leg %IK componets (can be any type of IK component).
|
|
|
|
|
/// </summary>
|
|
|
|
|
public IK[] legs;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The pelvis transform. Common ancestor of all the legs.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[Tooltip("The pelvis transform. Common ancestor of all the legs.")]
|
|
|
|
|
public Transform pelvis;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The root Transform of the character, with the rigidbody and the collider.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[Tooltip("The root Transform of the character, with the rigidbody and the collider.")]
|
|
|
|
|
public Transform characterRoot;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The weight of rotating the character root to the ground normal (range: 0 - 1).
|
|
|
|
|
/// </summary>
|
|
|
|
|
[Tooltip("The weight of rotating the character root to the ground normal (range: 0 - 1).")]
|
|
|
|
|
[Range(0f, 1f)]
|
|
|
|
|
public float rootRotationWeight;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The speed of rotating the character root to the ground normal (range: 0 - inf).
|
|
|
|
|
/// </summary>
|
|
|
|
|
[Tooltip("The speed of rotating the character root to the ground normal (range: 0 - inf).")]
|
|
|
|
|
public float rootRotationSpeed = 5f;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The maximum angle of root rotation (range: 0 - 90).
|
|
|
|
|
/// </summary>
|
|
|
|
|
[Tooltip("The maximum angle of root rotation (range: 0 - 90).")]
|
|
|
|
|
public float maxRootRotationAngle = 45f;
|
|
|
|
|
|
|
|
|
|
#endregion Main Interface
|
|
|
|
|
|
|
|
|
|
public override void ResetPosition() {
|
|
|
|
|
for (int i = 0; i < legs.Length; i++) {
|
|
|
|
|
legs[i].GetIKSolver().IKPosition = feet[i].transform.position;
|
|
|
|
|
if (legs[i] is LimbIK)
|
|
|
|
|
{
|
|
|
|
|
var leg = legs[i] as LimbIK;
|
|
|
|
|
leg.solver.IKRotation = solver.legs[i].transform.rotation;
|
|
|
|
|
}
|
|
|
|
|
footRotations[i] = feet[i].rotation;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
animatedPelvisLocalPosition = pelvis.localPosition;
|
|
|
|
|
solvedPelvisLocalPosition = pelvis.localPosition;
|
|
|
|
|
|
|
|
|
|
solver.Reset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Transform[] feet = new Transform[0];
|
|
|
|
|
private Quaternion[] footRotations = new Quaternion[0];
|
|
|
|
|
private Vector3 animatedPelvisLocalPosition, solvedPelvisLocalPosition;
|
|
|
|
|
private int solvedFeet;
|
|
|
|
|
private bool solved;
|
|
|
|
|
private float lastWeight;
|
|
|
|
|
private Rigidbody characterRootRigidbody;
|
|
|
|
|
|
|
|
|
|
// Can we initiate the Grounding?
|
|
|
|
|
private bool IsReadyToInitiate() {
|
|
|
|
|
if (pelvis == null) return false;
|
|
|
|
|
|
|
|
|
|
if (legs.Length == 0) return false;
|
|
|
|
|
|
|
|
|
|
foreach (IK leg in legs) {
|
|
|
|
|
if (leg == null) return false;
|
|
|
|
|
|
|
|
|
|
if (leg is FullBodyBipedIK) {
|
|
|
|
|
LogWarning("GrounderIK does not support FullBodyBipedIK, use CCDIK, FABRIK, LimbIK or TrigonometricIK instead. If you want to use FullBodyBipedIK, use the GrounderFBBIK component.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (leg is FABRIKRoot) {
|
|
|
|
|
LogWarning("GrounderIK does not support FABRIKRoot, use CCDIK, FABRIK, LimbIK or TrigonometricIK instead.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (leg is AimIK) {
|
|
|
|
|
LogWarning("GrounderIK does not support AimIK, use CCDIK, FABRIK, LimbIK or TrigonometricIK instead.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Weigh out the IK solvers properly when the component is disabled
|
|
|
|
|
void OnDisable() {
|
|
|
|
|
if (!initiated) return;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < legs.Length; i++) {
|
|
|
|
|
if (legs[i] != null) legs[i].GetIKSolver().IKPositionWeight = 0f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initiate once we have all the required components
|
|
|
|
|
void Update() {
|
|
|
|
|
weight = Mathf.Clamp(weight, 0f, 1f);
|
|
|
|
|
if (weight <= 0f) return;
|
|
|
|
|
|
|
|
|
|
solved = false;
|
|
|
|
|
|
|
|
|
|
if (initiated) {
|
|
|
|
|
// Clamping values
|
|
|
|
|
rootRotationWeight = Mathf.Clamp(rootRotationWeight, 0f, 1f);
|
|
|
|
|
rootRotationSpeed = Mathf.Clamp(rootRotationSpeed, 0f, rootRotationSpeed);
|
|
|
|
|
|
|
|
|
|
// Root rotation
|
|
|
|
|
if (characterRoot != null && rootRotationSpeed > 0f && rootRotationWeight > 0f && solver.isGrounded) {
|
|
|
|
|
Vector3 normal = solver.GetLegsPlaneNormal();
|
|
|
|
|
|
|
|
|
|
// Root rotation weight
|
|
|
|
|
if (rootRotationWeight < 1f) {
|
|
|
|
|
normal = Vector3.Slerp(Vector3.up, normal, rootRotationWeight);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Root rotation limit
|
|
|
|
|
Quaternion upRotation = Quaternion.FromToRotation(transform.up, Vector3.up) * characterRoot.rotation;
|
|
|
|
|
Quaternion rotationTarget = Quaternion.RotateTowards(upRotation, Quaternion.FromToRotation(transform.up, normal) * characterRoot.rotation, maxRootRotationAngle);
|
|
|
|
|
|
|
|
|
|
// Rotate the root
|
|
|
|
|
if (characterRootRigidbody == null) {
|
|
|
|
|
characterRoot.rotation = Quaternion.Lerp(characterRoot.rotation, rotationTarget, Time.deltaTime * rootRotationSpeed);
|
|
|
|
|
} else {
|
|
|
|
|
characterRootRigidbody.MoveRotation(Quaternion.Lerp(characterRoot.rotation, rotationTarget, Time.deltaTime * rootRotationSpeed));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!IsReadyToInitiate()) return;
|
|
|
|
|
|
|
|
|
|
Initiate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Initiate() {
|
|
|
|
|
// Building arrays
|
|
|
|
|
feet = new Transform[legs.Length];
|
|
|
|
|
footRotations = new Quaternion[legs.Length];
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < feet.Length; i++) footRotations[i] = Quaternion.identity;
|
|
|
|
|
|
|
|
|
|
// Gathering the last bones of the IK solvers as feet
|
|
|
|
|
for (int i = 0; i < legs.Length; i++) {
|
|
|
|
|
IKSolver.Point[] points = legs[i].GetIKSolver().GetPoints();
|
|
|
|
|
|
|
|
|
|
feet[i] = points[points.Length - 1].transform;
|
|
|
|
|
|
|
|
|
|
// Add to the update delegates of each ik solver
|
|
|
|
|
legs[i].GetIKSolver().OnPreUpdate += OnSolverUpdate;
|
|
|
|
|
legs[i].GetIKSolver().OnPostUpdate += OnPostSolverUpdate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Store the default localPosition of the pelvis
|
|
|
|
|
animatedPelvisLocalPosition = pelvis.localPosition;
|
|
|
|
|
|
|
|
|
|
// Initiate the Grounding
|
|
|
|
|
solver.Initiate(transform, feet);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < legs.Length; i++) {
|
|
|
|
|
if (legs [i] is LegIK) {
|
|
|
|
|
solver.legs[i].invertFootCenter = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
characterRootRigidbody = characterRoot.GetComponent<Rigidbody>();
|
|
|
|
|
|
|
|
|
|
initiated = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Called before updating the first IK solver
|
|
|
|
|
private void OnSolverUpdate() {
|
|
|
|
|
if (!enabled) return;
|
|
|
|
|
|
|
|
|
|
if (weight <= 0f) {
|
|
|
|
|
if (lastWeight <= 0f) return;
|
|
|
|
|
|
|
|
|
|
// Weigh out the limb solvers properly
|
|
|
|
|
OnDisable();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lastWeight = weight;
|
|
|
|
|
|
|
|
|
|
// If another IK has already solved in this frame, do nothing
|
|
|
|
|
if (solved) return;
|
|
|
|
|
|
|
|
|
|
if (OnPreGrounder != null) OnPreGrounder();
|
|
|
|
|
|
|
|
|
|
// If the pelvis local position has not changed since last solved state, consider it unanimated
|
|
|
|
|
if (pelvis.localPosition != solvedPelvisLocalPosition) animatedPelvisLocalPosition = pelvis.localPosition;
|
|
|
|
|
else pelvis.localPosition = animatedPelvisLocalPosition;
|
|
|
|
|
|
|
|
|
|
// Update the Grounding
|
|
|
|
|
solver.Update();
|
|
|
|
|
|
|
|
|
|
// Update the IKPositions and IKPositonWeights of the legs
|
|
|
|
|
for (int i = 0; i < legs.Length; i++) SetLegIK(i);
|
|
|
|
|
|
|
|
|
|
// Move the pelvis
|
|
|
|
|
pelvis.position += solver.pelvis.IKOffset * weight;
|
|
|
|
|
|
|
|
|
|
solved = true;
|
|
|
|
|
solvedFeet = 0;
|
|
|
|
|
|
|
|
|
|
if (OnPostGrounder != null) OnPostGrounder();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set the IK position and weight for a limb
|
|
|
|
|
private void SetLegIK(int index) {
|
|
|
|
|
footRotations[index] = feet[index].rotation;
|
|
|
|
|
|
|
|
|
|
if (legs [index] is LegIK) {
|
|
|
|
|
(legs[index].GetIKSolver() as IKSolverLeg).IKRotation = Quaternion.Slerp(Quaternion.identity, solver.legs[index].rotationOffset, weight) * footRotations[index];
|
|
|
|
|
(legs[index].GetIKSolver() as IKSolverLeg).IKRotationWeight = 1f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
legs[index].GetIKSolver().IKPosition = solver.legs[index].IKPosition;
|
|
|
|
|
legs[index].GetIKSolver().IKPositionWeight = weight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Rotating the feet after IK has finished
|
|
|
|
|
private void OnPostSolverUpdate() {
|
|
|
|
|
if (weight <= 0f) return;
|
|
|
|
|
if (!enabled) return;
|
|
|
|
|
|
|
|
|
|
// Only do this after the last IK solver has finished
|
|
|
|
|
solvedFeet ++;
|
|
|
|
|
if (solvedFeet < feet.Length) return;
|
|
|
|
|
solved = false;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < feet.Length; i++) {
|
|
|
|
|
feet[i].rotation = Quaternion.Slerp(Quaternion.identity, solver.legs[i].rotationOffset, weight) * footRotations[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Store the local position of the pelvis so we know it it changes
|
|
|
|
|
solvedPelvisLocalPosition = pelvis.localPosition;
|
|
|
|
|
|
|
|
|
|
if (OnPostIK != null) OnPostIK();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cleaning up the delegates
|
|
|
|
|
void OnDestroy() {
|
|
|
|
|
if (initiated) {
|
|
|
|
|
foreach (IK leg in legs) {
|
|
|
|
|
if (leg != null) {
|
|
|
|
|
leg.GetIKSolver().OnPreUpdate -= OnSolverUpdate;
|
|
|
|
|
leg.GetIKSolver().OnPostUpdate -= OnPostSolverUpdate;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|