ShoulderRotator.cs 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. using UnityEngine;
  2. using System.Collections;
  3. using RootMotion.FinalIK;
  4. namespace RootMotion.FinalIK {
  5. /// <summary>
  6. /// Shoulder rotator is a workaround for FBBIK not rotating the shoulder bones when pulled by hands.
  7. /// It get's the job done if you need it, but will take 2 solving iterations.
  8. /// </summary>
  9. public class ShoulderRotator : MonoBehaviour {
  10. [Tooltip("Weight of shoulder rotation")]
  11. public float weight = 1.5f;
  12. [Tooltip("The greater the offset, the sooner the shoulder will start rotating")]
  13. public float offset = 0.2f;
  14. private FullBodyBipedIK ik;
  15. private bool skip;
  16. void Start() {
  17. ik = GetComponent<FullBodyBipedIK>();
  18. // You can use just LateUpdate, but note that it doesn't work when you have animatePhysics turned on for the character.
  19. ik.solver.OnPostUpdate += RotateShoulders;
  20. }
  21. private void RotateShoulders () {
  22. if (ik == null) return;
  23. if (ik.solver.IKPositionWeight <= 0f) return;
  24. // Skipping the second update cycle
  25. if (skip) {
  26. skip = false;
  27. return;
  28. }
  29. RotateShoulder(FullBodyBipedChain.LeftArm, weight, offset); // Rotate the left shoulder
  30. RotateShoulder(FullBodyBipedChain.RightArm, weight, offset); // Rotate the right shoulder
  31. skip = true;
  32. ik.solver.Update(); // Update FBBIK again with the rotated shoulders
  33. }
  34. // Rotates a shoulder of a FBBIK character
  35. private void RotateShoulder(FullBodyBipedChain chain, float weight, float offset) {
  36. // Get FromToRotation from the current swing direction of the shoulder to the IK target direction
  37. Quaternion fromTo = Quaternion.FromToRotation(GetParentBoneMap(chain).swingDirection, ik.solver.GetEndEffector(chain).position - GetParentBoneMap(chain).transform.position);
  38. // Direction to the IK target
  39. Vector3 toTarget = ik.solver.GetEndEffector(chain).position - ik.solver.GetLimbMapping(chain).bone1.position;
  40. // Length of the limb
  41. float limbLength = ik.solver.GetChain(chain).nodes[0].length + ik.solver.GetChain(chain).nodes[1].length;
  42. // Divide IK Target direction magnitude by limb length to know how much the limb is being pulled
  43. float delta = (toTarget.magnitude / limbLength) - 1f + offset;
  44. delta = Mathf.Clamp(delta * weight, 0f, 1f);
  45. // Calculate the rotation offset for the shoulder
  46. Quaternion rotationOffset = Quaternion.Lerp(Quaternion.identity, fromTo, delta * ik.solver.GetEndEffector(chain).positionWeight * ik.solver.IKPositionWeight);
  47. // Rotate the shoulder
  48. ik.solver.GetLimbMapping(chain).parentBone.rotation = rotationOffset * ik.solver.GetLimbMapping(chain).parentBone.rotation;
  49. }
  50. // Get the shoulder BoneMap
  51. private IKMapping.BoneMap GetParentBoneMap(FullBodyBipedChain chain) {
  52. return ik.solver.GetLimbMapping(chain).GetBoneMap(IKMappingLimb.BoneMapType.Parent);
  53. }
  54. // Remove the delegate when destroyed
  55. void OnDestroy() {
  56. if (ik != null) ik.solver.OnPostUpdate -= RotateShoulders;
  57. }
  58. }
  59. }