RVOExampleAgent.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. using Pathfinding.RVO;
  4. namespace Pathfinding.Examples {
  5. /// <summary>
  6. /// Example movement script for using RVO.
  7. ///
  8. /// Primarily intended for the example scenes.
  9. /// You can use the AIPath or RichAI movement scripts in your own projects.
  10. ///
  11. /// See: <see cref="Pathfinding.AIPath"/>
  12. /// See: <see cref="Pathfinding.RichAI"/>
  13. /// See: <see cref="Pathfinding.RVO.RVOController"/>
  14. /// </summary>
  15. [RequireComponent(typeof(RVOController))]
  16. [RequireComponent(typeof(Seeker))]
  17. [HelpURL("http://arongranberg.com/astar/documentation/stable/class_pathfinding_1_1_examples_1_1_r_v_o_example_agent.php")]
  18. public class RVOExampleAgent : MonoBehaviour {
  19. public float repathRate = 1;
  20. private float nextRepath = 0;
  21. private Vector3 target;
  22. private bool canSearchAgain = true;
  23. private RVOController controller;
  24. public float maxSpeed = 10;
  25. Path path = null;
  26. List<Vector3> vectorPath;
  27. int wp;
  28. public float moveNextDist = 1;
  29. public float slowdownDistance = 1;
  30. public LayerMask groundMask;
  31. Seeker seeker;
  32. MeshRenderer[] rends;
  33. public void Awake () {
  34. seeker = GetComponent<Seeker>();
  35. controller = GetComponent<RVOController>();
  36. }
  37. /// <summary>Set the point to move to</summary>
  38. public void SetTarget (Vector3 target) {
  39. this.target = target;
  40. RecalculatePath();
  41. }
  42. /// <summary>Animate the change of color</summary>
  43. public void SetColor (Color color) {
  44. if (rends == null) rends = GetComponentsInChildren<MeshRenderer>();
  45. foreach (MeshRenderer rend in rends) {
  46. Color current = rend.material.GetColor("_TintColor");
  47. AnimationCurve curveR = AnimationCurve.Linear(0, current.r, 1, color.r);
  48. AnimationCurve curveG = AnimationCurve.Linear(0, current.g, 1, color.g);
  49. AnimationCurve curveB = AnimationCurve.Linear(0, current.b, 1, color.b);
  50. AnimationClip clip = new AnimationClip();
  51. #if !(UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_4_8)
  52. // Needed to make Unity5 happy
  53. clip.legacy = true;
  54. #endif
  55. clip.SetCurve("", typeof(Material), "_TintColor.r", curveR);
  56. clip.SetCurve("", typeof(Material), "_TintColor.g", curveG);
  57. clip.SetCurve("", typeof(Material), "_TintColor.b", curveB);
  58. Animation anim = rend.gameObject.GetComponent<Animation>();
  59. if (anim == null) {
  60. anim = rend.gameObject.AddComponent<Animation>();
  61. }
  62. clip.wrapMode = WrapMode.Once;
  63. anim.AddClip(clip, "ColorAnim");
  64. anim.Play("ColorAnim");
  65. }
  66. }
  67. public void RecalculatePath () {
  68. canSearchAgain = false;
  69. nextRepath = Time.time+repathRate*(Random.value+0.5f);
  70. seeker.StartPath(transform.position, target, OnPathComplete);
  71. }
  72. public void OnPathComplete (Path _p) {
  73. ABPath p = _p as ABPath;
  74. canSearchAgain = true;
  75. if (path != null) path.Release(this);
  76. path = p;
  77. p.Claim(this);
  78. if (p.error) {
  79. wp = 0;
  80. vectorPath = null;
  81. return;
  82. }
  83. Vector3 p1 = p.originalStartPoint;
  84. Vector3 p2 = transform.position;
  85. p1.y = p2.y;
  86. float d = (p2-p1).magnitude;
  87. wp = 0;
  88. vectorPath = p.vectorPath;
  89. Vector3 waypoint;
  90. if (moveNextDist > 0) {
  91. for (float t = 0; t <= d; t += moveNextDist*0.6f) {
  92. wp--;
  93. Vector3 pos = p1 + (p2-p1)*t;
  94. do {
  95. wp++;
  96. waypoint = vectorPath[wp];
  97. } while (controller.To2D(pos - waypoint).sqrMagnitude < moveNextDist*moveNextDist && wp != vectorPath.Count-1);
  98. }
  99. }
  100. }
  101. public void Update () {
  102. if (Time.time >= nextRepath && canSearchAgain) {
  103. RecalculatePath();
  104. }
  105. Vector3 pos = transform.position;
  106. if (vectorPath != null && vectorPath.Count != 0) {
  107. while ((controller.To2D(pos - vectorPath[wp]).sqrMagnitude < moveNextDist*moveNextDist && wp != vectorPath.Count-1) || wp == 0) {
  108. wp++;
  109. }
  110. // Current path segment goes from vectorPath[wp-1] to vectorPath[wp]
  111. // We want to find the point on that segment that is 'moveNextDist' from our current position.
  112. // This can be visualized as finding the intersection of a circle with radius 'moveNextDist'
  113. // centered at our current position with that segment.
  114. var p1 = vectorPath[wp-1];
  115. var p2 = vectorPath[wp];
  116. // Calculate the intersection with the circle. This involves some math.
  117. var t = VectorMath.LineCircleIntersectionFactor(controller.To2D(transform.position), controller.To2D(p1), controller.To2D(p2), moveNextDist);
  118. // Clamp to a point on the segment
  119. t = Mathf.Clamp01(t);
  120. Vector3 waypoint = Vector3.Lerp(p1, p2, t);
  121. // Calculate distance to the end of the path
  122. float remainingDistance = controller.To2D(waypoint - pos).magnitude + controller.To2D(waypoint - p2).magnitude;
  123. for (int i = wp; i < vectorPath.Count - 1; i++) remainingDistance += controller.To2D(vectorPath[i+1] - vectorPath[i]).magnitude;
  124. // Set the target to a point in the direction of the current waypoint at a distance
  125. // equal to the remaining distance along the path. Since the rvo agent assumes that
  126. // it should stop when it reaches the target point, this will produce good avoidance
  127. // behavior near the end of the path. When not close to the end point it will act just
  128. // as being commanded to move in a particular direction, not toward a particular point
  129. var rvoTarget = (waypoint - pos).normalized * remainingDistance + pos;
  130. // When within [slowdownDistance] units from the target, use a progressively lower speed
  131. var desiredSpeed = Mathf.Clamp01(remainingDistance / slowdownDistance) * maxSpeed;
  132. Debug.DrawLine(transform.position, waypoint, Color.red);
  133. controller.SetTarget(rvoTarget, desiredSpeed, maxSpeed);
  134. } else {
  135. // Stand still
  136. controller.SetTarget(pos, maxSpeed, maxSpeed);
  137. }
  138. // Get a processed movement delta from the rvo controller and move the character.
  139. // This is based on information from earlier frames.
  140. var movementDelta = controller.CalculateMovementDelta(Time.deltaTime);
  141. pos += movementDelta;
  142. // Rotate the character if the velocity is not extremely small
  143. if (Time.deltaTime > 0 && movementDelta.magnitude / Time.deltaTime > 0.01f) {
  144. var rot = transform.rotation;
  145. var targetRot = Quaternion.LookRotation(movementDelta, controller.To3D(Vector2.zero, 1));
  146. const float RotationSpeed = 5;
  147. if (controller.movementPlane == MovementPlane.XY) {
  148. targetRot = targetRot * Quaternion.Euler(-90, 180, 0);
  149. }
  150. transform.rotation = Quaternion.Slerp(rot, targetRot, Time.deltaTime * RotationSpeed);
  151. }
  152. if (controller.movementPlane == MovementPlane.XZ) {
  153. RaycastHit hit;
  154. if (Physics.Raycast(pos + Vector3.up, Vector3.down, out hit, 2, groundMask)) {
  155. pos.y = hit.point.y;
  156. }
  157. }
  158. transform.position = pos;
  159. }
  160. }
  161. }