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.
		
		
		
		
		
			
		
			
				
	
	
		
			115 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			C#
		
	
			
		
		
	
	
			115 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			C#
		
	
using UnityEngine;
 | 
						|
using System.Collections;
 | 
						|
using System;
 | 
						|
 | 
						|
namespace RootMotion.FinalIK {
 | 
						|
 | 
						|
	/// <summary>
 | 
						|
	/// CCD (Cyclic Coordinate Descent) constrainable heuristic inverse kinematics algorithm.
 | 
						|
	/// </summary>
 | 
						|
	[System.Serializable]
 | 
						|
	public class IKSolverCCD : IKSolverHeuristic {
 | 
						|
		
 | 
						|
		#region Main Interface
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		/// CCD tends to overemphasise the rotations of the bones closer to the target position. Reducing bone weight down the hierarchy will compensate for this effect.
 | 
						|
		/// </summary>
 | 
						|
		public void FadeOutBoneWeights() {
 | 
						|
			if (bones.Length < 2) return;
 | 
						|
			
 | 
						|
			bones[0].weight = 1f;
 | 
						|
			float step = 1f / (bones.Length - 1);
 | 
						|
			
 | 
						|
			for (int i = 1; i < bones.Length; i++) {
 | 
						|
				bones[i].weight = step * (bones.Length - 1 - i);
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/// <summary>
 | 
						|
		/// Called before each iteration of the solver.
 | 
						|
		/// </summary>
 | 
						|
		public IterationDelegate OnPreIteration;
 | 
						|
		
 | 
						|
		#endregion Main Interface
 | 
						|
		
 | 
						|
		protected override void OnInitiate() {
 | 
						|
			if (firstInitiation || !Application.isPlaying) IKPosition = bones[bones.Length - 1].transform.position;
 | 
						|
			
 | 
						|
			InitiateBones();
 | 
						|
		}
 | 
						|
		
 | 
						|
		protected override void OnUpdate() {
 | 
						|
			if (IKPositionWeight <= 0) return;	
 | 
						|
			IKPositionWeight = Mathf.Clamp(IKPositionWeight, 0f, 1f);
 | 
						|
 | 
						|
			if (target != null) IKPosition = target.position;
 | 
						|
			if (XY) IKPosition.z = bones[0].transform.position.z;
 | 
						|
			
 | 
						|
			Vector3 singularityOffset = maxIterations > 1? GetSingularityOffset(): Vector3.zero;
 | 
						|
 | 
						|
			// Iterating the solver
 | 
						|
			for (int i = 0; i < maxIterations; i++) {
 | 
						|
				
 | 
						|
				// Optimizations
 | 
						|
				if (singularityOffset == Vector3.zero && i >= 1 && tolerance > 0 && positionOffset < tolerance * tolerance) break;
 | 
						|
				lastLocalDirection = localDirection;
 | 
						|
 | 
						|
				if (OnPreIteration != null) OnPreIteration(i);
 | 
						|
				
 | 
						|
				Solve(IKPosition + (i == 0? singularityOffset: Vector3.zero));
 | 
						|
			}
 | 
						|
			
 | 
						|
			lastLocalDirection = localDirection;
 | 
						|
		}
 | 
						|
 | 
						|
        /*
 | 
						|
		 * Solve the CCD algorithm
 | 
						|
		 * */
 | 
						|
        protected void Solve(Vector3 targetPosition) {
 | 
						|
			// 2D
 | 
						|
			if (XY) {
 | 
						|
				for (int i = bones.Length - 2; i > -1; i--) {
 | 
						|
					//CCD tends to overemphasise the rotations of the bones closer to the target position. Reducing bone weight down the hierarchy will compensate for this effect.
 | 
						|
					float w = bones[i].weight * IKPositionWeight;
 | 
						|
 | 
						|
					if (w > 0f) {
 | 
						|
						Vector3 toLastBone = bones[bones.Length - 1].transform.position - bones[i].transform.position;
 | 
						|
						Vector3 toTarget = targetPosition - bones[i].transform.position;
 | 
						|
 | 
						|
						float angleToLastBone = Mathf.Atan2(toLastBone.x, toLastBone.y) * Mathf.Rad2Deg;
 | 
						|
						float angleToTarget = Mathf.Atan2(toTarget.x, toTarget.y) * Mathf.Rad2Deg;
 | 
						|
 | 
						|
						// Rotation to direct the last bone to the target
 | 
						|
						bones[i].transform.rotation = Quaternion.AngleAxis(Mathf.DeltaAngle(angleToLastBone, angleToTarget) * w, Vector3.back) * bones[i].transform.rotation;
 | 
						|
					}
 | 
						|
 | 
						|
					// Rotation Constraints
 | 
						|
					if (useRotationLimits && bones[i].rotationLimit != null) bones[i].rotationLimit.Apply();
 | 
						|
				}
 | 
						|
			// 3D
 | 
						|
			} else {
 | 
						|
				for (int i = bones.Length - 2; i > -1; i--) {
 | 
						|
					// Slerp if weight is < 0
 | 
						|
					//CCD tends to overemphasise the rotations of the bones closer to the target position. Reducing bone weight down the hierarchy will compensate for this effect.
 | 
						|
					float w = bones[i].weight * IKPositionWeight;
 | 
						|
 | 
						|
					if (w > 0f) {
 | 
						|
						Vector3 toLastBone = bones[bones.Length - 1].transform.position - bones[i].transform.position;
 | 
						|
						Vector3 toTarget = targetPosition - bones[i].transform.position;
 | 
						|
						
 | 
						|
						// Get the rotation to direct the last bone to the target
 | 
						|
						Quaternion targetRotation = Quaternion.FromToRotation(toLastBone, toTarget) * bones[i].transform.rotation;
 | 
						|
 | 
						|
						if (w >= 1) bones[i].transform.rotation = targetRotation;
 | 
						|
						else bones[i].transform.rotation = Quaternion.Lerp(bones[i].transform.rotation, targetRotation, w);
 | 
						|
					}
 | 
						|
 | 
						|
					// Rotation Constraints
 | 
						|
					if (useRotationLimits && bones[i].rotationLimit != null) bones[i].rotationLimit.Apply();
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |