You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			281 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C#
		
	
			
		
		
	
	
			281 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C#
		
	
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;
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |