using UnityEngine; namespace Invector.IK { using Invector; /// /// Class that helps to Align arm to a position using a /// public class vArmAimAlign { #region Bones protected Transform root; protected Transform upperArm; protected Transform foreArm; protected Transform hand; public bool smoothIKAlignmentPoint = true; #endregion #region Bone Helpers public Transform upperArmHelper { get; protected set; } public Transform foreArmHelper { get; protected set; } public Transform handHelper { get; protected set; } public Transform aimReferenceHelper { get; protected set; } #endregion #region public Variables /// /// Smooth the alignment /// public float smooth { get; set; } /// /// Max angle (x) of the alignment based on Aim Reference /// public float maxVerticalAligmentAngle { get; set; } /// /// Max angle (y) of the alignment based on Aim Reference /// public float maxHorizontalAligmentAngle { get; set; } /// /// Reference transform to align arm. if this transform be null, the arm don't will be aligned; /// public Transform aimReference { get; set; } #endregion #region Protected Variables protected Quaternion _upperArmRotation; protected Quaternion _foreArmRotation; protected Quaternion _handRotation; protected Quaternion _aimReferenceRotation; protected Quaternion _upperArmRotationSmooth; protected Quaternion _handRotationSmooth; protected Quaternion smoothRotationDirection; protected Vector3 _upperArmPosition; protected Vector3 _foreArmPosition; protected Vector3 _handPosition; protected Vector3 _aimReferencePosition; #endregion public vArmAimAlign(Transform upperArm, Transform foreArm, Transform hand) { this.upperArm = upperArm; this.foreArm = foreArm; this.hand = hand; if (!upperArm || !foreArm || !hand) return; root = upperArm.parent; if (!upperArmHelper) { upperArmHelper = new GameObject("UpperArmHelper").transform; upperArmHelper.gameObject.tag = "Ignore Ragdoll"; } if (!foreArmHelper) { foreArmHelper = new GameObject("ForeArmHelper").transform; foreArmHelper.gameObject.tag = "Ignore Ragdoll"; } if (!handHelper) { handHelper = new GameObject("HandHelper").transform; handHelper.gameObject.tag = "Ignore Ragdoll"; } if (!aimReferenceHelper) { aimReferenceHelper = new GameObject("AimReferenceHelper").transform; aimReferenceHelper.gameObject.tag = "Ignore Ragdoll"; } upperArmHelper.parent = root; foreArmHelper.parent = upperArmHelper; handHelper.parent = foreArmHelper; aimReferenceHelper.parent = handHelper; } /// /// Check if the Arm is valid (all necessary transforms is assigned) /// public bool IsValid => this.upperArm && this.foreArm && this.hand && this.aimReference; /// /// Update Arm to use default animation alignment /// /// public void UpdateDefaultAlignment() { if (!IsValid) return; upperArmHelper.SetPositionAndRotation(upperArm.position, upperArm.rotation); upperArmHelper.localScale = upperArm.localScale; foreArmHelper.SetPositionAndRotation(foreArm.position, foreArm.rotation); foreArmHelper.localScale = foreArm.localScale; handHelper.SetPositionAndRotation(hand.position, hand.rotation); handHelper.localScale = hand.localScale; aimReferenceHelper.SetPositionAndRotation(aimReference.position, aimReference.rotation); aimReferenceHelper.localScale = aimReference.localScale; _upperArmRotation = upperArmHelper.localRotation; _foreArmRotation = foreArmHelper.localRotation; _handRotation = handHelper.localRotation; _aimReferenceRotation = aimReferenceHelper.localRotation; _upperArmPosition = upperArmHelper.localPosition; _foreArmPosition = foreArmHelper.localPosition; _handPosition = handHelper.localPosition; _aimReferencePosition = aimReferenceHelper.localPosition; } /// /// Restore last alignment applied from /// /// public void RestoreToLastAlignment() { if (!IsValid) return; upperArmHelper.localRotation = _upperArmRotation; foreArmHelper.localRotation = _foreArmRotation; handHelper.localRotation = _handRotation; aimReferenceHelper.localRotation = _aimReferenceRotation; upperArmHelper.localPosition = _upperArmPosition; foreArmHelper.localPosition = _foreArmPosition; handHelper.localPosition = _handPosition; aimReferenceHelper.localPosition = _aimReferencePosition; } /// /// Align Arm to a position /// /// position to align Arm /// alignment weight (0~1) /// /// public void AlignToArmToPosition(Vector3 targetPosition, float weight, bool alignUpperArm = true, bool alignHand = true) { if (!IsValid) return; if (smoothIKAlignmentPoint) { targetPosition = SmootAndClampPosition(targetPosition, maxHorizontalAligmentAngle, maxVerticalAligmentAngle); } if (alignUpperArm) AlignUpperArm(targetPosition, alignHand ? weight * 0.5f : weight); if (alignHand) AlignHand(targetPosition, weight); } /// /// Align upper arm to a position /// /// position to align upper arm /// alignment weight (0~1) protected virtual void AlignUpperArm(Vector3 targetPosition, float weight) { AlignArmBone(upperArm, upperArmHelper, ref _upperArmRotationSmooth, targetPosition, weight); } /// /// Align hand to a position /// /// position to align hand /// alignment weight (0~1) protected virtual void AlignHand(Vector3 targetPosition, float weight) { AlignArmBone(hand, handHelper, ref _handRotationSmooth, targetPosition, weight); } /// /// Align bone to a target position /// /// target bone /// target bone helper /// rotation of this bone (used to smooth) /// position to align bone /// alignment weight (0~1) protected virtual void AlignArmBone(Transform bone, Transform boneHelper, ref Quaternion smoothRotation, Vector3 targetPosition, float weight) { if (!smoothIKAlignmentPoint && System.Math.Round(weight, 1) == 0) { smoothRotation = Quaternion.identity; return; } Vector3 v = targetPosition - aimReferenceHelper.position; Quaternion targetRotation = Quaternion.identity; if (!smoothIKAlignmentPoint) { targetRotation = ClampRotation(Quaternion.LookRotation(v, aimReferenceHelper.up), maxHorizontalAligmentAngle * (weight), maxVerticalAligmentAngle * (weight)); } else { targetRotation = Quaternion.LookRotation(v, aimReferenceHelper.up); } var currentOrientation = boneHelper.InverseTransformDirection(aimReferenceHelper.forward); var targetOrientation = boneHelper.InverseTransformDirection(targetRotation * Vector3.forward); var alignmentRotation = Quaternion.FromToRotation(currentOrientation, targetOrientation); if ((!float.IsNaN(alignmentRotation.x) && !float.IsNaN(alignmentRotation.y) && !float.IsNaN(alignmentRotation.z))) { Quaternion additiveRotation; if (!smoothIKAlignmentPoint) { smoothRotation = Quaternion.Slerp(smoothRotation, Quaternion.Lerp(Quaternion.identity, alignmentRotation, weight), smooth * Time.fixedDeltaTime); additiveRotation = smoothRotation; } else additiveRotation = Quaternion.Lerp(Quaternion.identity, alignmentRotation, weight); bone.localRotation *= additiveRotation; boneHelper.localRotation *= additiveRotation; } } /// /// Clamp the target IK alignment rotation base to transform /// /// rotation to clamp /// max horizontal angle (y) /// max vertical angle (x) /// protected virtual Quaternion ClampRotation(Quaternion rotation, float maxHorizontalAngle, float maxVerticalAngle) { var direction = rotation * Vector3.forward; var localDirection = aimReferenceHelper.InverseTransformDirection(direction); var localRotation = Quaternion.LookRotation(localDirection); var rotationEuler = localRotation.eulerAngles.NormalizeAngle(); rotationEuler.y = Mathf.Clamp(rotationEuler.y, -maxHorizontalAngle, maxHorizontalAngle); rotationEuler.x = Mathf.Clamp(rotationEuler.x, -maxVerticalAngle, maxVerticalAngle); rotationEuler.z = 0; direction = aimReferenceHelper.TransformDirection(Quaternion.Euler(rotationEuler.NormalizeAngle()) * Vector3.forward); return Quaternion.LookRotation(direction, aimReferenceHelper.up); } /// /// Apply smooth and Clamp to the target Ik alignment Position base to transform /// /// position to clamp smoothly /// /// /// protected virtual Vector3 SmootAndClampPosition(Vector3 position, float maxHorizontalAngle, float maxVerticalAngle) { var direction = position - aimReferenceHelper.position; smoothRotationDirection = Quaternion.Slerp(ClampRotation(smoothRotationDirection, maxHorizontalAngle, maxVerticalAngle), Quaternion.LookRotation(position - aimReferenceHelper.position, aimReferenceHelper.up), smooth * Time.fixedDeltaTime); position = aimReferenceHelper.position + smoothRotationDirection * Vector3.forward * direction.magnitude; return position; } public void DrawBones(Color color) { if (!IsValid) return; Debug.DrawLine(upperArm.position, foreArm.position, color); Debug.DrawLine(foreArm.position, hand.position, color); } public void DrawHelpers(Color color) { if (!IsValid) return; Debug.DrawLine(upperArmHelper.position, foreArmHelper.position, color); Debug.DrawLine(foreArmHelper.position, handHelper.position, color); } } }