using System; using UnityEngine; namespace RootMotion.FinalIK { public class HitReaction : OffsetModifier { [Serializable] public abstract class HitPoint { [Tooltip("Just for visual clarity, not used at all")] public string name; [Tooltip("Linking this hit point to a collider")] public Collider collider; [Tooltip("Only used if this hit point gets hit when already processing another hit")] [SerializeField] private float crossFadeTime = 0.1f; private float length; private float crossFadeSpeed; private float lastTime; public bool inProgress => timer < length; protected float crossFader { get; private set; } protected float timer { get; private set; } protected Vector3 force { get; private set; } public virtual void Hit(Vector3 force, Vector3 point) { if (length == 0f) { length = GetLength(); } if (length <= 0f) { Debug.LogError("Hit Point WeightCurve length is zero."); return; } if (timer < 1f) { crossFader = 0f; } crossFadeSpeed = ((crossFadeTime > 0f) ? (1f / crossFadeTime) : 0f); CrossFadeStart(); timer = 0f; this.force = force; } public void Apply(IKSolverFullBodyBiped solver, float weight) { float num = Time.time - lastTime; lastTime = Time.time; if (!(timer >= length)) { timer = Mathf.Clamp(timer + num, 0f, length); if (crossFadeSpeed > 0f) { crossFader = Mathf.Clamp(crossFader + num * crossFadeSpeed, 0f, 1f); } else { crossFader = 1f; } OnApply(solver, weight); } } protected abstract float GetLength(); protected abstract void CrossFadeStart(); protected abstract void OnApply(IKSolverFullBodyBiped solver, float weight); } [Serializable] public class HitPointEffector : HitPoint { [Serializable] public class EffectorLink { [Tooltip("The FBBIK effector type")] public FullBodyBipedEffector effector; [Tooltip("The weight of this effector (could also be negative)")] public float weight; private Vector3 lastValue; private Vector3 current; public void Apply(IKSolverFullBodyBiped solver, Vector3 offset, float crossFader) { current = Vector3.Lerp(lastValue, offset * weight, crossFader); solver.GetEffector(effector).positionOffset += current; } public void CrossFadeStart() { lastValue = current; } } [Tooltip("Offset magnitude in the direction of the hit force")] public AnimationCurve offsetInForceDirection; [Tooltip("Offset magnitude in the direction of character.up")] public AnimationCurve offsetInUpDirection; [Tooltip("Linking this offset to the FBBIK effectors")] public EffectorLink[] effectorLinks; protected override float GetLength() { float num = ((offsetInForceDirection.keys.Length != 0) ? offsetInForceDirection.keys[offsetInForceDirection.length - 1].time : 0f); float min = ((offsetInUpDirection.keys.Length != 0) ? offsetInUpDirection.keys[offsetInUpDirection.length - 1].time : 0f); return Mathf.Clamp(num, min, num); } protected override void CrossFadeStart() { EffectorLink[] array = effectorLinks; for (int i = 0; i < array.Length; i++) { array[i].CrossFadeStart(); } } protected override void OnApply(IKSolverFullBodyBiped solver, float weight) { Vector3 vector = solver.GetRoot().up * base.force.magnitude; Vector3 offset = offsetInForceDirection.Evaluate(base.timer) * base.force + offsetInUpDirection.Evaluate(base.timer) * vector; offset *= weight; EffectorLink[] array = effectorLinks; for (int i = 0; i < array.Length; i++) { array[i].Apply(solver, offset, base.crossFader); } } } [Serializable] public class HitPointBone : HitPoint { [Serializable] public class BoneLink { [Tooltip("Reference to the bone that this hit point rotates")] public Transform bone; [Tooltip("Weight of rotating the bone")] [Range(0f, 1f)] public float weight; private Quaternion lastValue = Quaternion.identity; private Quaternion current = Quaternion.identity; public void Apply(IKSolverFullBodyBiped solver, Quaternion offset, float crossFader) { current = Quaternion.Lerp(lastValue, Quaternion.Lerp(Quaternion.identity, offset, weight), crossFader); bone.rotation = current * bone.rotation; } public void CrossFadeStart() { lastValue = current; } } [Tooltip("The angle to rotate the bone around its rigidbody's world center of mass")] public AnimationCurve aroundCenterOfMass; [Tooltip("Linking this hit point to bone(s)")] public BoneLink[] boneLinks; private Rigidbody rigidbody; private Vector3 comAxis; public override void Hit(Vector3 force, Vector3 point) { base.Hit(force, point); if (rigidbody == null) { rigidbody = collider.GetComponent(); } Vector3 vector = ((rigidbody != null) ? rigidbody.worldCenterOfMass : collider.transform.position); comAxis = Vector3.Cross(force, point - vector); } protected override float GetLength() { if (aroundCenterOfMass.keys.Length == 0) { return 0f; } return aroundCenterOfMass.keys[aroundCenterOfMass.length - 1].time; } protected override void CrossFadeStart() { BoneLink[] array = boneLinks; for (int i = 0; i < array.Length; i++) { array[i].CrossFadeStart(); } } protected override void OnApply(IKSolverFullBodyBiped solver, float weight) { Quaternion offset = Quaternion.AngleAxis(aroundCenterOfMass.Evaluate(base.timer) * weight, comAxis); BoneLink[] array = boneLinks; for (int i = 0; i < array.Length; i++) { array[i].Apply(solver, offset, base.crossFader); } } } [Tooltip("Hit points for the FBBIK effectors")] public HitPointEffector[] effectorHitPoints; [Tooltip(" Hit points for bones without an effector, such as the head")] public HitPointBone[] boneHitPoints; public bool inProgress { get { HitPointEffector[] array = effectorHitPoints; for (int i = 0; i < array.Length; i++) { if (array[i].inProgress) { return true; } } HitPointBone[] array2 = boneHitPoints; for (int i = 0; i < array2.Length; i++) { if (array2[i].inProgress) { return true; } } return false; } } protected override void OnModifyOffset() { HitPointEffector[] array = effectorHitPoints; for (int i = 0; i < array.Length; i++) { array[i].Apply(ik.solver, weight); } HitPointBone[] array2 = boneHitPoints; for (int i = 0; i < array2.Length; i++) { array2[i].Apply(ik.solver, weight); } } public void Hit(Collider collider, Vector3 force, Vector3 point) { if (ik == null) { Debug.LogError("No IK assigned in HitReaction"); return; } HitPointEffector[] array = effectorHitPoints; foreach (HitPointEffector hitPointEffector in array) { if (hitPointEffector.collider == collider) { hitPointEffector.Hit(force, point); } } HitPointBone[] array2 = boneHitPoints; foreach (HitPointBone hitPointBone in array2) { if (hitPointBone.collider == collider) { hitPointBone.Hit(force, point); } } } } }