AnimatorController3rdPersonIK.cs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. using UnityEngine;
  2. using System.Collections;
  3. using RootMotion.FinalIK;
  4. namespace RootMotion.Demos {
  5. // Extends the default Animator controller for 3rd person view to add IK
  6. public class AnimatorController3rdPersonIK: AnimatorController3rdPerson {
  7. [Range(0f, 1f)] public float headLookWeight = 1f;
  8. public Vector3 gunHoldOffset;
  9. public Vector3 leftHandOffset;
  10. public Recoil recoil;
  11. // The IK components
  12. private AimIK aim;
  13. private FullBodyBipedIK ik;
  14. private Vector3 headLookAxis;
  15. private Vector3 leftHandPosRelToRightHand;
  16. private Quaternion leftHandRotRelToRightHand;
  17. private Vector3 aimTarget;
  18. private Quaternion rightHandRotation;
  19. protected override void Start() {
  20. base.Start();
  21. // Find the IK components
  22. aim = GetComponent<AimIK>();
  23. ik = GetComponent<FullBodyBipedIK>();
  24. ik.solver.OnPreRead += OnPreRead;
  25. // Disable the IK components to manage their updating
  26. aim.enabled = false;
  27. ik.enabled = false;
  28. // Presuming head is rotated towards character forward at Start
  29. headLookAxis = ik.references.head.InverseTransformVector(ik.references.root.forward);
  30. // Enable the upper-body aiming pose
  31. animator.SetLayerWeight(1, 1f);
  32. }
  33. public override void Move(Vector3 moveInput, bool isMoving, Vector3 faceDirection, Vector3 aimTarget) {
  34. base.Move(moveInput, isMoving, faceDirection, aimTarget);
  35. // Snatch the aim target from the Move call, it will be used by AimIK (Move is called by CharacterController3rdPerson that controls the actual motion of the character)
  36. this.aimTarget = aimTarget;
  37. // IK procedures, make sure this updates AFTER the camera is moved/rotated
  38. // Sample something from the current pose of the character
  39. Read();
  40. // AimIK pass
  41. AimIK();
  42. // FBBIK pass - put the left hand back to where it was relative to the right hand before AimIK solved
  43. FBBIK();
  44. // AimIK pass
  45. AimIK();
  46. // Rotate the head to look at the aim target
  47. HeadLookAt(aimTarget);
  48. }
  49. private void Read() {
  50. // Remember the position and rotation of the left hand relative to the right hand
  51. leftHandPosRelToRightHand = ik.references.rightHand.InverseTransformPoint(ik.references.leftHand.position);
  52. leftHandRotRelToRightHand = Quaternion.Inverse(ik.references.rightHand.rotation) * ik.references.leftHand.rotation;
  53. }
  54. private void AimIK() {
  55. // Set AimIK target position and update
  56. aim.solver.IKPosition = aimTarget;
  57. aim.solver.Update(); // Update AimIK
  58. }
  59. // Positioning the left hand on the gun after aiming has finished
  60. private void FBBIK() {
  61. // Store the current rotation of the right hand
  62. rightHandRotation = ik.references.rightHand.rotation;
  63. // Offsetting hands, you might need that to support multiple weapons with the same aiming pose
  64. Vector3 rightHandOffset = ik.references.rightHand.rotation * gunHoldOffset;
  65. ik.solver.rightHandEffector.positionOffset += rightHandOffset;
  66. if (recoil != null) recoil.SetHandRotations(rightHandRotation * leftHandRotRelToRightHand, rightHandRotation);
  67. // Update FBBIK
  68. ik.solver.Update();
  69. // Rotating the hand bones after IK has finished
  70. if (recoil != null) {
  71. ik.references.rightHand.rotation = recoil.rotationOffset * rightHandRotation;
  72. ik.references.leftHand.rotation = recoil.rotationOffset * rightHandRotation * leftHandRotRelToRightHand;
  73. } else {
  74. ik.references.rightHand.rotation = rightHandRotation;
  75. ik.references.leftHand.rotation = rightHandRotation * leftHandRotRelToRightHand;
  76. }
  77. }
  78. // Final calculations before FBBIK solves. Recoil has already solved by, so we can use it's calculated offsets.
  79. // Here we set the left hand position relative to the position and rotation of the right hand.
  80. private void OnPreRead() {
  81. Quaternion r = recoil != null? recoil.rotationOffset * rightHandRotation: rightHandRotation;
  82. Vector3 leftHandTarget = ik.references.rightHand.position + ik.solver.rightHandEffector.positionOffset + r * leftHandPosRelToRightHand;
  83. ik.solver.leftHandEffector.positionOffset += leftHandTarget - ik.references.leftHand.position - ik.solver.leftHandEffector.positionOffset + r * leftHandOffset;
  84. }
  85. // Rotating the head to look at the target
  86. private void HeadLookAt(Vector3 lookAtTarget) {
  87. Quaternion headRotationTarget = Quaternion.FromToRotation(ik.references.head.rotation * headLookAxis, lookAtTarget - ik.references.head.position);
  88. ik.references.head.rotation = Quaternion.Lerp(Quaternion.identity, headRotationTarget, headLookWeight) * ik.references.head.rotation;
  89. }
  90. // Cleaning up the delegates
  91. void OnDestroy() {
  92. if (ik != null) ik.solver.OnPreRead -= OnPreRead;
  93. }
  94. }
  95. }