Grounding.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. using UnityEngine;
  2. using System.Collections;
  3. namespace RootMotion.FinalIK {
  4. /// <summary>
  5. /// Foot placement system.
  6. /// </summary>
  7. [System.Serializable]
  8. public partial class Grounding {
  9. #region Main Interface
  10. /// <summary>
  11. /// The raycasting quality. Fastest is a single raycast per foot, Simple is three raycasts, Best is one raycast and a capsule cast per foot.
  12. /// </summary>
  13. [System.Serializable]
  14. public enum Quality {
  15. Fastest,
  16. Simple,
  17. Best
  18. }
  19. /// <summary>
  20. /// Layers to ground the character to. Make sure to exclude the layer of the character controller.
  21. /// </summary>
  22. [Tooltip("Layers to ground the character to. Make sure to exclude the layer of the character controller.")]
  23. public LayerMask layers;
  24. /// <summary>
  25. /// Max step height. Maximum vertical distance of Grounding from the root of the character.
  26. /// </summary>
  27. [Tooltip("Max step height. Maximum vertical distance of Grounding from the root of the character.")]
  28. public float maxStep = 0.5f;
  29. /// <summary>
  30. /// The height offset of the root.
  31. /// </summary>
  32. [Tooltip("The height offset of the root.")]
  33. public float heightOffset;
  34. /// <summary>
  35. /// The speed of moving the feet up/down.
  36. /// </summary>
  37. [Tooltip("The speed of moving the feet up/down.")]
  38. public float footSpeed = 2.5f;
  39. /// <summary>
  40. /// CapsuleCast radius. Should match approximately with the size of the feet.
  41. /// </summary>
  42. [Tooltip("CapsuleCast radius. Should match approximately with the size of the feet.")]
  43. public float footRadius = 0.15f;
  44. /// <summary>
  45. /// Offset of the foot center along character forward axis.
  46. /// </summary>
  47. [Tooltip("Offset of the foot center along character forward axis.")]
  48. [HideInInspector] public float footCenterOffset; // TODO make visible in inspector if Grounder Visualization is finished.
  49. /// <summary>
  50. /// Amount of velocity based prediction of the foot positions.
  51. /// </summary>
  52. [Tooltip("Amount of velocity based prediction of the foot positions.")]
  53. public float prediction = 0.05f;
  54. /// <summary>
  55. /// Weight of rotating the feet to the ground normal offset.
  56. /// </summary>
  57. [Tooltip("Weight of rotating the feet to the ground normal offset.")]
  58. [Range(0f, 1f)]
  59. public float footRotationWeight = 1f;
  60. /// <summary>
  61. /// Speed of slerping the feet to their grounded rotations.
  62. /// </summary>
  63. [Tooltip("Speed of slerping the feet to their grounded rotations.")]
  64. public float footRotationSpeed = 7f;
  65. /// <summary>
  66. /// Max Foot Rotation Angle, Max angular offset from the foot's rotation (Reasonable range: 0-90 degrees).
  67. /// </summary>
  68. [Tooltip("Max Foot Rotation Angle. Max angular offset from the foot's rotation.")]
  69. [Range(0f, 90f)]
  70. public float maxFootRotationAngle = 45f;
  71. /// <summary>
  72. /// If true, solver will rotate with the character root so the character can be grounded for example to spherical planets.
  73. /// For performance reasons leave this off unless needed.
  74. /// </summary>
  75. [Tooltip("If true, solver will rotate with the character root so the character can be grounded for example to spherical planets. For performance reasons leave this off unless needed.")]
  76. public bool rotateSolver;
  77. /// <summary>
  78. /// The speed of moving the character up/down.
  79. /// </summary>
  80. [Tooltip("The speed of moving the character up/down.")]
  81. public float pelvisSpeed = 5f;
  82. /// <summary>
  83. /// Used for smoothing out vertical pelvis movement (range 0 - 1).
  84. /// </summary>
  85. [Tooltip("Used for smoothing out vertical pelvis movement (range 0 - 1).")]
  86. [Range(0f, 1f)]
  87. public float pelvisDamper;
  88. /// <summary>
  89. /// The weight of lowering the pelvis to the lowest foot.
  90. /// </summary>
  91. [Tooltip("The weight of lowering the pelvis to the lowest foot.")]
  92. public float lowerPelvisWeight = 1f;
  93. /// <summary>
  94. /// The weight of lifting the pelvis to the highest foot. This is useful when you don't want the feet to go too high relative to the body when crouching.
  95. /// </summary>
  96. [Tooltip("The weight of lifting the pelvis to the highest foot. This is useful when you don't want the feet to go too high relative to the body when crouching.")]
  97. public float liftPelvisWeight;
  98. /// <summary>
  99. /// The radius of the spherecast from the root that determines whether the character root is grounded.
  100. /// </summary>
  101. [Tooltip("The radius of the spherecast from the root that determines whether the character root is grounded.")]
  102. public float rootSphereCastRadius = 0.1f;
  103. /// <summary>
  104. /// If false, keeps the foot that is over a ledge at the root level. If true, lowers the overstepping foot and body by the 'Max Step' value.
  105. /// </summary>
  106. [Tooltip("If false, keeps the foot that is over a ledge at the root level. If true, lowers the overstepping foot and body by the 'Max Step' value.")]
  107. public bool overstepFallsDown = true;
  108. /// <summary>
  109. /// The raycasting quality. Fastest is a single raycast per foot, Simple is three raycasts, Best is one raycast and a capsule cast per foot.
  110. /// </summary>
  111. [Tooltip("The raycasting quality. Fastest is a single raycast per foot, Simple is three raycasts, Best is one raycast and a capsule cast per foot.")]
  112. public Quality quality = Quality.Best;
  113. /// <summary>
  114. /// The %Grounding legs.
  115. /// </summary>
  116. public Leg[] legs { get; private set; }
  117. /// <summary>
  118. /// The %Grounding pelvis.
  119. /// </summary>
  120. public Pelvis pelvis { get; private set; }
  121. /// <summary>
  122. /// Gets a value indicating whether any of the legs are grounded
  123. /// </summary>
  124. public bool isGrounded { get; private set; }
  125. /// <summary>
  126. /// The root Transform
  127. /// </summary>
  128. public Transform root { get; private set; }
  129. /// <summary>
  130. /// Ground height at the root position.
  131. /// </summary>
  132. public RaycastHit rootHit { get; private set; }
  133. /// <summary>
  134. /// Is the RaycastHit from the root grounded?
  135. /// </summary>
  136. public bool rootGrounded {
  137. get {
  138. return rootHit.distance < maxStep * 2f;
  139. }
  140. }
  141. /// <summary>
  142. /// Raycasts or sphereCasts to find the root ground point. Distance of the Ray/Sphere cast is maxDistanceMlp x maxStep. Use this instead of rootHit if the Grounder is weighed out/disabled and not updated.
  143. /// </summary>
  144. public RaycastHit GetRootHit(float maxDistanceMlp = 10f) {
  145. RaycastHit h = new RaycastHit();
  146. Vector3 _up = up;
  147. Vector3 legsCenter = Vector3.zero;
  148. foreach (Leg leg in legs) legsCenter += leg.transform.position;
  149. legsCenter /= (float)legs.Length;
  150. h.point = legsCenter - _up * maxStep * 10f;
  151. float distMlp = maxDistanceMlp + 1;
  152. h.distance = maxStep * distMlp;
  153. if (maxStep <= 0f) return h;
  154. if (quality != Quality.Best) Physics.Raycast(legsCenter + _up * maxStep, -_up, out h, maxStep * distMlp, layers, QueryTriggerInteraction.Ignore);
  155. else Physics.SphereCast(legsCenter + _up * maxStep, rootSphereCastRadius, -up, out h, maxStep * distMlp, layers, QueryTriggerInteraction.Ignore);
  156. return h;
  157. }
  158. /// <summary>
  159. /// Gets a value indicating whether this <see cref="Grounding"/> is valid.
  160. /// </summary>
  161. public bool IsValid(ref string errorMessage) {
  162. if (root == null) {
  163. errorMessage = "Root transform is null. Can't initiate Grounding.";
  164. return false;
  165. }
  166. if (legs == null) {
  167. errorMessage = "Grounding legs is null. Can't initiate Grounding.";
  168. return false;
  169. }
  170. if (pelvis == null) {
  171. errorMessage = "Grounding pelvis is null. Can't initiate Grounding.";
  172. return false;
  173. }
  174. if (legs.Length == 0) {
  175. errorMessage = "Grounding has 0 legs. Can't initiate Grounding.";
  176. return false;
  177. }
  178. return true;
  179. }
  180. /// <summary>
  181. /// Initiate the %Grounding as an integrated solver by providing the root Transform, leg solvers, pelvis Transform and spine solver.
  182. /// </summary>
  183. public void Initiate(Transform root, Transform[] feet) {
  184. this.root = root;
  185. initiated = false;
  186. rootHit = new RaycastHit();
  187. // Constructing Legs
  188. if (legs == null) legs = new Leg[feet.Length];
  189. if (legs.Length != feet.Length) legs = new Leg[feet.Length];
  190. for (int i = 0; i < feet.Length; i++) if (legs[i] == null) legs[i] = new Leg();
  191. // Constructing pelvis
  192. if (pelvis == null) pelvis = new Pelvis();
  193. string errorMessage = string.Empty;
  194. if (!IsValid(ref errorMessage)) {
  195. Warning.Log(errorMessage, root, false);
  196. return;
  197. }
  198. // Initiate solvers only if application is playing
  199. if (Application.isPlaying) {
  200. for (int i = 0; i < feet.Length; i++) legs[i].Initiate(this, feet[i]);
  201. pelvis.Initiate(this);
  202. initiated = true;
  203. }
  204. }
  205. /// <summary>
  206. /// Updates the Grounding.
  207. /// </summary>
  208. public void Update() {
  209. if (!initiated) return;
  210. if (layers == 0) LogWarning("Grounding layers are set to nothing. Please add a ground layer.");
  211. maxStep = Mathf.Clamp(maxStep, 0f, maxStep);
  212. footRadius = Mathf.Clamp(footRadius, 0.0001f, maxStep);
  213. pelvisDamper = Mathf.Clamp(pelvisDamper, 0f, 1f);
  214. rootSphereCastRadius = Mathf.Clamp(rootSphereCastRadius, 0.0001f, rootSphereCastRadius);
  215. maxFootRotationAngle = Mathf.Clamp(maxFootRotationAngle, 0f, 90f);
  216. prediction = Mathf.Clamp(prediction, 0f, prediction);
  217. footSpeed = Mathf.Clamp(footSpeed, 0f, footSpeed);
  218. // Root hit
  219. rootHit = GetRootHit();
  220. float lowestOffset = Mathf.NegativeInfinity;
  221. float highestOffset = Mathf.Infinity;
  222. isGrounded = false;
  223. // Process legs
  224. foreach (Leg leg in legs) {
  225. leg.Process();
  226. if (leg.IKOffset > lowestOffset) lowestOffset = leg.IKOffset;
  227. if (leg.IKOffset < highestOffset) highestOffset = leg.IKOffset;
  228. if (leg.isGrounded) isGrounded = true;
  229. }
  230. // Precess pelvis
  231. lowestOffset = Mathf.Max(lowestOffset, 0f);
  232. highestOffset = Mathf.Min(highestOffset, 0f);
  233. pelvis.Process(-lowestOffset * lowerPelvisWeight, -highestOffset * liftPelvisWeight, isGrounded);
  234. }
  235. // Calculate the normal of the plane defined by leg positions, so we know how to rotate the body
  236. public Vector3 GetLegsPlaneNormal() {
  237. if (!initiated) return Vector3.up;
  238. Vector3 _up = up;
  239. Vector3 normal = _up;
  240. // Go through all the legs, rotate the normal by it's offset
  241. for (int i = 0; i < legs.Length; i++) {
  242. // Direction from the root to the leg
  243. Vector3 legDirection = legs[i].IKPosition - root.position;
  244. // Find the tangent
  245. Vector3 legNormal = _up;
  246. Vector3 legTangent = legDirection;
  247. Vector3.OrthoNormalize(ref legNormal, ref legTangent);
  248. // Find the rotation offset from the tangent to the direction
  249. Quaternion fromTo = Quaternion.FromToRotation(legTangent, legDirection);
  250. // Rotate the normal
  251. normal = fromTo * normal;
  252. }
  253. return normal;
  254. }
  255. // Set everything to 0
  256. public void Reset() {
  257. if (!Application.isPlaying) return;
  258. pelvis.Reset();
  259. foreach (Leg leg in legs) leg.Reset();
  260. }
  261. #endregion Main Interface
  262. private bool initiated;
  263. // Logs the warning if no other warning has beed logged in this session.
  264. public void LogWarning(string message) {
  265. Warning.Log(message, root);
  266. }
  267. // The up vector in solver rotation space.
  268. public Vector3 up {
  269. get {
  270. return (useRootRotation? root.up: Vector3.up);
  271. }
  272. }
  273. // Gets the vertical offset between two vectors in solver rotation space
  274. public float GetVerticalOffset(Vector3 p1, Vector3 p2) {
  275. if (useRootRotation) {
  276. Vector3 v = Quaternion.Inverse(root.rotation) * (p1 - p2);
  277. return v.y;
  278. }
  279. return p1.y - p2.y;
  280. }
  281. // Flattens a vector to ground plane in solver rotation space
  282. public Vector3 Flatten(Vector3 v) {
  283. if (useRootRotation) {
  284. Vector3 tangent = v;
  285. Vector3 normal = root.up;
  286. Vector3.OrthoNormalize(ref normal, ref tangent);
  287. return Vector3.Project(v, tangent);
  288. }
  289. v.y = 0;
  290. return v;
  291. }
  292. // Determines whether to use root rotation as solver rotation
  293. private bool useRootRotation {
  294. get {
  295. if (!rotateSolver) return false;
  296. if (root.up == Vector3.up) return false;
  297. return true;
  298. }
  299. }
  300. public Vector3 GetFootCenterOffset() {
  301. return root.forward * footRadius + root.forward * footCenterOffset;
  302. }
  303. }
  304. }