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

463 lines
9.5 KiB
C#

using System;
using System.Collections;
using UnityEngine;
namespace RootMotion.FinalIK
{
public class RagdollUtility : MonoBehaviour
{
public class Rigidbone
{
public Rigidbody r;
public Transform t;
public Collider collider;
public Joint joint;
public Rigidbody c;
public bool updateAnchor;
public Vector3 deltaPosition;
public Quaternion deltaRotation;
public float deltaTime;
public Vector3 lastPosition;
public Quaternion lastRotation;
public Rigidbone(Rigidbody r)
{
this.r = r;
t = r.transform;
joint = t.GetComponent<Joint>();
collider = t.GetComponent<Collider>();
if (joint != null)
{
c = joint.connectedBody;
updateAnchor = c != null;
}
lastPosition = t.position;
lastRotation = t.rotation;
}
public void RecordVelocity()
{
deltaPosition = t.position - lastPosition;
lastPosition = t.position;
deltaRotation = QuaTools.FromToRotation(lastRotation, t.rotation);
lastRotation = t.rotation;
deltaTime = Time.deltaTime;
}
public void WakeUp(float velocityWeight, float angularVelocityWeight)
{
if (updateAnchor)
{
joint.connectedAnchor = t.InverseTransformPoint(c.position);
}
r.isKinematic = false;
if (velocityWeight != 0f)
{
r.linearVelocity = deltaPosition / deltaTime * velocityWeight;
}
if (angularVelocityWeight != 0f)
{
float angle = 0f;
Vector3 axis = Vector3.zero;
deltaRotation.ToAngleAxis(out angle, out axis);
angle *= MathF.PI / 180f;
angle /= deltaTime;
axis *= angle * angularVelocityWeight;
r.angularVelocity = Vector3.ClampMagnitude(axis, r.maxAngularVelocity);
}
r.WakeUp();
}
}
public class Child
{
public Transform t;
public Vector3 localPosition;
public Quaternion localRotation;
public Child(Transform transform)
{
t = transform;
localPosition = t.localPosition;
localRotation = t.localRotation;
}
public void FixTransform(float weight)
{
if (!(weight <= 0f))
{
if (weight >= 1f)
{
t.localPosition = localPosition;
t.localRotation = localRotation;
}
else
{
t.localPosition = Vector3.Lerp(t.localPosition, localPosition, weight);
t.localRotation = Quaternion.Lerp(t.localRotation, localRotation, weight);
}
}
}
public void StoreLocalState()
{
localPosition = t.localPosition;
localRotation = t.localRotation;
}
}
[Tooltip("If you have multiple IK components, then this should be the one that solves last each frame.")]
public IK ik;
[Tooltip("How long does it take to blend from ragdoll to animation?")]
public float ragdollToAnimationTime = 0.2f;
[Tooltip("If true, IK can be used on top of physical ragdoll simulation.")]
public bool applyIkOnRagdoll;
[Tooltip("How much velocity transfer from animation to ragdoll?")]
public float applyVelocity = 1f;
[Tooltip("How much angular velocity to transfer from animation to ragdoll?")]
public float applyAngularVelocity = 1f;
private Animator animator;
private Rigidbone[] rigidbones = new Rigidbone[0];
private Child[] children = new Child[0];
private bool enableRagdollFlag;
private AnimatorUpdateMode animatorUpdateMode;
private IK[] allIKComponents = new IK[0];
private bool[] fixTransforms = new bool[0];
private float ragdollWeight;
private float ragdollWeightV;
private bool fixedFrame;
private bool[] disabledIKComponents = new bool[0];
private bool animatorDisabled;
private bool isRagdoll
{
get
{
if (!rigidbones[0].r.isKinematic)
{
return !animator.enabled;
}
return false;
}
}
private bool ikUsed
{
get
{
if (ik == null)
{
return false;
}
if (ik.enabled && ik.GetIKSolver().IKPositionWeight > 0f)
{
return true;
}
IK[] array = allIKComponents;
foreach (IK iK in array)
{
if (iK.enabled && iK.GetIKSolver().IKPositionWeight > 0f)
{
return true;
}
}
return false;
}
}
public void EnableRagdoll()
{
if (!isRagdoll)
{
StopAllCoroutines();
enableRagdollFlag = true;
}
}
public void DisableRagdoll()
{
if (isRagdoll)
{
StoreLocalState();
StopAllCoroutines();
StartCoroutine(DisableRagdollSmooth());
}
}
public void Start()
{
animator = GetComponent<Animator>();
allIKComponents = GetComponentsInChildren<IK>();
disabledIKComponents = new bool[allIKComponents.Length];
fixTransforms = new bool[allIKComponents.Length];
if (ik != null)
{
IKSolver iKSolver = ik.GetIKSolver();
iKSolver.OnPostUpdate = (IKSolver.UpdateDelegate)Delegate.Combine(iKSolver.OnPostUpdate, new IKSolver.UpdateDelegate(AfterLastIK));
}
Rigidbody[] componentsInChildren = GetComponentsInChildren<Rigidbody>();
int num = ((componentsInChildren[0].gameObject == base.gameObject) ? 1 : 0);
rigidbones = new Rigidbone[(num == 0) ? componentsInChildren.Length : (componentsInChildren.Length - 1)];
for (int i = 0; i < rigidbones.Length; i++)
{
rigidbones[i] = new Rigidbone(componentsInChildren[i + num]);
}
Transform[] componentsInChildren2 = GetComponentsInChildren<Transform>();
children = new Child[componentsInChildren2.Length - 1];
for (int j = 0; j < children.Length; j++)
{
children[j] = new Child(componentsInChildren2[j + 1]);
}
}
private IEnumerator DisableRagdollSmooth()
{
for (int i = 0; i < rigidbones.Length; i++)
{
rigidbones[i].r.isKinematic = true;
}
for (int j = 0; j < allIKComponents.Length; j++)
{
allIKComponents[j].fixTransforms = fixTransforms[j];
if (disabledIKComponents[j])
{
allIKComponents[j].enabled = true;
}
}
animator.updateMode = animatorUpdateMode;
if (animatorDisabled)
{
animator.enabled = true;
animatorDisabled = false;
}
while (ragdollWeight > 0f)
{
ragdollWeight = Mathf.SmoothDamp(ragdollWeight, 0f, ref ragdollWeightV, ragdollToAnimationTime);
if (ragdollWeight < 0.001f)
{
ragdollWeight = 0f;
}
yield return null;
}
yield return null;
}
public void Update()
{
if (!isRagdoll)
{
return;
}
if (!applyIkOnRagdoll)
{
bool flag = false;
for (int i = 0; i < allIKComponents.Length; i++)
{
if (allIKComponents[i].enabled)
{
flag = true;
break;
}
}
if (flag)
{
for (int j = 0; j < allIKComponents.Length; j++)
{
disabledIKComponents[j] = false;
}
}
for (int k = 0; k < allIKComponents.Length; k++)
{
if (allIKComponents[k].enabled)
{
allIKComponents[k].enabled = false;
disabledIKComponents[k] = true;
}
}
return;
}
bool flag2 = false;
for (int l = 0; l < allIKComponents.Length; l++)
{
if (disabledIKComponents[l])
{
flag2 = true;
break;
}
}
if (!flag2)
{
return;
}
for (int m = 0; m < allIKComponents.Length; m++)
{
if (disabledIKComponents[m])
{
allIKComponents[m].enabled = true;
}
}
for (int n = 0; n < allIKComponents.Length; n++)
{
disabledIKComponents[n] = false;
}
}
public void FixedUpdate()
{
if (isRagdoll && applyIkOnRagdoll)
{
FixTransforms(1f);
}
fixedFrame = true;
}
public void LateUpdate()
{
if (animator.updateMode != AnimatorUpdateMode.Fixed || (animator.updateMode == AnimatorUpdateMode.Fixed && fixedFrame))
{
AfterAnimation();
}
fixedFrame = false;
if (!ikUsed)
{
OnFinalPose();
}
}
private void AfterLastIK()
{
if (ikUsed)
{
OnFinalPose();
}
}
private void AfterAnimation()
{
if (isRagdoll)
{
StoreLocalState();
}
else
{
FixTransforms(ragdollWeight);
}
}
private void OnFinalPose()
{
if (!isRagdoll)
{
RecordVelocities();
}
if (enableRagdollFlag)
{
RagdollEnabler();
}
}
private void RagdollEnabler()
{
StoreLocalState();
for (int i = 0; i < allIKComponents.Length; i++)
{
disabledIKComponents[i] = false;
}
if (!applyIkOnRagdoll)
{
for (int j = 0; j < allIKComponents.Length; j++)
{
if (allIKComponents[j].enabled)
{
allIKComponents[j].enabled = false;
disabledIKComponents[j] = true;
}
}
}
animatorUpdateMode = animator.updateMode;
animator.updateMode = AnimatorUpdateMode.Fixed;
if (animator.enabled)
{
animator.enabled = false;
animatorDisabled = true;
}
for (int k = 0; k < rigidbones.Length; k++)
{
rigidbones[k].WakeUp(applyVelocity, applyAngularVelocity);
}
for (int l = 0; l < fixTransforms.Length; l++)
{
fixTransforms[l] = allIKComponents[l].fixTransforms;
allIKComponents[l].fixTransforms = false;
}
ragdollWeight = 1f;
ragdollWeightV = 0f;
enableRagdollFlag = false;
}
private void RecordVelocities()
{
Rigidbone[] array = rigidbones;
for (int i = 0; i < array.Length; i++)
{
array[i].RecordVelocity();
}
}
private void StoreLocalState()
{
Child[] array = children;
for (int i = 0; i < array.Length; i++)
{
array[i].StoreLocalState();
}
}
private void FixTransforms(float weight)
{
Child[] array = children;
for (int i = 0; i < array.Length; i++)
{
array[i].FixTransform(weight);
}
}
private void OnDestroy()
{
if (ik != null)
{
IKSolver iKSolver = ik.GetIKSolver();
iKSolver.OnPostUpdate = (IKSolver.UpdateDelegate)Delegate.Remove(iKSolver.OnPostUpdate, new IKSolver.UpdateDelegate(AfterLastIK));
}
}
}
}