public override void OnInspectorGUI (NavGraph target) {

			var graph = target as LayerGridGraph;

			base.OnInspectorGUI (target);

			if (graph.neighbours != NumNeighbours.Four) {
				Debug.Log ("Note: Only 4 neighbours per grid node is allowed in this graph type");
			}
		}
		public override void OnInspectorGUI (NavGraph target) {
			var graph = target as NavMeshGraph;

			graph.sourceMesh = ObjectField ("Source Mesh", graph.sourceMesh, typeof(Mesh), false) as Mesh;

			graph.offset = EditorGUILayout.Vector3Field ("Offset",graph.offset);

			graph.rotation = EditorGUILayout.Vector3Field ("Rotation",graph.rotation);

			graph.scale = EditorGUILayout.FloatField (new GUIContent ("Scale","Scale of the mesh"),graph.scale);
			graph.scale = (graph.scale < 0.01F && graph.scale > -0.01F) ? (graph.scale >= 0 ? 0.01F : -0.01F) : graph.scale;

			graph.accurateNearestNode = EditorGUILayout.Toggle (new GUIContent ("Accurate Nearest Node Queries","More accurate nearest node queries. See docs for more info"),graph.accurateNearestNode);
		}
		public override void OnInspectorGUI (NavGraph target) {
			var graph = target as PointGraph;

			graph.root = ObjectField (new GUIContent ("Root","All childs of this object will be used as nodes, if it is not set, a tag search will be used instead (see below)"),graph.root,typeof(Transform),true) as Transform;

			graph.recursive = EditorGUILayout.Toggle (new GUIContent ("Recursive","Should childs of the childs in the root GameObject be searched"),graph.recursive);
			graph.searchTag = EditorGUILayout.TagField (new GUIContent ("Tag","If root is not set, all objects with this tag will be used as nodes"),graph.searchTag);

			if (graph.root != null) {
				EditorGUILayout.HelpBox ("All childs "+(graph.recursive ? "and sub-childs ":"") +"of 'root' will be used as nodes\nSet root to null to use a tag search instead", MessageType.None);
			} else {
				EditorGUILayout.HelpBox ("All object with the tag '"+graph.searchTag+"' will be used as nodes"+(graph.searchTag == "Untagged" ? "\nNote: the tag 'Untagged' cannot be used" : ""), MessageType.None);
			}

			graph.maxDistance = EditorGUILayout.FloatField (new GUIContent ("Max Distance","The max distance in world space for a connection to be valid. A zero counts as infinity"),graph.maxDistance);

			graph.limits = EditorGUILayout.Vector3Field ("Max Distance (axis aligned)",graph.limits);

			graph.raycast = EditorGUILayout.Toggle (new GUIContent ("Raycast","Use raycasting to check if connections are valid between each pair of nodes"),graph.raycast);

			if ( graph.raycast ) {
				EditorGUI.indentLevel++;

				graph.use2DPhysics = EditorGUILayout.Toggle (new GUIContent ("Use 2D Physics", "If enabled, all raycasts will use the Unity 2D Physics API instead of the 3D one."), graph.use2DPhysics);
			 	graph.thickRaycast = EditorGUILayout.Toggle (new GUIContent ("Thick Raycast","A thick raycast checks along a thick line with radius instead of just along a line"),graph.thickRaycast);

				if ( graph.thickRaycast ) {
					EditorGUI.indentLevel++;
			 		graph.thickRaycastRadius = EditorGUILayout.FloatField (new GUIContent ("Raycast Radius","The radius in world units for the thick raycast"),graph.thickRaycastRadius);
			 		EditorGUI.indentLevel--;
				}

				graph.mask = EditorGUILayoutx.LayerMaskField ("Mask",graph.mask);
				EditorGUI.indentLevel--;
			}

			graph.optimizeForSparseGraph = EditorGUILayout.Toggle (new GUIContent ("Optimize For Sparse Graph","Check online documentation for more information."),graph.optimizeForSparseGraph);

			if ( graph.optimizeForSparseGraph ) {
				EditorGUI.indentLevel++;

				graph.optimizeFor2D = EditorGUILayout.Toggle (new GUIContent ("Optimize For XZ Plane","Check online documentation for more information."),graph.optimizeFor2D);

				EditorGUI.indentLevel--;
			}
		}
		public override void OnInspectorGUI (NavGraph target) {

			var graph = target as GridGraph;

			DrawFirstSection (graph);

			Separator ();

			DrawMiddleSection (graph);

			Separator ();

			DrawCollisionEditor (graph.collision);

			if ( graph.collision.use2D ) {
				if ( Mathf.Abs ( Vector3.Dot ( Vector3.forward, Quaternion.Euler (graph.rotation) * Vector3.up ) ) < 0.9f ) {
					EditorGUILayout.HelpBox ("When using 2D it is recommended to rotate the graph so that it aligns with the 2D plane.", MessageType.Warning );
				}
			}

			Separator ();

			DrawLastSection (graph);
		}
		public override void OnSceneGUI (NavGraph target) {

			Event e = Event.current;

			var graph = target as GridGraph;

			Matrix4x4 matrixPre = graph.matrix;

			graph.GenerateMatrix ();

			if (e.type == EventType.MouseDown) {
				isMouseDown = true;
			} else if (e.type == EventType.MouseUp) {
				isMouseDown = false;
			}

			if (!isMouseDown) {
				savedMatrix = graph.boundsMatrix;
			}

			Handles.matrix = savedMatrix;

			if ((graph.GetType() == typeof(GridGraph) && graph.nodes == null) || (graph.uniformWidthDepthGrid && graph.depth*graph.width != graph.nodes.Length) || graph.matrix != matrixPre) {
				//Rescan the graphs
				if (AutoScan ()) {
					GUI.changed = true;
				}
			}

			Matrix4x4 inversed = savedMatrix.inverse;

			Handles.color = AstarColor.BoundsHandles;

			Handles.DrawCapFunction cap = Handles.CylinderCap;

			Vector2 extents = graph.unclampedSize*0.5F;

			Vector3 center = inversed.MultiplyPoint3x4 (graph.center);


			if (Tools.current == Tool.Scale) {
				const float HandleScale = 0.1f;

				EditorGUI.BeginChangeCheck ();

				Vector3 p1 = Handles.Slider (center+new Vector3 (extents.x,0,0),	Vector3.right,		HandleScale*HandleUtility.GetHandleSize (center+new Vector3 (extents.x,0,0)),cap,0);
				Vector3 p2 = Handles.Slider (center+new Vector3 (0,0,extents.y),	Vector3.forward,	HandleScale*HandleUtility.GetHandleSize (center+new Vector3 (0,0,extents.y)),cap,0);

				Vector3 p4 = Handles.Slider (center+new Vector3 (-extents.x,0,0),	-Vector3.right,		HandleScale*HandleUtility.GetHandleSize (center+new Vector3 (-extents.x,0,0)),cap,0);
				Vector3 p5 = Handles.Slider (center+new Vector3 (0,0,-extents.y),	-Vector3.forward,	HandleScale*HandleUtility.GetHandleSize (center+new Vector3 (0,0,-extents.y)),cap,0);

				Vector3 p6 = Handles.Slider (center, Vector3.up, HandleScale*HandleUtility.GetHandleSize (center),cap,0);

				var r1 = new Vector3 (p1.x,p6.y,p2.z);
				var r2 = new Vector3 (p4.x,p6.y,p5.z);

				if (EditorGUI.EndChangeCheck ()) {
					graph.center = savedMatrix.MultiplyPoint3x4 ((r1+r2)/2F);

					Vector3 tmp = r1-r2;
					graph.unclampedSize = new Vector2(tmp.x,tmp.z);
				}

			} else if (Tools.current == Tool.Move) {

				if (Tools.pivotRotation == PivotRotation.Local) {
					EditorGUI.BeginChangeCheck ();
					center = Handles.PositionHandle (center,Quaternion.identity);

					if (EditorGUI.EndChangeCheck () && Tools.viewTool != ViewTool.Orbit) {
						graph.center = savedMatrix.MultiplyPoint3x4 (center);
					}
				} else {
					Handles.matrix = Matrix4x4.identity;

					EditorGUI.BeginChangeCheck ();
					center = Handles.PositionHandle (graph.center,Quaternion.identity);

					if (EditorGUI.EndChangeCheck () && Tools.viewTool != ViewTool.Orbit) {
						graph.center = center;
					}
				}
			} else if (Tools.current == Tool.Rotate) {
				//The rotation handle doesn't seem to be able to handle different matrixes of some reason
				Handles.matrix = Matrix4x4.identity;

				EditorGUI.BeginChangeCheck ();
				var rot = Handles.RotationHandle (Quaternion.Euler (graph.rotation),graph.center);

				if (EditorGUI.EndChangeCheck () && Tools.viewTool != ViewTool.Orbit) {
					graph.rotation = rot.eulerAngles;
				}
			}

			Handles.matrix = Matrix4x4.identity;

	#if ASTARDEBUG
			//Draws some info over the node closest to the mouse
			Ray ray = HandleUtility.GUIPointToWorldRay (Event.current.mousePosition);

			Vector3 p = ray.GetPoint (100);


			if (Event.current.shift) {

				GraphNode close = graph.GetNearest (p).node;

				if (close != null) {
					node1 = close;
				}

				if (node1 == null) {
					return;
				}

				Handles.SphereCap (0,(Vector3)node1.position,Quaternion.identity,graph.nodeSize*0.5F);


				//Node node = node1;

				GUI.color = Color.white;
				//Handles.Label((Vector3)node.position + Vector3.up*2,"G : "+node.+"\nH : "+node.h+"\nF : "+node.f+"\nPosition : "+node.position.ToString (),EditorStyles.whiteBoldLabel);
			}

	#endif

		}
Пример #6
0
        /** Draws the inspector for the given graph with the given graph editor */
        bool DrawGraph (NavGraph graph, GraphEditor graphEditor) {

            // Graph guid, just used to get a unique value
            string graphGUIDString = graph.guid.ToString();

            Color tmp1 = GUI.color;
            EditorGUILayoutx.FadeArea topFadeArea = guiLayoutx.BeginFadeArea (graph.open, "", graphGUIDString, graphBoxStyle);

            Color tmp2 = GUI.color;
            GUI.color = tmp1;

            GUILayout.BeginHorizontal ();

            // Make sure that the graph name is not null
            graph.name = graph.name ?? graphEditorTypes[graph.GetType ().Name].displayName;

            GUI.SetNextControlName (graphGUIDString);
            graph.name = GUILayout.TextField (graph.name, EditorGUILayoutx.defaultLabelStyle, GUILayout.ExpandWidth(false),GUILayout.ExpandHeight(false));

            // If the graph name text field is not focused and the graph name is empty, then fill it in
            if (graph.name == "" && Event.current.type == EventType.Repaint && GUI.GetNameOfFocusedControl() != graphGUIDString) {
                graph.name = graphEditorTypes[graph.GetType ().Name].displayName;
            }

            if (GUILayout.Button ("", EditorGUILayoutx.defaultLabelStyle)) {
                graph.open = !graph.open;
                if (!graph.open) {
                    graph.infoScreenOpen = false;
                }
                RepaintSceneView ();
                return true;
            }

            if (script.prioritizeGraphs) {
                if (GUILayout.Button (new GUIContent ("Up","Increase the graph priority"),GUILayout.Width (40))) {
                    int index = script.astarData.GetGraphIndex (graph);

                    // Find the previous non null graph
                    int next = index-1;
                    for (;next >= 0;next--) if (script.graphs[next] != null) break;

                    if (next >= 0) {
                        NavGraph tmp = script.graphs[next];
                        script.graphs[next] = graph;
                        script.graphs[index] = tmp;

                        GraphEditor tmpEditor = graphEditors[next];
                        graphEditors[next] = graphEditors[index];
                        graphEditors[index] = tmpEditor;
                    }
                    CheckGraphEditors ();
                    Repaint ();
                }
                if (GUILayout.Button (new GUIContent ("Down","Decrease the graph priority"),GUILayout.Width (40))) {
                    int index = script.astarData.GetGraphIndex (graph);

                    // Find the next non null graph
                    int next = index+1;
                    for (;next<script.graphs.Length;next++) if (script.graphs[next] != null) break;

                    if (next < script.graphs.Length) {
                        NavGraph tmp = script.graphs[next];
                        script.graphs[next] = graph;
                        script.graphs[index] = tmp;

                        GraphEditor tmpEditor = graphEditors[next];
                        graphEditors[next] = graphEditors[index];
                        graphEditors[index] = tmpEditor;
                    }
                    CheckGraphEditors ();
                    Repaint ();
                }
            }

            bool drawGizmos = GUILayout.Toggle (graph.drawGizmos, "Draw Gizmos", graphGizmoButtonStyle);
            if (drawGizmos != graph.drawGizmos) {
                graph.drawGizmos = drawGizmos;

                // Make sure that the scene view is repainted when gizmos are toggled on or off
                RepaintSceneView ();
            }

            if (GUILayout.Toggle (graph.infoScreenOpen,"Info",graphInfoButtonStyle)) {
                if (!graph.infoScreenOpen) {
                    graph.infoScreenOpen = true;
                    graph.open = true;
                }
            } else {
                graph.infoScreenOpen = false;
            }

            if (GUILayout.Button ("Delete",graphDeleteButtonStyle)) {
                RemoveGraph (graph);
                return true;
            }
            GUILayout.EndHorizontal ();

            if (topFadeArea.Show () ) {
                EditorGUILayoutx.FadeArea fadeArea = guiLayoutx.BeginFadeArea (graph.infoScreenOpen,"graph_info_"+graphGUIDString,0);
                if (fadeArea.Show ()) {

                    bool nodenull = false;
                    int total = 0;
                    int numWalkable = 0;

                    KeyValuePair<float,KeyValuePair<int,int>> pair;
                    graphNodeCounts = graphNodeCounts ?? new Dictionary<NavGraph, KeyValuePair<float, KeyValuePair<int, int>>>();

                    if ( !graphNodeCounts.TryGetValue ( graph, out pair ) || (Time.realtimeSinceStartup-pair.Key) > 2 ) {
                        GraphNodeDelegateCancelable counter = node => {
                                                                          if (node == null) {
                                                                              nodenull = true;
                                                                          } else {
                                                                              total++;
                                                                              if (node.Walkable) numWalkable++;
                                                                          }
                                                                          return true;
                        };
                        graph.GetNodes (counter);
                        pair = new KeyValuePair<float, KeyValuePair<int, int>> (Time.realtimeSinceStartup, new KeyValuePair<int,int>( total, numWalkable ) );
                        graphNodeCounts[graph] = pair;
                    }

                    total = pair.Value.Key;
                    numWalkable = pair.Value.Value;


                    EditorGUI.indentLevel++;

                    if (nodenull) {
                        //EditorGUILayout.HelpBox ("Some nodes in the graph are null. Please report this error.", MessageType.Info);
                        Debug.LogError ("Some nodes in the graph are null. Please report this error.");
                    }

                    EditorGUILayout.LabelField ("Nodes",total.ToString());
                    EditorGUILayout.LabelField ("Walkable",numWalkable.ToString ());
                    EditorGUILayout.LabelField ("Unwalkable",(total-numWalkable).ToString ());
                    if (total == 0) EditorGUILayout.HelpBox ("The number of nodes in the graph is zero. The graph might not be scanned",MessageType.Info);

                    EditorGUI.indentLevel--;
                }
                guiLayoutx.EndFadeArea ();

                GUI.color = tmp2;

                graphEditor.OnInspectorGUI (graph);
                graphEditor.OnBaseInspectorGUI (graph);
            }

            guiLayoutx.EndFadeArea ();

            return false;
        }
Пример #7
0
 void RemoveGraph (NavGraph graph) {
     guiLayoutx.RemoveID (graph.guid.ToString());
     script.astarData.RemoveGraph (graph);
     CheckGraphEditors ();
     GUI.changed = true;
     Repaint ();
 }
Пример #8
0
		public void SerializeGraphs (NavGraph[] _graphs) {
			if (graphs != null) throw new InvalidOperationException ("Cannot serialize graphs multiple times.");
			graphs = _graphs;
			
			if (zip == null) throw new NullReferenceException ("You must not call CloseSerialize before a call to this function");
			
			if (graphs == null) graphs = new NavGraph[0];
			
			for (int i=0;i<graphs.Length;i++) {
				//Ignore graph if null
				if (graphs[i] == null) continue;

				// Serialize the graph to a byte array
				byte[] bytes = Serialize(graphs[i]);
				
				AddChecksum (bytes);
				zip.AddEntry ("graph"+i+jsonExt,bytes);
			}
		}
Пример #9
0
		/** Removes the specified graph from the #graphs array and Destroys it in a safe manner.
		 * To avoid changing graph indices for the other graphs, the graph is simply nulled in the array instead
		 * of actually removing it from the array.
		 * The empty position will be reused if a new graph is added.
		 * 
		 * \returns True if the graph was sucessfully removed (i.e it did exist in the #graphs array). False otherwise.
		 * 
		 * 
		 * \version Changed in 3.2.5 to call SafeOnDestroy before removing
		 * and nulling it in the array instead of removing the element completely in the #graphs array.
		 * 
		 */
		public bool RemoveGraph (NavGraph graph) {

			// Make sure all graph updates and other callbacks are done
			active.FlushWorkItems (false, true);

			// Make sure the pathfinding threads are stopped
			active.BlockUntilPathQueueBlocked ();

			// //Safe OnDestroy is called since there is a risk that the pathfinding is searching through the graph right now,
			// //and if we don't wait until the search has completed we could end up with evil NullReferenceExceptions
			graph.OnDestroy ();

			int i = System.Array.IndexOf (graphs, graph);

			if (i == -1) {
				return false;
			}
			
			graphs[i] = null;
			
			UpdateShortcuts ();
			
			return true;
		}
Пример #10
0
		/** Draws common graph settings */
		public void OnBaseInspectorGUI (NavGraph target) {
			int penalty = EditorGUILayout.IntField (new GUIContent ("Initial Penalty","Initial Penalty for nodes in this graph. Set during Scan."),(int)target.initialPenalty);
			if (penalty < 0) penalty = 0;
			target.initialPenalty = (uint)penalty;
		}
Пример #11
0
		/** This performs a linear search through all polygons returning the closest one.
		  * This will fill the NNInfo with .node for the closest node not necessarily complying with the NNConstraint, and .constrainedNode with the closest node
		  * complying with the NNConstraint.
		  * \see GetNearestForce(Node[],Int3[],Vector3,NNConstraint,bool)
		  */
		public static NNInfo GetNearestForceBoth (NavGraph graph, INavmeshHolder navmesh, Vector3 position, NNConstraint constraint, bool accurateNearestNode) {
			var pos = (Int3)position;

			float minDist = -1;
			GraphNode minNode = null;

			float minConstDist = -1;
			GraphNode minConstNode = null;

			float maxDistSqr = constraint.constrainDistance ? AstarPath.active.maxNearestNodeDistanceSqr : float.PositiveInfinity;

			GraphNodeDelegateCancelable del = delegate (GraphNode _node) {
				var node = _node as TriangleMeshNode;

				if (accurateNearestNode) {

					Vector3 closest = node.ClosestPointOnNode (position);
					float dist = ((Vector3)pos-closest).sqrMagnitude;

					if (minNode == null || dist < minDist) {
						minDist = dist;
						minNode = node;
					}

					if (dist < maxDistSqr && constraint.Suitable (node)) {
						if (minConstNode == null || dist < minConstDist) {
							minConstDist = dist;
							minConstNode = node;
						}
					}

				} else {

					if (!node.ContainsPoint ((Int3)position)) {

						float dist = (node.position-pos).sqrMagnitude;
						if (minNode == null || dist < minDist) {
							minDist = dist;
							minNode = node;
						}

						if (dist < maxDistSqr && constraint.Suitable (node)) {
							if (minConstNode == null || dist < minConstDist) {
								minConstDist = dist;
								minConstNode = node;
							}
						}

					} else {
						int dist = AstarMath.Abs (node.position.y-pos.y);

						if (minNode == null || dist < minDist) {
							minDist = dist;
							minNode = node;
						}

						if (dist < maxDistSqr && constraint.Suitable (node)) {
							if (minConstNode == null || dist < minConstDist) {
								minConstDist = dist;
								minConstNode = node;
							}
						}
					}
				}
				return true;
			};

			graph.GetNodes (del);

			var nninfo = new NNInfo (minNode);

			//Find the point closest to the nearest triangle

			if (nninfo.node != null) {
				var node = nninfo.node as TriangleMeshNode;//minNode2 as MeshNode;

				Vector3 clP = node.ClosestPointOnNode (position);

				nninfo.clampedPosition = clP;
			}

			nninfo.constrainedNode = minConstNode;
			if (nninfo.constrainedNode != null) {
				var node = nninfo.constrainedNode as TriangleMeshNode;//minNode2 as MeshNode;

				Vector3 clP = node.ClosestPointOnNode (position);

				nninfo.constClampedPosition = clP;
			}

			return nninfo;
		}
Пример #12
0
		/** This performs a linear search through all polygons returning the closest one */
		public static NNInfo GetNearestForce (NavGraph graph, INavmeshHolder navmesh, Vector3 position, NNConstraint constraint, bool accurateNearestNode) {
			NNInfo nn = GetNearestForceBoth (graph, navmesh,position,constraint,accurateNearestNode);
			nn.node = nn.constrainedNode;
			nn.clampedPosition = nn.constClampedPosition;
			return nn;
		}
Пример #13
0
		/** Deserializes graph settings.
		 * \note Stored in files named "graph#.json" where # is the graph number.
		 */
		public NavGraph[] DeserializeGraphs () {
			// Allocate a list of graphs to be deserialized
			graphs = new NavGraph[meta.graphs];

			int nonNull = 0;

			for (int i=0;i<meta.graphs;i++) {
				// Get the graph type from the metadata we deserialized earlier
				var tp = meta.GetGraphType(i);
				
				// Graph was null when saving, ignore
				if (System.Type.Equals (tp, null)) continue;

				nonNull++;

				var entry = zip["graph"+i+jsonExt];
				
				if (entry == null)
					throw new FileNotFoundException ("Could not find data for graph "+i+" in zip. Entry 'graph+"+i+jsonExt+"' does not exist");

				// Create a new graph of the right type
				NavGraph graph = data.CreateGraph(tp);
				graph.graphIndex = (uint)(i + graphIndexOffset);

#if !ASTAR_NO_JSON
				var entryText = GetString(entry);
					
				var reader = new JsonReader(entryText,readerSettings);

				reader.PopulateObject (ref graph);
				
#else
				var mem = new MemoryStream ();
				entry.Extract(mem);
				mem.Position = 0;
				var reader = new BinaryReader (mem);
				var ctx = new GraphSerializationContext(reader, null, i + graphIndexOffset);
				graph.DeserializeSettings (ctx);
#endif

				graphs[i] = graph;
				if (graphs[i].guid.ToString () != meta.guids[i])
					throw new Exception ("Guid in graph file not equal to guid defined in meta file. Have you edited the data manually?\n"+graphs[i].guid+" != "+meta.guids[i]);
			}

			// Remove any null entries from the list
			var compressed = new NavGraph[nonNull];
			nonNull = 0;
			for ( int i=0;i<graphs.Length;i++) {
				if ( graphs[i] != null ) {
					compressed[nonNull] = graphs[i];
					nonNull++;
				}
			}

			graphs = compressed;

			return graphs;
		}
Пример #14
0
		/** Serializes the graph settings to JSON and returns the data */
		public byte[] Serialize (NavGraph graph) {
#if !ASTAR_NO_JSON
			// Grab a cached string builder to avoid allocations
			var output = GetStringBuilder ();
			var writer = new JsonWriter (output,writerSettings);
			writer.Write (graph);
			
			return encoding.GetBytes (output.ToString());
#else
			var mem = new System.IO.MemoryStream();
			var writer = new System.IO.BinaryWriter(mem);
			var ctx = new GraphSerializationContext (writer);
			graph.SerializeSettings (ctx);
			return mem.ToArray();
#endif
		}
Пример #15
0
        /** Returns whether or not the graph conforms to this NNConstraint's rules.
		  * Note that only the first 31 graphs are considered using this function.
		  * If the graphMask has bit 31 set (i.e the last graph possible to fit in the mask), all graphs
		  * above index 31 will also be considered suitable.
		  */
        public virtual bool SuitableGraph (int graphIndex, NavGraph graph) {
            return ((graphMask >> graphIndex) & 1) != 0;
        }
Пример #16
0
		/** Adds obstacles for a graph */
		public void AddGraphObstacles (Simulator sim, NavGraph graph) {
			if (obstacles.Count > 0 && lastSim != null && lastSim != sim) {
				Debug.LogError ("Simulator has changed but some old obstacles are still added for the previous simulator. Deleting previous obstacles.");
				RemoveObstacles ();
			}
			
			//Remember which simulator these obstacles were added to
			lastSim = sim;
			
			INavmesh ng = graph as INavmesh;
			
			if (ng == null) return;
			
			//Assume less than 20 vertices per node (actually assumes 3, but I will change that some day)
			int[] uses = new int[20];
			
			ng.GetNodes (delegate(GraphNode _node) {
				TriangleMeshNode node = _node as TriangleMeshNode;
				
				uses[0] = uses[1] = uses[2] = 0;
				
				if (node != null) {
					
					//Find out which edges are shared with other nodes
					for (int j=0;j<node.connections.Length;j++) {
						TriangleMeshNode other = node.connections[j] as TriangleMeshNode;
						
						// Not necessarily a TriangleMeshNode
						if (other != null) {
							int a = node.SharedEdge(other);
							if (a != -1) uses[a] = 1;
						}
					}
					
					//Loop through all edges on the node
					for (int j=0;j<3;j++) {
						//The edge is not shared with any other node
						//I.e it is an exterior edge on the mesh
						if (uses[j] == 0) {
							//The two vertices of the edge
							Vector3 v1 = (Vector3)node.GetVertex(j);
							Vector3 v2 = (Vector3)node.GetVertex((j+1) % node.GetVertexCount());
							
							//I think node vertices always should be clockwise, but it's good to be certain
							/*if (!Polygon.IsClockwise (v1,v2,(Vector3)node.GetVertex((j+2) % node.GetVertexCount()))) {
								Vector3 tmp = v2;
								v2 = v1;
								v1 = tmp;
							}*/
							
		#if ASTARDEBUG
							Debug.DrawLine (v1,v2,Color.red);
							Debug.DrawRay (v1,Vector3.up*wallHeight,Color.red);
		#endif
							
							//Find out the height of the wall/obstacle we are about to add
							float height = System.Math.Abs(v1.y-v2.y);
							height = System.Math.Max (height,5);
							
							//Enqueue the edge as a line obstacle
							obstacles.Add (sim.AddObstacle (v1, v2, wallHeight));
						}
					}
				}
				
				return true;
			});
			
		}
Пример #17
0
		/** Override to implement graph inspectors */
		public virtual void OnInspectorGUI (NavGraph target) {
		}
Пример #18
0
		/** Adds the specified graph to the #graphs array */
		public void AddGraph (NavGraph graph) {
			
			// Make sure to not interfere with pathfinding
			AstarPath.active.BlockUntilPathQueueBlocked();
			
			//Try to fill in an empty position
			for (int i=0;i<graphs.Length;i++) {
				if (graphs[i] == null) {
					graphs[i] = graph;
					graph.active = active;
					graph.Awake ();
					graph.graphIndex = (uint)i;
					UpdateShortcuts ();
					return;
				}
			}
			
			if (graphs != null && graphs.Length >= GraphNode.MaxGraphIndex) {
				throw new System.Exception("Graph Count Limit Reached. You cannot have more than " + GraphNode.MaxGraphIndex +
					" graphs. Some compiler directives can change this limit, e.g ASTAR_MORE_AREAS, look under the " +
					"'Optimizations' tab in the A* Inspector");
			}
			
			//Enqueue a new entry to the list
			var ls = new List<NavGraph> (graphs);
			ls.Add (graph);
			graphs = ls.ToArray ();
			
			UpdateShortcuts ();
			
			graph.active = active;
			graph.Awake ();
			graph.graphIndex = (uint)(graphs.Length-1);
		}
Пример #19
0
		/** Override to implement scene GUI drawing for the graph */
		public virtual void OnSceneGUI (NavGraph target) {
		}
Пример #20
0
		/** Gets the index of the NavGraph in the #graphs array */
		public int GetGraphIndex (NavGraph graph) {
			if (graph == null) throw new System.ArgumentNullException ("graph");
			
			if ( graphs != null ) {
				for (int i=0;i<graphs.Length;i++) {
					if (graph == graphs[i]) {
						return i;
					}
				}
			}
			Debug.LogError ("Graph doesn't exist");
			return -1;
		}
Пример #21
0
		public override void OnInspectorGUI (NavGraph target) {
			var graph = target as RecastGraph;

			bool preEnabled = GUI.enabled;

			System.Int64 estWidth = Mathf.RoundToInt (Mathf.Ceil (graph.forcedBoundsSize.x / graph.cellSize));
			System.Int64 estDepth = Mathf.RoundToInt (Mathf.Ceil (graph.forcedBoundsSize.z / graph.cellSize));

			// Show a warning if the number of voxels is too large
			if (estWidth*estDepth >= 1024*1024 || estDepth >= 1024*1024 || estWidth >= 1024*1024) {
				GUIStyle helpBox = GUI.skin.FindStyle ("HelpBox") ?? GUI.skin.FindStyle ("Box");

				Color preColor = GUI.color;
				if (estWidth*estDepth >= 2048*2048 || estDepth >= 2048*2048 || estWidth >= 2048*2048) {
					GUI.color = Color.red;
				} else {
					GUI.color = Color.yellow;
				}

				GUILayout.Label ("Warning : Might take some time to calculate",helpBox);
				GUI.color = preColor;
			}

			GUI.enabled = false;
			EditorGUILayout.LabelField ("Width (voxels)",estWidth.ToString ());

			EditorGUILayout.LabelField ("Depth (voxels)",estDepth.ToString ());
			GUI.enabled = preEnabled;

			graph.cellSize = EditorGUILayout.FloatField (new GUIContent ("Cell Size","Size of one voxel in world units"),graph.cellSize);
			if (graph.cellSize < 0.001F) graph.cellSize = 0.001F;

			graph.cellHeight = EditorGUILayout.FloatField (new GUIContent ("Cell Height","Height of one voxel in world units"),graph.cellHeight);
			if (graph.cellHeight < 0.001F) graph.cellHeight = 0.001F;

			graph.useTiles = (UseTiles)EditorGUILayout.EnumPopup ("Use Tiles", graph.useTiles?UseTiles.UseTiles:UseTiles.DontUseTiles) == UseTiles.UseTiles;

			if (graph.useTiles) {
				EditorGUI.indentLevel++;
				graph.editorTileSize = EditorGUILayout.IntField (new GUIContent ("Tile Size", "Size in voxels of a single tile.\n" +
				 "This is the width of the tile.\n" +
				 "\n" +
				 "A large tile size can be faster to initially scan (but beware of out of memory issues if you try with a too large tile size in a large world)\n" +
				 "smaller tile sizes are (much) faster to update.\n" +
				 "\n" +
				 "Different tile sizes can affect the quality of paths. It is often good to split up huge open areas into several tiles for\n" +
				 "better quality paths, but too small tiles can lead to effects looking like invisible obstacles."), graph.editorTileSize);
				EditorGUI.indentLevel--;
			}

			graph.minRegionSize = EditorGUILayout.FloatField (new GUIContent ("Min Region Size", "Small regions will be removed. In square world units"), graph.minRegionSize);

			graph.walkableHeight = EditorGUILayout.FloatField (new GUIContent ("Walkable Height","Minimum distance to the roof for an area to be walkable"),graph.walkableHeight);
			graph.walkableHeight = Mathf.Max (graph.walkableHeight, 0);

			graph.walkableClimb = EditorGUILayout.FloatField (new GUIContent ("Walkable Climb","How high can the character climb"),graph.walkableClimb);

			// A walkableClimb higher than this can cause issues when generating the navmesh since then it can in some cases
			// Both be valid for a character to walk under an obstacle and climb up on top of it (and that cannot be handled with a navmesh without links)
			if (graph.walkableClimb >= graph.walkableHeight) {
				graph.walkableClimb = graph.walkableHeight;
				EditorGUILayout.HelpBox ("Walkable climb should be less than walkable height. Clamping to " + graph.walkableHeight+".",MessageType.Warning);
			} else if (graph.walkableClimb < 0) {
				graph.walkableClimb = 0;
			}

			graph.characterRadius = EditorGUILayout.FloatField (new GUIContent ("Character Radius","Radius of the character. It's good to add some margin.\nIn world units."),graph.characterRadius);
			graph.characterRadius = Mathf.Max (graph.characterRadius, 0);

			graph.maxSlope = EditorGUILayout.Slider (new GUIContent ("Max Slope","Approximate maximum slope"),graph.maxSlope,0F,90F);
			graph.maxEdgeLength = EditorGUILayout.FloatField (new GUIContent ("Max Edge Length","Maximum length of one edge in the completed navmesh before it is split. A lower value can often yield better quality graphs"),graph.maxEdgeLength);
			graph.maxEdgeLength = graph.maxEdgeLength < graph.cellSize ? graph.cellSize : graph.maxEdgeLength;

			graph.contourMaxError = EditorGUILayout.FloatField (new GUIContent ("Max Edge Error","Amount of simplification to apply to edges.\nIn world units."),graph.contourMaxError);

			graph.rasterizeTerrain = EditorGUILayout.Toggle (new GUIContent ("Rasterize Terrain","Should a rasterized terrain be included"), graph.rasterizeTerrain);
			if (graph.rasterizeTerrain) {
				EditorGUI.indentLevel++;
				graph.rasterizeTrees = EditorGUILayout.Toggle (new GUIContent ("Rasterize Trees", "Rasterize tree colliders on terrains. " +
					"If the tree prefab has a collider, that collider will be rasterized. " +
					"Otherwise a simple box collider will be used and the script will " +
					"try to adjust it to the tree's scale, it might not do a very good job though so " +
					"an attached collider is preferable."), graph.rasterizeTrees);
				if (graph.rasterizeTrees) {
					EditorGUI.indentLevel++;
					graph.colliderRasterizeDetail = EditorGUILayout.FloatField (new GUIContent ("Collider Detail", "Controls the detail of the generated collider meshes. "+
						"Increasing does not necessarily yield better navmeshes, but lowering will speed up scan.\n"+
						"Spheres and capsule colliders will be converted to meshes in order to be able to rasterize them, a higher value will increase the number of triangles in those meshes."), graph.colliderRasterizeDetail);
					EditorGUI.indentLevel--;
				}

				graph.terrainSampleSize = EditorGUILayout.IntField (new GUIContent ("Terrain Sample Size","Size of terrain samples. A lower value is better, but slower"), graph.terrainSampleSize);
				graph.terrainSampleSize = graph.terrainSampleSize < 1 ? 1 : graph.terrainSampleSize;//Clamp to at least 1
				EditorGUI.indentLevel--;
			}

			graph.rasterizeMeshes = EditorGUILayout.Toggle (new GUIContent ("Rasterize Meshes", "Should meshes be rasterized and used for building the navmesh"), graph.rasterizeMeshes);
			graph.rasterizeColliders = EditorGUILayout.Toggle (new GUIContent ("Rasterize Colliders", "Should colliders be rasterized and used for building the navmesh"), graph.rasterizeColliders);
			if (graph.rasterizeColliders) {
				EditorGUI.indentLevel++;
				graph.colliderRasterizeDetail = EditorGUILayout.FloatField (new GUIContent ("Collider Detail", "Controls the detail of the generated collider meshes. "+
						"Increasing does not necessarily yield better navmeshes, but lowering will speed up scan.\n"+
						"Spheres and capsule colliders will be converted to meshes in order to be able to rasterize them, a higher value will increase the number of triangles in those meshes."), graph.colliderRasterizeDetail);
				EditorGUI.indentLevel--;
			}

			Separator ();

			graph.forcedBoundsCenter = EditorGUILayout.Vector3Field ("Center",graph.forcedBoundsCenter);
			graph.forcedBoundsSize = EditorGUILayout.Vector3Field ("Size",graph.forcedBoundsSize);

			if (GUILayout.Button (new GUIContent ("Snap bounds to scene","Will snap the bounds of the graph to exactly contain all meshes that the bounds currently touches"))) {
				graph.SnapForceBoundsToScene ();
				GUI.changed = true;
			}

			Separator ();

			EditorGUILayout.HelpBox ("Objects contained in any of these masks will be rasterized", MessageType.None);
			graph.mask = EditorGUILayoutx.LayerMaskField ("Layer Mask",graph.mask);
			tagMaskFoldout = EditorGUILayoutx.UnityTagMaskList (new GUIContent("Tag Mask"), tagMaskFoldout, graph.tagMask);

			Separator ();

			graph.showMeshOutline = EditorGUILayout.Toggle (new GUIContent ("Show mesh outline","Toggles gizmos for drawing an outline of the mesh"),graph.showMeshOutline);
			graph.showNodeConnections = EditorGUILayout.Toggle (new GUIContent ("Show node connections","Toggles gizmos for drawing node connections"),graph.showNodeConnections);

			if (GUILayout.Button ("Export to .obj file")) {
				ExportToFile (graph);
			}


			Separator ();
			GUILayout.Label (new GUIContent ("Advanced"), EditorStyles.boldLabel);

			graph.relevantGraphSurfaceMode = (RecastGraph.RelevantGraphSurfaceMode)EditorGUILayout.EnumPopup (new GUIContent ("Relevant Graph Surface Mode",
				"Require every region to have a RelevantGraphSurface component inside it.\n" +
				"A RelevantGraphSurface component placed in the scene specifies that\n" +
				"the navmesh region it is inside should be included in the navmesh.\n\n" +
				"If this is set to OnlyForCompletelyInsideTile\n" +
				"a navmesh region is included in the navmesh if it\n" +
				"has a RelevantGraphSurface inside it, or if it\n" +
				"is adjacent to a tile border. This can leave some small regions\n" +
				"which you didn't want to have included because they are adjacent\n" +
				"to tile borders, but it removes the need to place a component\n" +
				"in every single tile, which can be tedious (see below).\n\n" +
				"If this is set to RequireForAll\n" +
				"a navmesh region is included only if it has a RelevantGraphSurface\n" +
				"inside it. Note that even though the navmesh\n" +
				"looks continous between tiles, the tiles are computed individually\n" +
				"and therefore you need a RelevantGraphSurface component for each\n" +
				"region and for each tile."),
				graph.relevantGraphSurfaceMode);

			graph.nearestSearchOnlyXZ = EditorGUILayout.Toggle (new GUIContent ("Nearest node queries in XZ space",
				"Recomended for single-layered environments.\nFaster but can be inacurate esp. in multilayered contexts."), graph.nearestSearchOnlyXZ);
		}