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.
		
		
		
		
		
			
		
			
	
	
		
			252 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C#
		
	
		
		
			
		
	
	
			252 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C#
		
	
| 
								 
											1 year ago
										 
									 | 
							
								using UnityEngine;
							 | 
						||
| 
								 | 
							
								using System.Collections;
							 | 
						||
| 
								 | 
							
								using System;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								namespace RootMotion.FinalIK {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/// <summary>
							 | 
						||
| 
								 | 
							
									/// Rotates a hierarchy of bones to make a Transform aim at a target.
							 | 
						||
| 
								 | 
							
									/// If there are problems with continuity and the solver get's jumpy, make sure to keep IKPosition at a safe distance from the transform and try decreasing solver and bone weights.
							 | 
						||
| 
								 | 
							
									/// </summary>
							 | 
						||
| 
								 | 
							
									[System.Serializable]
							 | 
						||
| 
								 | 
							
									public class IKSolverAim : IKSolverHeuristic {
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										#region Main Interface
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/// <summary>
							 | 
						||
| 
								 | 
							
										/// The transform that we want to aim at IKPosition.
							 | 
						||
| 
								 | 
							
										/// </summary>
							 | 
						||
| 
								 | 
							
										public Transform transform;
							 | 
						||
| 
								 | 
							
										/// <summary>
							 | 
						||
| 
								 | 
							
										/// The local axis of the Transform that you want to be aimed at IKPosition.
							 | 
						||
| 
								 | 
							
										/// </summary>
							 | 
						||
| 
								 | 
							
										public Vector3 axis = Vector3.forward;
							 | 
						||
| 
								 | 
							
										/// <summary>
							 | 
						||
| 
								 | 
							
										/// Keeps that axis of the Aim Transform directed at the polePosition.
							 | 
						||
| 
								 | 
							
										/// </summary>
							 | 
						||
| 
								 | 
							
										public Vector3 poleAxis = Vector3.up;
							 | 
						||
| 
								 | 
							
										/// <summary>
							 | 
						||
| 
								 | 
							
										/// The position in world space to keep the pole axis of the Aim Transform directed at.
							 | 
						||
| 
								 | 
							
										/// </summary>
							 | 
						||
| 
								 | 
							
										public Vector3 polePosition;
							 | 
						||
| 
								 | 
							
										/// <summary>
							 | 
						||
| 
								 | 
							
										/// The weight of the Pole.
							 | 
						||
| 
								 | 
							
										/// </summary>
							 | 
						||
| 
								 | 
							
										[Range(0f, 1f)]
							 | 
						||
| 
								 | 
							
										public float poleWeight;
							 | 
						||
| 
								 | 
							
										/// <summary>
							 | 
						||
| 
								 | 
							
										/// If assigned, will automatically set polePosition to the position of this Transform.
							 | 
						||
| 
								 | 
							
										/// </summary>
							 | 
						||
| 
								 | 
							
										public Transform poleTarget;
							 | 
						||
| 
								 | 
							
										/// <summary>
							 | 
						||
| 
								 | 
							
										/// Clamping rotation of the solver. 0 is free rotation, 1 is completely clamped to transform axis.
							 | 
						||
| 
								 | 
							
										/// </summary>
							 | 
						||
| 
								 | 
							
										[Range(0f, 1f)]
							 | 
						||
| 
								 | 
							
										public float clampWeight = 0.1f;
							 | 
						||
| 
								 | 
							
										/// <summary>
							 | 
						||
| 
								 | 
							
										/// Number of sine smoothing iterations applied to clamping to make it smoother.
							 | 
						||
| 
								 | 
							
										/// </summary>
							 | 
						||
| 
								 | 
							
										[Range(0, 2)]
							 | 
						||
| 
								 | 
							
										public int clampSmoothing = 2;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/// <summary>
							 | 
						||
| 
								 | 
							
										/// Gets the angular offset.
							 | 
						||
| 
								 | 
							
										/// </summary>
							 | 
						||
| 
								 | 
							
										public float GetAngle() {
							 | 
						||
| 
								 | 
							
											return Vector3.Angle(transformAxis, IKPosition - transform.position);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/// <summary>
							 | 
						||
| 
								 | 
							
										/// Gets the Axis of the AimTransform is world space.
							 | 
						||
| 
								 | 
							
										/// </summary>
							 | 
						||
| 
								 | 
							
										public Vector3 transformAxis {
							 | 
						||
| 
								 | 
							
											get {
							 | 
						||
| 
								 | 
							
												return transform.rotation * axis;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/// <summary>
							 | 
						||
| 
								 | 
							
										/// Gets the Pole Axis of the AimTransform is world space.
							 | 
						||
| 
								 | 
							
										/// </summary>
							 | 
						||
| 
								 | 
							
										public Vector3 transformPoleAxis {
							 | 
						||
| 
								 | 
							
											get {
							 | 
						||
| 
								 | 
							
												return transform.rotation * poleAxis;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/// <summary>
							 | 
						||
| 
								 | 
							
										/// Called before each iteration of the solver.
							 | 
						||
| 
								 | 
							
										/// </summary>
							 | 
						||
| 
								 | 
							
										public IterationDelegate OnPreIteration;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										#endregion Main Interface
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										protected override void OnInitiate() {
							 | 
						||
| 
								 | 
							
											if ((firstInitiation || !Application.isPlaying) && transform != null) {
							 | 
						||
| 
								 | 
							
												IKPosition = transform.position + transformAxis * 3f;
							 | 
						||
| 
								 | 
							
												polePosition = transform.position + transformPoleAxis * 3f;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Disable Rotation Limits from updating to take control of their execution order
							 | 
						||
| 
								 | 
							
											for (int i = 0; i < bones.Length; i++) {
							 | 
						||
| 
								 | 
							
												if (bones[i].rotationLimit != null) bones[i].rotationLimit.Disable();
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											step = 1f / (float)bones.Length;
							 | 
						||
| 
								 | 
							
											if (Application.isPlaying) axis = axis.normalized;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										protected override void OnUpdate() {
							 | 
						||
| 
								 | 
							
											if (axis == Vector3.zero) {
							 | 
						||
| 
								 | 
							
												if (!Warning.logged) LogWarning("IKSolverAim axis is Vector3.zero.");
							 | 
						||
| 
								 | 
							
												return;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if (poleAxis == Vector3.zero && poleWeight > 0f) {
							 | 
						||
| 
								 | 
							
												if (!Warning.logged) LogWarning("IKSolverAim poleAxis is Vector3.zero.");
							 | 
						||
| 
								 | 
							
												return;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if (target != null) IKPosition = target.position;
							 | 
						||
| 
								 | 
							
											if (poleTarget != null) polePosition = poleTarget.position;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if (XY) IKPosition.z = bones[0].transform.position.z;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Clamping weights
							 | 
						||
| 
								 | 
							
											if (IKPositionWeight <= 0) return;
							 | 
						||
| 
								 | 
							
											IKPositionWeight = Mathf.Clamp(IKPositionWeight, 0f, 1f);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Rotation Limit on the Aim Transform
							 | 
						||
| 
								 | 
							
											if (transform != lastTransform) {
							 | 
						||
| 
								 | 
							
												transformLimit = transform.GetComponent<RotationLimit>();
							 | 
						||
| 
								 | 
							
												if (transformLimit != null) transformLimit.enabled = false;
							 | 
						||
| 
								 | 
							
												lastTransform = transform;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if (transformLimit != null) transformLimit.Apply();
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// In case transform becomes unassigned in runtime
							 | 
						||
| 
								 | 
							
											if (transform == null) {
							 | 
						||
| 
								 | 
							
												if (!Warning.logged) LogWarning("Aim Transform unassigned in Aim IK solver. Please Assign a Transform (lineal descendant to the last bone in the spine) that you want to be aimed at IKPosition");
							 | 
						||
| 
								 | 
							
												return;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											clampWeight = Mathf.Clamp(clampWeight, 0f, 1f);
							 | 
						||
| 
								 | 
							
											clampedIKPosition = GetClampedIKPosition();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											Vector3 dir = clampedIKPosition - transform.position;
							 | 
						||
| 
								 | 
							
											dir = Vector3.Slerp(transformAxis * dir.magnitude, dir, IKPositionWeight);
							 | 
						||
| 
								 | 
							
											clampedIKPosition = transform.position + dir;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Iterating the solver
							 | 
						||
| 
								 | 
							
											for (int i = 0; i < maxIterations; i++) {
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												// Optimizations
							 | 
						||
| 
								 | 
							
												if (i >= 1 && tolerance > 0 && GetAngle() < tolerance) break;
							 | 
						||
| 
								 | 
							
												lastLocalDirection = localDirection;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if (OnPreIteration != null) OnPreIteration(i);
							 | 
						||
| 
								 | 
							
												
							 | 
						||
| 
								 | 
							
												Solve();
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											lastLocalDirection = localDirection;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										protected override int minBones { get { return 1; }}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										private float step;
							 | 
						||
| 
								 | 
							
										private Vector3 clampedIKPosition;
							 | 
						||
| 
								 | 
							
										private RotationLimit transformLimit;
							 | 
						||
| 
								 | 
							
										private Transform lastTransform;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        /*
							 | 
						||
| 
								 | 
							
										 * Solving the hierarchy
							 | 
						||
| 
								 | 
							
										 * */
							 | 
						||
| 
								 | 
							
								        private void Solve() {
							 | 
						||
| 
								 | 
							
											// Rotating bones to get closer to target.
							 | 
						||
| 
								 | 
							
											for (int i = 0; i < bones.Length - 1; i++) RotateToTarget(clampedIKPosition, bones[i], step * (i + 1) * IKPositionWeight * bones[i].weight);
							 | 
						||
| 
								 | 
							
											RotateToTarget(clampedIKPosition, bones[bones.Length - 1], IKPositionWeight * bones[bones.Length - 1].weight);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										/*
							 | 
						||
| 
								 | 
							
										 * Clamping the IKPosition to legal range
							 | 
						||
| 
								 | 
							
										 * */
							 | 
						||
| 
								 | 
							
										private Vector3 GetClampedIKPosition() {
							 | 
						||
| 
								 | 
							
											if (clampWeight <= 0f) return IKPosition;
							 | 
						||
| 
								 | 
							
											if (clampWeight >= 1f) return transform.position + transformAxis * (IKPosition - transform.position).magnitude;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Getting the dot product of IK direction and transformAxis
							 | 
						||
| 
								 | 
							
											//float dot = (Vector3.Dot(transformAxis, (IKPosition - transform.position).normalized) + 1) * 0.5f;
							 | 
						||
| 
								 | 
							
											float angle = Vector3.Angle(transformAxis, (IKPosition - transform.position));
							 | 
						||
| 
								 | 
							
											float dot = 1f - (angle / 180f);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Clamping the target
							 | 
						||
| 
								 | 
							
											float targetClampMlp = clampWeight > 0? Mathf.Clamp(1f - ((clampWeight - dot) / (1f - dot)), 0f, 1f): 1f;
							 | 
						||
| 
								 | 
							
											
							 | 
						||
| 
								 | 
							
											// Calculating the clamp multiplier
							 | 
						||
| 
								 | 
							
											float clampMlp = clampWeight > 0? Mathf.Clamp(dot / clampWeight, 0f, 1f): 1f;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											for (int i = 0; i < clampSmoothing; i++) {
							 | 
						||
| 
								 | 
							
												float sinF = clampMlp * Mathf.PI * 0.5f;
							 | 
						||
| 
								 | 
							
												clampMlp = Mathf.Sin(sinF);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Slerping the IK direction (don't use Lerp here, it breaks it)
							 | 
						||
| 
								 | 
							
											return transform.position + Vector3.Slerp(transformAxis * 10f, IKPosition - transform.position, clampMlp * targetClampMlp);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										/*
							 | 
						||
| 
								 | 
							
										 * Rotating bone to get transform aim closer to target
							 | 
						||
| 
								 | 
							
										 * */
							 | 
						||
| 
								 | 
							
										private void RotateToTarget(Vector3 targetPosition, IKSolver.Bone bone, float weight) {
							 | 
						||
| 
								 | 
							
											// Swing
							 | 
						||
| 
								 | 
							
											if (XY) {
							 | 
						||
| 
								 | 
							
												if (weight >= 0f) {
							 | 
						||
| 
								 | 
							
													Vector3 dir = transformAxis;
							 | 
						||
| 
								 | 
							
													Vector3 targetDir = targetPosition - transform.position;
							 | 
						||
| 
								 | 
							
													
							 | 
						||
| 
								 | 
							
													float angleDir = Mathf.Atan2(dir.x, dir.y) * Mathf.Rad2Deg;
							 | 
						||
| 
								 | 
							
													float angleTarget = Mathf.Atan2(targetDir.x, targetDir.y) * Mathf.Rad2Deg;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													bone.transform.rotation = Quaternion.AngleAxis(Mathf.DeltaAngle(angleDir, angleTarget), Vector3.back) * bone.transform.rotation;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											} else {
							 | 
						||
| 
								 | 
							
												if (weight >= 0f) {
							 | 
						||
| 
								 | 
							
													Quaternion rotationOffset = Quaternion.FromToRotation(transformAxis, targetPosition - transform.position);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													if (weight >= 1f) {
							 | 
						||
| 
								 | 
							
														bone.transform.rotation = rotationOffset * bone.transform.rotation;
							 | 
						||
| 
								 | 
							
													} else {
							 | 
						||
| 
								 | 
							
														bone.transform.rotation = Quaternion.Lerp(Quaternion.identity, rotationOffset, weight) * bone.transform.rotation;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												// Pole
							 | 
						||
| 
								 | 
							
												if (poleWeight > 0f) {
							 | 
						||
| 
								 | 
							
													Vector3 poleDirection = polePosition - transform.position;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													// Ortho-normalize to transform axis to make this a twisting only operation
							 | 
						||
| 
								 | 
							
													Vector3 poleDirOrtho = poleDirection;
							 | 
						||
| 
								 | 
							
													Vector3 normal = transformAxis;
							 | 
						||
| 
								 | 
							
													Vector3.OrthoNormalize(ref normal, ref poleDirOrtho);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													Quaternion toPole = Quaternion.FromToRotation(transformPoleAxis, poleDirOrtho);
							 | 
						||
| 
								 | 
							
													bone.transform.rotation = Quaternion.Lerp(Quaternion.identity, toPole, weight * poleWeight) * bone.transform.rotation;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if (useRotationLimits && bone.rotationLimit != null) bone.rotationLimit.Apply();
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										/*
							 | 
						||
| 
								 | 
							
										 * Gets the direction from last bone's forward in first bone's local space.
							 | 
						||
| 
								 | 
							
										 * */
							 | 
						||
| 
								 | 
							
										protected override Vector3 localDirection {
							 | 
						||
| 
								 | 
							
											get {
							 | 
						||
| 
								 | 
							
												return bones[0].transform.InverseTransformDirection(bones[bones.Length - 1].transform.forward);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 |