LegacyRichAI.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using Pathfinding.RVO;
  5. namespace Pathfinding.Legacy {
  6. [RequireComponent(typeof(Seeker))]
  7. [AddComponentMenu("Pathfinding/Legacy/AI/Legacy RichAI (3D, for navmesh)")]
  8. /// <summary>
  9. /// Advanced AI for navmesh based graphs.
  10. ///
  11. /// Deprecated: Use the RichAI class instead. This class only exists for compatibility reasons.
  12. /// </summary>
  13. [HelpURL("http://arongranberg.com/astar/documentation/stable/class_pathfinding_1_1_legacy_1_1_legacy_rich_a_i.php")]
  14. public class LegacyRichAI : RichAI {
  15. /// <summary>
  16. /// Use a 3rd degree equation for calculating slowdown acceleration instead of a 2nd degree.
  17. /// A 3rd degree equation can also make sure that the velocity when reaching the target is roughly zero and therefore
  18. /// it will have a more direct stop. In contrast solving a 2nd degree equation which will just make sure the target is reached but
  19. /// will usually have a larger velocity when reaching the target and therefore look more "bouncy".
  20. /// </summary>
  21. public bool preciseSlowdown = true;
  22. public bool raycastingForGroundPlacement = false;
  23. /// <summary>
  24. /// Current velocity of the agent.
  25. /// Includes eventual velocity due to gravity
  26. /// </summary>
  27. new Vector3 velocity;
  28. Vector3 lastTargetPoint;
  29. Vector3 currentTargetDirection;
  30. protected override void Awake () {
  31. base.Awake();
  32. if (rvoController != null) {
  33. if (rvoController is LegacyRVOController) (rvoController as LegacyRVOController).enableRotation = false;
  34. else Debug.LogError("The LegacyRichAI component only works with the legacy RVOController, not the latest one. Please upgrade this component", this);
  35. }
  36. }
  37. /// <summary>Smooth delta time to avoid getting overly affected by e.g GC</summary>
  38. static float deltaTime;
  39. /// <summary>Update is called once per frame</summary>
  40. protected override void Update () {
  41. deltaTime = Mathf.Min(Time.smoothDeltaTime*2, Time.deltaTime);
  42. if (richPath != null) {
  43. //System.Diagnostics.Stopwatch w = new System.Diagnostics.Stopwatch();
  44. //w.Start();
  45. RichPathPart pt = richPath.GetCurrentPart();
  46. var fn = pt as RichFunnel;
  47. if (fn != null) {
  48. //Clear buffers for reuse
  49. Vector3 position = UpdateTarget(fn);
  50. //tr.position = ps;
  51. //Only get walls every 5th frame to save on performance
  52. if (Time.frameCount % 5 == 0 && wallForce > 0 && wallDist > 0) {
  53. wallBuffer.Clear();
  54. fn.FindWalls(wallBuffer, wallDist);
  55. }
  56. /*for (int i=0;i<wallBuffer.Count;i+=2) {
  57. * Debug.DrawLine (wallBuffer[i],wallBuffer[i+1],Color.magenta);
  58. * }*/
  59. //Pick next waypoint if current is reached
  60. int tgIndex = 0;
  61. /*if (buffer.Count > 1) {
  62. * if ((buffer[tgIndex]-tr.position).sqrMagnitude < pickNextWaypointDist*pickNextWaypointDist) {
  63. * tgIndex++;
  64. * }
  65. * }*/
  66. //Target point
  67. Vector3 tg = nextCorners[tgIndex];
  68. Vector3 dir = tg-position;
  69. dir.y = 0;
  70. bool passedTarget = Vector3.Dot(dir, currentTargetDirection) < 0;
  71. //Check if passed target in another way
  72. if (passedTarget && nextCorners.Count-tgIndex > 1) {
  73. tgIndex++;
  74. tg = nextCorners[tgIndex];
  75. }
  76. if (tg != lastTargetPoint) {
  77. currentTargetDirection = (tg - position);
  78. currentTargetDirection.y = 0;
  79. currentTargetDirection.Normalize();
  80. lastTargetPoint = tg;
  81. //Debug.DrawRay (tr.position, Vector3.down*2,Color.blue,0.2f);
  82. }
  83. //Direction to target
  84. dir = (tg-position);
  85. dir.y = 0;
  86. float magn = dir.magnitude;
  87. //Write out for other scripts to read
  88. distanceToSteeringTarget = magn;
  89. //Normalize
  90. dir = magn == 0 ? Vector3.zero : dir/magn;
  91. Vector3 normdir = dir;
  92. Vector3 force = Vector3.zero;
  93. if (wallForce > 0 && wallDist > 0) {
  94. float wLeft = 0;
  95. float wRight = 0;
  96. for (int i = 0; i < wallBuffer.Count; i += 2) {
  97. Vector3 closest = VectorMath.ClosestPointOnSegment(wallBuffer[i], wallBuffer[i+1], tr.position);
  98. float dist = (closest-position).sqrMagnitude;
  99. if (dist > wallDist*wallDist) continue;
  100. Vector3 tang = (wallBuffer[i+1]-wallBuffer[i]).normalized;
  101. //Using the fact that all walls are laid out clockwise (seeing from inside)
  102. //Then left and right (ish) can be figured out like this
  103. float dot = Vector3.Dot(dir, tang) * (1 - System.Math.Max(0, (2*(dist / (wallDist*wallDist))-1)));
  104. if (dot > 0) wRight = System.Math.Max(wRight, dot);
  105. else wLeft = System.Math.Max(wLeft, -dot);
  106. }
  107. Vector3 norm = Vector3.Cross(Vector3.up, dir);
  108. force = norm*(wRight-wLeft);
  109. //Debug.DrawRay (tr.position, force, Color.cyan);
  110. }
  111. //Is the endpoint of the path (part) the current target point
  112. bool endPointIsTarget = lastCorner && nextCorners.Count-tgIndex == 1;
  113. if (endPointIsTarget) {
  114. //Use 2nd or 3rd degree motion equation to figure out acceleration to reach target in "exact" [slowdownTime] seconds
  115. //Clamp to avoid divide by zero
  116. if (slowdownTime < 0.001f) {
  117. slowdownTime = 0.001f;
  118. }
  119. Vector3 diff = tg - position;
  120. diff.y = 0;
  121. if (preciseSlowdown) {
  122. //{ t = slowdownTime
  123. //{ diff = vt + at^2/2 + qt^3/6
  124. //{ 0 = at + qt^2/2
  125. //{ solve for a
  126. dir = (6*diff - 4*slowdownTime*velocity)/(slowdownTime*slowdownTime);
  127. } else {
  128. dir = 2*(diff - slowdownTime*velocity)/(slowdownTime*slowdownTime);
  129. }
  130. dir = Vector3.ClampMagnitude(dir, acceleration);
  131. force *= System.Math.Min(magn/0.5f, 1);
  132. if (magn < endReachedDistance) {
  133. //END REACHED
  134. NextPart();
  135. }
  136. } else {
  137. dir *= acceleration;
  138. }
  139. //Debug.DrawRay (tr.position+Vector3.up, dir*3, Color.blue);
  140. velocity += (dir + force*wallForce)*deltaTime;
  141. if (slowWhenNotFacingTarget) {
  142. float dot = (Vector3.Dot(normdir, tr.forward)+0.5f)*(1.0f/1.5f);
  143. //velocity = Vector3.ClampMagnitude (velocity, maxSpeed * Mathf.Max (dot, 0.2f) );
  144. float xzmagn = Mathf.Sqrt(velocity.x*velocity.x + velocity.z*velocity.z);
  145. float prevy = velocity.y;
  146. velocity.y = 0;
  147. float mg = Mathf.Min(xzmagn, maxSpeed * Mathf.Max(dot, 0.2f));
  148. velocity = Vector3.Lerp(tr.forward * mg, velocity.normalized * mg, Mathf.Clamp(endPointIsTarget ? (magn*2) : 0, 0.5f, 1.0f));
  149. velocity.y = prevy;
  150. } else {
  151. // Clamp magnitude on the XZ axes
  152. float xzmagn = Mathf.Sqrt(velocity.x*velocity.x + velocity.z*velocity.z);
  153. xzmagn = maxSpeed/xzmagn;
  154. if (xzmagn < 1) {
  155. velocity.x *= xzmagn;
  156. velocity.z *= xzmagn;
  157. //Vector3.ClampMagnitude (velocity, maxSpeed);
  158. }
  159. }
  160. //Debug.DrawLine (tr.position, tg, lastCorner ? Color.red : Color.green);
  161. if (endPointIsTarget) {
  162. Vector3 trotdir = Vector3.Lerp(velocity, currentTargetDirection, System.Math.Max(1 - magn*2, 0));
  163. RotateTowards(trotdir);
  164. } else {
  165. RotateTowards(velocity);
  166. }
  167. //Applied after rotation to enable proper checks on if velocity is zero
  168. velocity += deltaTime * gravity;
  169. if (rvoController != null && rvoController.enabled) {
  170. //Use RVOController
  171. tr.position = position;
  172. rvoController.Move(velocity);
  173. } else
  174. if (controller != null && controller.enabled) {
  175. //Use CharacterController
  176. tr.position = position;
  177. controller.Move(velocity * deltaTime);
  178. } else {
  179. //Use Transform
  180. float lasty = position.y;
  181. position += velocity*deltaTime;
  182. position = RaycastPosition(position, lasty);
  183. tr.position = position;
  184. }
  185. } else {
  186. if (rvoController != null && rvoController.enabled) {
  187. //Use RVOController
  188. rvoController.Move(Vector3.zero);
  189. }
  190. }
  191. if (pt is RichSpecial) {
  192. if (!traversingOffMeshLink) {
  193. StartCoroutine(TraverseSpecial(pt as RichSpecial));
  194. }
  195. }
  196. //w.Stop();
  197. //Debug.Log ((w.Elapsed.TotalMilliseconds*1000));
  198. } else {
  199. if (rvoController != null && rvoController.enabled) {
  200. //Use RVOController
  201. rvoController.Move(Vector3.zero);
  202. } else
  203. if (controller != null && controller.enabled) {
  204. } else {
  205. tr.position = RaycastPosition(tr.position, tr.position.y);
  206. }
  207. }
  208. UpdateVelocity();
  209. lastDeltaTime = Time.deltaTime;
  210. }
  211. new Vector3 RaycastPosition (Vector3 position, float lasty) {
  212. if (raycastingForGroundPlacement) {
  213. RaycastHit hit;
  214. float up = Mathf.Max(height*0.5f, lasty-position.y+height*0.5f);
  215. if (Physics.Raycast(position+Vector3.up*up, Vector3.down, out hit, up, groundMask)) {
  216. if (hit.distance < up) {
  217. //grounded
  218. position = hit.point;//.up * -(hit.distance-centerOffset);
  219. velocity.y = 0;
  220. }
  221. }
  222. }
  223. return position;
  224. }
  225. /// <summary>Rotates along the Y-axis the transform towards trotdir</summary>
  226. bool RotateTowards (Vector3 trotdir) {
  227. trotdir.y = 0;
  228. if (trotdir != Vector3.zero) {
  229. Quaternion rot = tr.rotation;
  230. Vector3 trot = Quaternion.LookRotation(trotdir).eulerAngles;
  231. Vector3 eul = rot.eulerAngles;
  232. eul.y = Mathf.MoveTowardsAngle(eul.y, trot.y, rotationSpeed*deltaTime);
  233. tr.rotation = Quaternion.Euler(eul);
  234. //Magic number, should expose as variable
  235. return Mathf.Abs(eul.y-trot.y) < 5f;
  236. }
  237. return false;
  238. }
  239. }
  240. }