MechSpiderLeg.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. using UnityEngine;
  2. using System.Collections;
  3. using RootMotion.FinalIK;
  4. namespace RootMotion.Demos {
  5. /// <summary>
  6. /// Leg of the Mech spider. Controls stepping and positioning the IK target
  7. /// </summary>
  8. public class MechSpiderLeg : MonoBehaviour {
  9. public MechSpider mechSpider; // Reference to the target
  10. public MechSpiderLeg unSync; // One of the other legs that we dont want to be completely in sync with, that is stepping at the same time
  11. public Vector3 offset; // Offset from the default position
  12. public float minDelay = 0.2f, maxOffset = 1.0f, stepSpeed = 5.0f, footHeight = 0.15f, velocityPrediction = 0.2f, raycastFocus = 0.1f; // Parameters for stepping
  13. public AnimationCurve yOffset;
  14. public Transform foot;
  15. public Vector3 footUpAxis;
  16. public float footRotationSpeed = 10f;
  17. public ParticleSystem sand; // FX for sand
  18. private IK ik;
  19. private float stepProgress = 1f, lastStepTime;
  20. private Vector3 defaultPosition;
  21. private RaycastHit hit = new RaycastHit();
  22. private Quaternion lastFootLocalRotation;
  23. private Vector3 smoothHitNormal = Vector3.up;
  24. private Vector3 lastStepPosition;
  25. // Is the leg stepping?
  26. public bool isStepping {
  27. get {
  28. return stepProgress < 1f;
  29. }
  30. }
  31. // Gets and sets the IK position for this leg
  32. public Vector3 position {
  33. get {
  34. return ik.GetIKSolver().GetIKPosition();
  35. }
  36. set {
  37. ik.GetIKSolver().SetIKPosition(value);
  38. }
  39. }
  40. void Awake()
  41. {
  42. // Find the ik component
  43. ik = GetComponent<IK>();
  44. if (foot != null)
  45. {
  46. if (footUpAxis == Vector3.zero) footUpAxis = Quaternion.Inverse(foot.rotation) * Vector3.up;
  47. lastFootLocalRotation = foot.localRotation;
  48. ik.GetIKSolver().OnPostUpdate += AfterIK;
  49. }
  50. }
  51. private void AfterIK()
  52. {
  53. if (foot == null) return;
  54. foot.localRotation = lastFootLocalRotation;
  55. smoothHitNormal = Vector3.Slerp(smoothHitNormal, hit.normal, Time.deltaTime * footRotationSpeed);
  56. Quaternion f = Quaternion.FromToRotation(foot.rotation * footUpAxis, smoothHitNormal);
  57. foot.rotation = f * foot.rotation;
  58. }
  59. void Start() {
  60. // Workaround for Unity Win Store/Phone serialization bug
  61. stepProgress = 1f;
  62. hit = new RaycastHit();
  63. var points = ik.GetIKSolver().GetPoints();
  64. position = points[points.Length - 1].transform.position;
  65. lastStepPosition = position;
  66. hit.point = position;
  67. // Store the default rest position of the leg
  68. defaultPosition = mechSpider.transform.InverseTransformPoint(position + offset * mechSpider.scale);
  69. StartCoroutine(Step(position, position));
  70. }
  71. // Find the relaxed grounded positon of the leg relative to the body in world space.
  72. private Vector3 GetStepTarget(out bool stepFound, float focus, float distance) {
  73. stepFound = false;
  74. // place hit.point to the default position relative to the body
  75. Vector3 stepTarget = mechSpider.transform.TransformPoint(defaultPosition);
  76. //stepTarget += (hit.point - position) * velocityPrediction;
  77. stepTarget += mechSpider.velocity * velocityPrediction;
  78. Vector3 up = mechSpider.transform.up;
  79. // Focus the ray directions towards the spider body
  80. Vector3 toBody = mechSpider.body.position - position;
  81. Vector3 axis = Vector3.Cross(up, toBody);
  82. up = Quaternion.AngleAxis(focus, axis) * up;
  83. // Raycast to ground the relaxed position
  84. if (Physics.Raycast(stepTarget + up * mechSpider.raycastHeight * mechSpider.scale, -up, out hit, mechSpider.raycastHeight * mechSpider.scale + distance, mechSpider.raycastLayers)) stepFound = true;
  85. //return hit.point + mechSpider.transform.up * footHeight * mechSpider.scale;
  86. return hit.point + hit.normal * footHeight * mechSpider.scale;
  87. }
  88. private void UpdatePosition(float distance)
  89. {
  90. Vector3 up = mechSpider.transform.up;
  91. if (Physics.Raycast(lastStepPosition + up * mechSpider.raycastHeight * mechSpider.scale, -up, out hit, mechSpider.raycastHeight * mechSpider.scale + distance, mechSpider.raycastLayers))
  92. {
  93. position = hit.point + hit.normal * footHeight * mechSpider.scale;
  94. }
  95. }
  96. void Update () {
  97. UpdatePosition(mechSpider.raycastDistance * mechSpider.scale);
  98. // if already stepping, do nothing
  99. if (isStepping) return;
  100. // Minimum delay before stepping again
  101. if (Time.time < lastStepTime + minDelay) return;
  102. // If the unSync leg is stepping, do nothing
  103. if (unSync != null) {
  104. if (unSync.isStepping) return;
  105. }
  106. // Find the ideal relaxed position for the leg relative to the body
  107. bool stepFound = false;
  108. Vector3 idealPosition = GetStepTarget(out stepFound, raycastFocus, mechSpider.raycastDistance * mechSpider.scale);
  109. if (!stepFound) idealPosition = GetStepTarget(out stepFound, -raycastFocus, mechSpider.raycastDistance * 3f * mechSpider.scale); // Try again with inverted focus
  110. if (!stepFound) return;
  111. // If distance to that ideal position is less than the threshold, do nothing
  112. if (Vector3.Distance(position, idealPosition) < maxOffset * mechSpider.scale * UnityEngine.Random.Range(0.9f, 1.2f)) return;
  113. // Need to step closer to the ideal position
  114. StopAllCoroutines();
  115. StartCoroutine(Step(position, idealPosition));
  116. }
  117. // Stepping co-routine
  118. private IEnumerator Step(Vector3 stepStartPosition, Vector3 targetPosition) {
  119. stepProgress = 0f;
  120. // Moving the IK position
  121. while (stepProgress < 1) {
  122. stepProgress += Time.deltaTime * stepSpeed;
  123. position = Vector3.Lerp(stepStartPosition, targetPosition, stepProgress);
  124. position += mechSpider.transform.up * yOffset.Evaluate(stepProgress) * mechSpider.scale;
  125. lastStepPosition = position;
  126. yield return null;
  127. }
  128. position = targetPosition;
  129. lastStepPosition = position;
  130. // Emit sand
  131. if (sand != null) {
  132. sand.transform.position = position - mechSpider.transform.up * footHeight * mechSpider.scale;
  133. sand.Emit(20);
  134. }
  135. lastStepTime = Time.time;
  136. }
  137. }
  138. }