Files
2026-03-04 09:37:33 +08:00

287 lines
7.5 KiB
C#

using System;
using UnityEngine;
namespace RootMotion.FinalIK
{
[HelpURL("http://www.root-motion.com/finalikdox/html/page9.html")]
[AddComponentMenu("Scripts/RootMotion.FinalIK/Grounder/Grounder IK")]
public class GrounderIK : Grounder
{
public IK[] legs;
[Tooltip("The pelvis transform. Common ancestor of all the legs.")]
public Transform pelvis;
[Tooltip("The root Transform of the character, with the rigidbody and the collider.")]
public Transform characterRoot;
[Tooltip("The weight of rotating the character root to the ground normal (range: 0 - 1).")]
[Range(0f, 1f)]
public float rootRotationWeight;
[Tooltip("The speed of rotating the character root to the ground normal (range: 0 - inf).")]
public float rootRotationSpeed = 5f;
[Tooltip("The maximum angle of root rotation (range: 0 - 90).")]
public float maxRootRotationAngle = 45f;
private Transform[] feet = new Transform[0];
private Quaternion[] footRotations = new Quaternion[0];
private Vector3 animatedPelvisLocalPosition;
private Vector3 solvedPelvisLocalPosition;
private int solvedFeet;
private bool solved;
private float lastWeight;
private Rigidbody characterRootRigidbody;
[ContextMenu("User Manual")]
protected override void OpenUserManual()
{
Application.OpenURL("http://www.root-motion.com/finalikdox/html/page9.html");
}
[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");
}
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)
{
(legs[i] as LimbIK).solver.IKRotation = solver.legs[i].transform.rotation;
}
footRotations[i] = feet[i].rotation;
}
animatedPelvisLocalPosition = pelvis.localPosition;
solvedPelvisLocalPosition = pelvis.localPosition;
solver.Reset();
}
private bool IsReadyToInitiate()
{
if (pelvis == null)
{
return false;
}
if (legs.Length == 0)
{
return false;
}
IK[] array = legs;
foreach (IK iK in array)
{
if (iK == null)
{
return false;
}
if (iK 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 (iK is FABRIKRoot)
{
LogWarning("GrounderIK does not support FABRIKRoot, use CCDIK, FABRIK, LimbIK or TrigonometricIK instead.");
return false;
}
if (iK is AimIK)
{
LogWarning("GrounderIK does not support AimIK, use CCDIK, FABRIK, LimbIK or TrigonometricIK instead.");
return false;
}
}
return true;
}
private void OnDisable()
{
if (!base.initiated)
{
return;
}
for (int i = 0; i < legs.Length; i++)
{
if (legs[i] != null)
{
legs[i].GetIKSolver().IKPositionWeight = 0f;
}
}
}
private void Update()
{
weight = Mathf.Clamp(weight, 0f, 1f);
if (weight <= 0f)
{
return;
}
solved = false;
if (base.initiated)
{
rootRotationWeight = Mathf.Clamp(rootRotationWeight, 0f, 1f);
rootRotationSpeed = Mathf.Clamp(rootRotationSpeed, 0f, rootRotationSpeed);
if (characterRoot != null && rootRotationSpeed > 0f && rootRotationWeight > 0f && solver.isGrounded)
{
Vector3 vector = solver.GetLegsPlaneNormal();
if (rootRotationWeight < 1f)
{
vector = Vector3.Slerp(Vector3.up, vector, rootRotationWeight);
}
Quaternion b = Quaternion.RotateTowards(Quaternion.FromToRotation(base.transform.up, Vector3.up) * characterRoot.rotation, Quaternion.FromToRotation(base.transform.up, vector) * characterRoot.rotation, maxRootRotationAngle);
if (characterRootRigidbody == null)
{
characterRoot.rotation = Quaternion.Lerp(characterRoot.rotation, b, Time.deltaTime * rootRotationSpeed);
}
else
{
characterRootRigidbody.MoveRotation(Quaternion.Lerp(characterRoot.rotation, b, Time.deltaTime * rootRotationSpeed));
}
}
}
else if (IsReadyToInitiate())
{
Initiate();
}
}
private void Initiate()
{
feet = new Transform[legs.Length];
footRotations = new Quaternion[legs.Length];
for (int i = 0; i < feet.Length; i++)
{
footRotations[i] = Quaternion.identity;
}
for (int j = 0; j < legs.Length; j++)
{
IKSolver.Point[] points = legs[j].GetIKSolver().GetPoints();
feet[j] = points[^1].transform;
IKSolver iKSolver = legs[j].GetIKSolver();
iKSolver.OnPreUpdate = (IKSolver.UpdateDelegate)Delegate.Combine(iKSolver.OnPreUpdate, new IKSolver.UpdateDelegate(OnSolverUpdate));
IKSolver iKSolver2 = legs[j].GetIKSolver();
iKSolver2.OnPostUpdate = (IKSolver.UpdateDelegate)Delegate.Combine(iKSolver2.OnPostUpdate, new IKSolver.UpdateDelegate(OnPostSolverUpdate));
}
animatedPelvisLocalPosition = pelvis.localPosition;
solver.Initiate(base.transform, feet);
for (int k = 0; k < legs.Length; k++)
{
if (legs[k] is LegIK)
{
solver.legs[k].invertFootCenter = true;
}
}
characterRootRigidbody = characterRoot.GetComponent<Rigidbody>();
base.initiated = true;
}
private void OnSolverUpdate()
{
if (!base.enabled)
{
return;
}
if (weight <= 0f)
{
if (lastWeight <= 0f)
{
return;
}
OnDisable();
}
lastWeight = weight;
if (!solved)
{
if (OnPreGrounder != null)
{
OnPreGrounder();
}
if (pelvis.localPosition != solvedPelvisLocalPosition)
{
animatedPelvisLocalPosition = pelvis.localPosition;
}
else
{
pelvis.localPosition = animatedPelvisLocalPosition;
}
solver.Update();
for (int i = 0; i < legs.Length; i++)
{
SetLegIK(i);
}
pelvis.position += solver.pelvis.IKOffset * weight;
solved = true;
solvedFeet = 0;
if (OnPostGrounder != null)
{
OnPostGrounder();
}
}
}
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;
}
private void OnPostSolverUpdate()
{
if (weight <= 0f || !base.enabled)
{
return;
}
solvedFeet++;
if (solvedFeet >= feet.Length)
{
solved = false;
for (int i = 0; i < feet.Length; i++)
{
feet[i].rotation = Quaternion.Slerp(Quaternion.identity, solver.legs[i].rotationOffset, weight) * footRotations[i];
}
solvedPelvisLocalPosition = pelvis.localPosition;
if (OnPostIK != null)
{
OnPostIK();
}
}
}
private void OnDestroy()
{
if (!base.initiated)
{
return;
}
IK[] array = legs;
foreach (IK iK in array)
{
if (iK != null)
{
IKSolver iKSolver = iK.GetIKSolver();
iKSolver.OnPreUpdate = (IKSolver.UpdateDelegate)Delegate.Remove(iKSolver.OnPreUpdate, new IKSolver.UpdateDelegate(OnSolverUpdate));
IKSolver iKSolver2 = iK.GetIKSolver();
iKSolver2.OnPostUpdate = (IKSolver.UpdateDelegate)Delegate.Remove(iKSolver2.OnPostUpdate, new IKSolver.UpdateDelegate(OnPostSolverUpdate));
}
}
}
}
}