RVOObstacle.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. namespace Pathfinding.RVO {
  4. /// <summary>
  5. /// Base class for simple RVO colliders.
  6. ///
  7. /// This is a helper base class for RVO colliders. It provides automatic gizmos
  8. /// and helps with the winding order of the vertices as well as automatically updating the obstacle when moved.
  9. ///
  10. /// Extend this class to create custom RVO obstacles.
  11. ///
  12. /// See: writing-rvo-colliders (view in online documentation for working links)
  13. /// See: RVOSquareObstacle
  14. /// </summary>
  15. public abstract class RVOObstacle : VersionedMonoBehaviour {
  16. /// <summary>
  17. /// Mode of the obstacle.
  18. /// Determines winding order of the vertices
  19. /// </summary>
  20. public ObstacleVertexWinding obstacleMode;
  21. public RVOLayer layer = RVOLayer.DefaultObstacle;
  22. /// <summary>
  23. /// RVO Obstacle Modes.
  24. /// Determines winding order of obstacle vertices
  25. /// </summary>
  26. public enum ObstacleVertexWinding {
  27. /// <summary>Keeps agents from entering the obstacle</summary>
  28. KeepOut,
  29. /// <summary>Keeps agents inside the obstacle</summary>
  30. KeepIn,
  31. }
  32. /// <summary>Reference to simulator</summary>
  33. protected Pathfinding.RVO.Simulator sim;
  34. /// <summary>All obstacles added</summary>
  35. private List<ObstacleVertex> addedObstacles;
  36. /// <summary>Original vertices for the obstacles</summary>
  37. private List<Vector3[]> sourceObstacles;
  38. /// <summary>
  39. /// Create Obstacles.
  40. /// Override this and add logic for creating obstacles.
  41. /// You should not use the simulator's function calls directly.
  42. ///
  43. /// See: AddObstacle
  44. /// </summary>
  45. protected abstract void CreateObstacles();
  46. /// <summary>
  47. /// Enable executing in editor to draw gizmos.
  48. /// If enabled, the CreateObstacles function will be executed in the editor as well
  49. /// in order to draw gizmos.
  50. /// </summary>
  51. protected abstract bool ExecuteInEditor { get; }
  52. /// <summary>If enabled, all coordinates are handled as local.</summary>
  53. protected abstract bool LocalCoordinates { get; }
  54. /// <summary>
  55. /// Static or dynamic.
  56. /// This determines if the obstacle can be updated by e.g moving the transform
  57. /// around in the scene.
  58. /// </summary>
  59. protected abstract bool StaticObstacle { get; }
  60. protected abstract float Height { get; }
  61. /// <summary>
  62. /// Called in the editor.
  63. /// This function should return true if any variables which can change the shape or position of the obstacle
  64. /// has changed since the last call to this function. Take a look at the RVOSquareObstacle for an example.
  65. /// </summary>
  66. protected abstract bool AreGizmosDirty();
  67. /// <summary>Enabled if currently in OnDrawGizmos</summary>
  68. private bool gizmoDrawing = false;
  69. /// <summary>Vertices for gizmos</summary>
  70. private List<Vector3[]> gizmoVerts;
  71. /// <summary>
  72. /// Last obstacle mode.
  73. /// Used to check if the gizmos should be updated
  74. /// </summary>
  75. private ObstacleVertexWinding _obstacleMode;
  76. /// <summary>
  77. /// Last matrix the obstacle was updated with.
  78. /// Used to check if the obstacle should be updated
  79. /// </summary>
  80. private Matrix4x4 prevUpdateMatrix;
  81. /// <summary>Draws Gizmos</summary>
  82. public void OnDrawGizmos () {
  83. OnDrawGizmos(false);
  84. }
  85. /// <summary>Draws Gizmos</summary>
  86. public void OnDrawGizmosSelected () {
  87. OnDrawGizmos(true);
  88. }
  89. /// <summary>Draws Gizmos</summary>
  90. public void OnDrawGizmos (bool selected) {
  91. gizmoDrawing = true;
  92. Gizmos.color = new Color(0.615f, 1, 0.06f, selected ? 1.0f : 0.7f);
  93. var movementPlane = RVOSimulator.active != null ? RVOSimulator.active.movementPlane : MovementPlane.XZ;
  94. var up = movementPlane == MovementPlane.XZ ? Vector3.up : -Vector3.forward;
  95. if (gizmoVerts == null || AreGizmosDirty() || _obstacleMode != obstacleMode) {
  96. _obstacleMode = obstacleMode;
  97. if (gizmoVerts == null) gizmoVerts = new List<Vector3[]>();
  98. else gizmoVerts.Clear();
  99. CreateObstacles();
  100. }
  101. Matrix4x4 m = GetMatrix();
  102. for (int i = 0; i < gizmoVerts.Count; i++) {
  103. Vector3[] verts = gizmoVerts[i];
  104. for (int j = 0, q = verts.Length-1; j < verts.Length; q = j++) {
  105. Gizmos.DrawLine(m.MultiplyPoint3x4(verts[j]), m.MultiplyPoint3x4(verts[q]));
  106. }
  107. if (selected) {
  108. for (int j = 0, q = verts.Length-1; j < verts.Length; q = j++) {
  109. Vector3 a = m.MultiplyPoint3x4(verts[q]);
  110. Vector3 b = m.MultiplyPoint3x4(verts[j]);
  111. if (movementPlane != MovementPlane.XY) {
  112. Gizmos.DrawLine(a + up*Height, b + up*Height);
  113. Gizmos.DrawLine(a, a + up*Height);
  114. }
  115. Vector3 avg = (a + b) * 0.5f;
  116. Vector3 tang = (b - a).normalized;
  117. if (tang == Vector3.zero) continue;
  118. Vector3 normal = Vector3.Cross(up, tang);
  119. Gizmos.DrawLine(avg, avg+normal);
  120. Gizmos.DrawLine(avg+normal, avg+normal*0.5f+tang*0.5f);
  121. Gizmos.DrawLine(avg+normal, avg+normal*0.5f-tang*0.5f);
  122. }
  123. }
  124. }
  125. gizmoDrawing = false;
  126. }
  127. /// <summary>
  128. /// Get's the matrix to use for vertices.
  129. /// Can be overriden for custom matrices.
  130. /// Returns: transform.localToWorldMatrix if LocalCoordinates is true, otherwise Matrix4x4.identity
  131. /// </summary>
  132. protected virtual Matrix4x4 GetMatrix () {
  133. return LocalCoordinates ? transform.localToWorldMatrix : Matrix4x4.identity;
  134. }
  135. /// <summary>
  136. /// Disables the obstacle.
  137. /// Do not override this function
  138. /// </summary>
  139. public void OnDisable () {
  140. if (addedObstacles != null) {
  141. if (sim == null) throw new System.Exception("This should not happen! Make sure you are not overriding the OnEnable function");
  142. for (int i = 0; i < addedObstacles.Count; i++) {
  143. sim.RemoveObstacle(addedObstacles[i]);
  144. }
  145. }
  146. }
  147. /// <summary>
  148. /// Enabled the obstacle.
  149. /// Do not override this function
  150. /// </summary>
  151. public void OnEnable () {
  152. if (addedObstacles != null) {
  153. if (sim == null) throw new System.Exception("This should not happen! Make sure you are not overriding the OnDisable function");
  154. for (int i = 0; i < addedObstacles.Count; i++) {
  155. // Update height and layer
  156. var vertex = addedObstacles[i];
  157. var start = vertex;
  158. do {
  159. vertex.layer = layer;
  160. vertex = vertex.next;
  161. } while (vertex != start);
  162. sim.AddObstacle(addedObstacles[i]);
  163. }
  164. }
  165. }
  166. /// <summary>Creates obstacles</summary>
  167. public void Start () {
  168. addedObstacles = new List<ObstacleVertex>();
  169. sourceObstacles = new List<Vector3[]>();
  170. prevUpdateMatrix = GetMatrix();
  171. CreateObstacles();
  172. }
  173. /// <summary>
  174. /// Updates obstacle if required.
  175. /// Checks for if the obstacle should be updated (e.g if it has moved)
  176. /// </summary>
  177. public void Update () {
  178. Matrix4x4 m = GetMatrix();
  179. if (m != prevUpdateMatrix) {
  180. for (int i = 0; i < addedObstacles.Count; i++) {
  181. sim.UpdateObstacle(addedObstacles[i], sourceObstacles[i], m);
  182. }
  183. prevUpdateMatrix = m;
  184. }
  185. }
  186. /// <summary>
  187. /// Finds a simulator in the scene.
  188. ///
  189. /// Saves found simulator in <see cref="sim"/>.
  190. ///
  191. /// Throws: System.InvalidOperationException When no RVOSimulator could be found.
  192. /// </summary>
  193. protected void FindSimulator () {
  194. if (RVOSimulator.active == null) throw new System.InvalidOperationException("No RVOSimulator could be found in the scene. Please add one to any GameObject");
  195. sim = RVOSimulator.active.GetSimulator();
  196. }
  197. /// <summary>
  198. /// Adds an obstacle with the specified vertices.
  199. /// The vertices array might be changed by this function.
  200. /// </summary>
  201. protected void AddObstacle (Vector3[] vertices, float height) {
  202. if (vertices == null) throw new System.ArgumentNullException("Vertices Must Not Be Null");
  203. if (height < 0) throw new System.ArgumentOutOfRangeException("Height must be non-negative");
  204. if (vertices.Length < 2) throw new System.ArgumentException("An obstacle must have at least two vertices");
  205. if (sim == null) FindSimulator();
  206. if (gizmoDrawing) {
  207. var v = new Vector3[vertices.Length];
  208. WindCorrectly(vertices);
  209. System.Array.Copy(vertices, v, vertices.Length);
  210. gizmoVerts.Add(v);
  211. return;
  212. }
  213. if (vertices.Length == 2) {
  214. AddObstacleInternal(vertices, height);
  215. return;
  216. }
  217. WindCorrectly(vertices);
  218. AddObstacleInternal(vertices, height);
  219. }
  220. /// <summary>
  221. /// Adds an obstacle.
  222. /// Winding is assumed to be correct and very little error checking is done.
  223. /// </summary>
  224. private void AddObstacleInternal (Vector3[] vertices, float height) {
  225. addedObstacles.Add(sim.AddObstacle(vertices, height, GetMatrix(), layer));
  226. sourceObstacles.Add(vertices);
  227. }
  228. /// <summary>
  229. /// Winds the vertices correctly.
  230. /// Winding order is determined from <see cref="obstacleMode"/>.
  231. /// </summary>
  232. private void WindCorrectly (Vector3[] vertices) {
  233. int leftmost = 0;
  234. float leftmostX = float.PositiveInfinity;
  235. var matrix = GetMatrix();
  236. for (int i = 0; i < vertices.Length; i++) {
  237. var x = matrix.MultiplyPoint3x4(vertices[i]).x;
  238. if (x < leftmostX) {
  239. leftmost = i;
  240. leftmostX = x;
  241. }
  242. }
  243. var p1 = matrix.MultiplyPoint3x4(vertices[(leftmost-1 + vertices.Length) % vertices.Length]);
  244. var p2 = matrix.MultiplyPoint3x4(vertices[leftmost]);
  245. var p3 = matrix.MultiplyPoint3x4(vertices[(leftmost+1) % vertices.Length]);
  246. MovementPlane movementPlane;
  247. if (sim != null) movementPlane = sim.movementPlane;
  248. else if (RVOSimulator.active) movementPlane = RVOSimulator.active.movementPlane;
  249. else movementPlane = MovementPlane.XZ;
  250. if (movementPlane == MovementPlane.XY) {
  251. p1.z = p1.y;
  252. p2.z = p2.y;
  253. p3.z = p3.y;
  254. }
  255. if (VectorMath.IsClockwiseXZ(p1, p2, p3) != (obstacleMode == ObstacleVertexWinding.KeepIn)) {
  256. System.Array.Reverse(vertices);
  257. }
  258. }
  259. }
  260. }