LookAtController.cs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. using UnityEngine;
  2. using System.Collections;
  3. namespace RootMotion.FinalIK {
  4. public class LookAtController : MonoBehaviour {
  5. public LookAtIK ik;
  6. [Header("Target Smoothing")]
  7. [Tooltip("The target to look at. Do not use the Target transform that is assigned to LookAtIK. Set to null if you wish to stop looking.")]
  8. public Transform target;
  9. [Range(0f, 1f)] public float weight = 1f;
  10. public Vector3 offset;
  11. [Tooltip("The time it takes to switch targets.")]
  12. public float targetSwitchSmoothTime = 0.3f;
  13. [Tooltip("The time it takes to blend in/out of LookAtIK weight.")]
  14. public float weightSmoothTime = 0.3f;
  15. [Header("Turning Towards The Target")]
  16. [Tooltip("Enables smooth turning towards the target according to the parameters under this header.")]
  17. public bool smoothTurnTowardsTarget = true;
  18. [Tooltip("Speed of turning towards the target using Vector3.RotateTowards.")]
  19. public float maxRadiansDelta = 3f;
  20. [Tooltip("Speed of moving towards the target using Vector3.RotateTowards.")]
  21. public float maxMagnitudeDelta = 3f;
  22. [Tooltip("Speed of slerping towards the target.")]
  23. public float slerpSpeed = 3f;
  24. [Tooltip("The position of the pivot that the look at target is rotated around relative to the root of the character.")]
  25. public Vector3 pivotOffsetFromRoot = Vector3.up;
  26. [Tooltip("Minimum distance of looking from the first bone. Keeps the solver from failing if the target is too close.")]
  27. public float minDistance = 1f;
  28. [Header("RootRotation")]
  29. [Tooltip("Character root will be rotate around the Y axis to keep root forward within this angle from the look direction.")]
  30. [Range(0f, 180f)]
  31. public float maxRootAngle = 45f;
  32. private Transform lastTarget;
  33. private float switchWeight, switchWeightV;
  34. private float weightV;
  35. private Vector3 lastPosition;
  36. private Vector3 dir;
  37. private bool lastSmoothTowardsTarget;
  38. void Start() {
  39. lastPosition = ik.solver.IKPosition;
  40. dir = ik.solver.IKPosition - pivot;
  41. }
  42. void LateUpdate () {
  43. // If target has changed...
  44. if (target != lastTarget) {
  45. if (lastTarget == null && target != null && ik.solver.IKPositionWeight <= 0f) {
  46. lastPosition = target.position;
  47. dir = target.position - pivot;
  48. ik.solver.IKPosition = target.position + offset;
  49. } else {
  50. lastPosition = ik.solver.IKPosition;
  51. dir = ik.solver.IKPosition - pivot;
  52. }
  53. switchWeight = 0f;
  54. lastTarget = target;
  55. }
  56. // Smooth weight
  57. float targetWeight = target != null ? weight : 0f;
  58. ik.solver.IKPositionWeight = Mathf.SmoothDamp(ik.solver.IKPositionWeight, targetWeight, ref weightV, weightSmoothTime);
  59. if (ik.solver.IKPositionWeight >= 0.999f && targetWeight > ik.solver.IKPositionWeight) ik.solver.IKPositionWeight = 1f;
  60. if (ik.solver.IKPositionWeight <= 0.001f && targetWeight < ik.solver.IKPositionWeight) ik.solver.IKPositionWeight = 0f;
  61. if (ik.solver.IKPositionWeight <= 0f) return;
  62. // Smooth target switching
  63. switchWeight = Mathf.SmoothDamp(switchWeight, 1f, ref switchWeightV, targetSwitchSmoothTime);
  64. if (switchWeight >= 0.999f) switchWeight = 1f;
  65. if (target != null) {
  66. ik.solver.IKPosition = Vector3.Lerp(lastPosition, target.position + offset, switchWeight);
  67. }
  68. // Smooth turn towards target
  69. if (smoothTurnTowardsTarget != lastSmoothTowardsTarget) {
  70. dir = ik.solver.IKPosition - pivot;
  71. lastSmoothTowardsTarget = smoothTurnTowardsTarget;
  72. }
  73. if (smoothTurnTowardsTarget) {
  74. Vector3 targetDir = ik.solver.IKPosition - pivot;
  75. dir = Vector3.Slerp(dir, targetDir, Time.deltaTime * slerpSpeed);
  76. dir = Vector3.RotateTowards(dir, targetDir, Time.deltaTime * maxRadiansDelta, maxMagnitudeDelta);
  77. ik.solver.IKPosition = pivot + dir;
  78. }
  79. // Min distance from the pivot
  80. ApplyMinDistance();
  81. // Root rotation
  82. RootRotation();
  83. }
  84. // Pivot of rotating the aiming direction.
  85. private Vector3 pivot {
  86. get {
  87. return ik.transform.position + ik.transform.rotation * pivotOffsetFromRoot;
  88. }
  89. }
  90. // Make sure aiming target is not too close (might make the solver instable when the target is closer to the first bone than the last bone is).
  91. void ApplyMinDistance() {
  92. Vector3 aimFrom = pivot;
  93. Vector3 direction = (ik.solver.IKPosition - aimFrom);
  94. direction = direction.normalized * Mathf.Max(direction.magnitude, minDistance);
  95. ik.solver.IKPosition = aimFrom + direction;
  96. }
  97. // Character root will be rotate around the Y axis to keep root forward within this angle from the looking direction.
  98. private void RootRotation() {
  99. float max = Mathf.Lerp(180f, maxRootAngle, ik.solver.IKPositionWeight);
  100. if (max < 180f) {
  101. Vector3 faceDirLocal = Quaternion.Inverse(ik.transform.rotation) * (ik.solver.IKPosition - pivot);
  102. float angle = Mathf.Atan2(faceDirLocal.x, faceDirLocal.z) * Mathf.Rad2Deg;
  103. float rotation = 0f;
  104. if (angle > max) {
  105. rotation = angle - max;
  106. }
  107. if (angle < -max) {
  108. rotation = angle + max;
  109. }
  110. ik.transform.rotation = Quaternion.AngleAxis(rotation, ik.transform.up) * ik.transform.rotation;
  111. }
  112. }
  113. }
  114. }