using UnityEngine; using System.Collections; namespace RootMotion.FinalIK { /// /// Grounding for BipedIK characters. /// [HelpURL("http://www.root-motion.com/finalikdox/html/page9.html")] [AddComponentMenu("Scripts/RootMotion.FinalIK/Grounder/Grounder Biped")] public class GrounderBipedIK: 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_biped_i_k.html"); } #region Main Interface /// /// The BipedIK componet. /// [Tooltip("The BipedIK componet.")] public BipedIK ik; /// /// The amount of spine bending towards upward slopes. /// [Tooltip("The amount of spine bending towards upward slopes.")] public float spineBend = 7f; /// /// The interpolation speed of spine bending. /// [Tooltip("The interpolation speed of spine bending.")] public float spineSpeed = 3f; #endregion Main Interface public override void ResetPosition() { solver.Reset(); spineOffset = Vector3.zero; } private Transform[] feet = new Transform[2]; private Quaternion[] footRotations = new Quaternion[2]; private Vector3 animatedPelvisLocalPosition, solvedPelvisLocalPosition; private Vector3 spineOffset; private float lastWeight; // Can we initiate the Grounding? private bool IsReadyToInitiate() { if (ik == null) return false; if (!ik.solvers.leftFoot.initiated) return false; if (!ik.solvers.rightFoot.initiated) return false; return true; } // Initiate once we have a BipedIK component void Update() { weight = Mathf.Clamp(weight, 0f, 1f); if (weight <= 0f) return; if (initiated) return; if (!IsReadyToInitiate()) return; Initiate(); } private void Initiate() { // Gathering both foot bones from the BipedIK feet = new Transform[2]; footRotations = new Quaternion[2]; feet[0] = ik.references.leftFoot; feet[1] = ik.references.rightFoot; footRotations[0] = Quaternion.identity; footRotations[1] = Quaternion.identity; // Adding to the delegates to get call at certain points in the solving process ik.solvers.spine.OnPreUpdate += OnSolverUpdate; ik.solvers.rightFoot.OnPostUpdate += OnPostSolverUpdate; // Store the default localPosition of the pelvis animatedPelvisLocalPosition = ik.references.pelvis.localPosition; // Initiate the Grounding solver.Initiate(ik.references.root, feet); initiated = true; } // Weigh out the limb solvers properly when the component is disabled void OnDisable() { if (!initiated) return; ik.solvers.leftFoot.IKPositionWeight = 0f; ik.solvers.rightFoot.IKPositionWeight = 0f; } // Called before updating the spine 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 (OnPreGrounder != null) OnPreGrounder(); // If the pelvis local position has not changed since last solved state, consider it unanimated if (ik.references.pelvis.localPosition != solvedPelvisLocalPosition) animatedPelvisLocalPosition = ik.references.pelvis.localPosition; else ik.references.pelvis.localPosition = animatedPelvisLocalPosition; // Update the Grounding solver.Update(); // Move the pelvis ik.references.pelvis.position += solver.pelvis.IKOffset * weight; // Update IKPositions and IKPositionWeights of the feet SetLegIK(ik.solvers.leftFoot, 0); SetLegIK(ik.solvers.rightFoot, 1); // Bending the spine if (spineBend != 0f && ik.references.spine.Length > 0) { spineSpeed = Mathf.Clamp(spineSpeed, 0f, spineSpeed); Vector3 spineOffseTarget = GetSpineOffsetTarget() * weight; spineOffset = Vector3.Lerp(spineOffset, spineOffseTarget * spineBend, Time.deltaTime * spineSpeed); // Store upper arm rotations to revert them after we rotate the spine Quaternion leftArmRotation = ik.references.leftUpperArm.rotation; Quaternion rightArmRotation = ik.references.rightUpperArm.rotation; // Get the offset rotation for the spine Vector3 up = solver.up; Quaternion f = Quaternion.FromToRotation(up, up + spineOffset); // Rotate the spine ik.references.spine[0].rotation = f * ik.references.spine[0].rotation; // Revert the upper arms ik.references.leftUpperArm.rotation = leftArmRotation; ik.references.rightUpperArm.rotation = rightArmRotation; ik.solvers.lookAt.SetDirty(); } if (OnPostGrounder != null) OnPostGrounder(); } // Set the IK position and weight for a limb private void SetLegIK(IKSolverLimb limb, int index) { footRotations[index] = feet[index].rotation; limb.IKPosition = solver.legs[index].IKPosition; limb.IKPositionWeight = weight; } // Rotating the feet after IK has finished private void OnPostSolverUpdate() { if (weight <= 0f) return; if (!enabled) return; 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 = ik.references.pelvis.localPosition; if (OnPostIK != null) OnPostIK(); } // Cleaning up the delegates void OnDestroy() { if (initiated && ik != null) { ik.solvers.spine.OnPreUpdate -= OnSolverUpdate; ik.solvers.rightFoot.OnPostUpdate -= OnPostSolverUpdate; } } } }