protected override void DrawNeighbours(GridGraph graph) { graph.neighbours = NumNeighbours.Four; EditorGUI.BeginDisabledGroup(true); EditorGUILayout.EnumPopup (new GUIContent ("Connections","Only 4 connections per node is possible on layered grid graphs"),graph.neighbours); EditorGUI.EndDisabledGroup(); }
public void Reset() { foreach (var t in GameObject.FindGameObjectsWithTag("Tower")) { Destroy(t); } gg = AstarPath.active.astarData.gridGraph; BuildingPlane = new Plane(Vector3.up, gg.center); w = gg.width; h = gg.depth; towers = new Tower[w * h]; buildingLocations = new bool[w * h]; for (int i = 0; i < w * h; i++) { int ti = i % w; int tj = w - 1 - i / w; buildingLocations[ti + tj * w] = gg.nodes[i].Walkable; } }
public static void SetGridGraph (int graphIndex, GridGraph graph) { if (_gridGraphs.Length <= graphIndex) { var gg = new GridGraph[graphIndex+1]; for (int i = 0; i < _gridGraphs.Length; i++) gg[i] = _gridGraphs[i]; _gridGraphs = gg; } _gridGraphs[graphIndex] = graph; }
protected override void DrawMaxClimb (GridGraph graph) { var layerGridGraph = graph as LayerGridGraph; base.DrawMaxClimb(graph); layerGridGraph.maxClimb = Mathf.Clamp(layerGridGraph.maxClimb, 0, layerGridGraph.characterHeight); if (layerGridGraph.maxClimb == layerGridGraph.characterHeight) { EditorGUILayout.HelpBox("Max climb needs to be smaller or equal to character height", MessageType.Info); } }
private IEnumerator newMazeGame() { mainLight = GameObject.FindWithTag("MainLight"); menuControl.loadingState = "Generating Maze..."; MazeGenerator mazeGenerator = GameObject.FindWithTag("Maze").GetComponent <MazeGenerator>(); yield return(new WaitForEndOfFrame()); mazeGenerator.newMaze(5 * (((int)difficulty) + 1)); yield return(new WaitForEndOfFrame()); menuControl.loadingState = "Funerizing Maze..."; yield return(mazeGenerator.StartCoroutine("removeRandomWalls")); menuControl.loadingState = "Generating navmesh..."; Pathfinding.GridGraph graph = (Pathfinding.GridGraph)AstarPath.active.graphs[0]; int graphSize = 50 * ((((int)difficulty) + 1)); float pos = graphSize / 2 * graph.nodeSize; graph.width = graphSize; graph.depth = graphSize; graph.center = new Vector3(pos, 0, pos); graph.UpdateSizeFromWidthDepth(); AstarPath.active.Scan(); yield return(new WaitForEndOfFrame()); menuControl.loadingState = "Generating keys and exit..."; keysOnMap = (((int)difficulty) + 1) * 2; spawnControl.spawnPoints = new List <MazeNode>(mazeGenerator.spawnPoints); spawnControl.spawnKeys(keysOnMap); spawnControl.spawnDoor(); yield return(new WaitForEndOfFrame()); menuControl.loadingState = "Spawning player..."; player = spawnControl.spawnPlayer(); yield return(new WaitForEndOfFrame()); menuControl.loadingState = "Spawning enemy..."; enemies = new List <GameObject>(); enemiesChasing = new List <GameObject>(); spawnControl.spawnPoints = new List <MazeNode>(mazeGenerator.spawnPoints); for (int i = 0; i < ((int)difficulty); i++) { spawnEnemy(); } yield return(new WaitForEndOfFrame()); menuControl.loadingState = "Initialising game..."; audioControl.playAsset(ambience); StartCoroutine(updateParameterWithList(enemies, true)); loading = false; setGameOver(false); }
public void CutCorners(int Value) { Pathfinding.GridGraph gGraph = AstarPath.active.data.gridGraph; if (Value == 0) { gGraph.cutCorners = true; } else if (Value == 1) { gGraph.cutCorners = false; } }
protected override void DrawMiddleSection (GridGraph graph) { var layerGridGraph = graph as LayerGridGraph; DrawNeighbours(graph); layerGridGraph.characterHeight = EditorGUILayout.FloatField("Character Height", layerGridGraph.characterHeight); DrawMaxClimb(graph); DrawMaxSlope(graph); DrawErosion(graph); layerGridGraph.mergeSpanRange = EditorGUILayout.FloatField("Merge Span Range", layerGridGraph.mergeSpanRange); }
public void makepath() { FindAllStones(); FindAllTrees(); foreach (GameObject item in GameObject.FindGameObjectsWithTag("Mark")) { Destroy(item); } int msize = (int)fireplaceStats.maxDistance.Value; gGraph = AstarPath.active.data.gridGraph; gGraph.width = msize; gGraph.depth = msize; gGraph.UpdateSizeFromWidthDepth(); markLD.SetActive(true); markLU.SetActive(true); markRD.SetActive(true); markRU.SetActive(true); markLD.transform.position = transform.position + new Vector3(-msize / 4 + 0.2f, -msize / 4 + 1.5f); markLU.transform.position = transform.position + new Vector3(-msize / 4 + 0.2f, msize / 4 + 1); markRD.transform.position = transform.position + new Vector3(msize / 4 - 0.2f, -msize / 4 + 1.5f); markRU.transform.position = transform.position + new Vector3(msize / 4 - 0.2f, msize / 4 + 1); for (int i = -msize / 4; i < msize / 4; i += 10) { GameObject mark = GameObject.Instantiate(marker, transform.position + new Vector3(-msize / 4 + 0.2f, i), Quaternion.identity); mark.SetActive(true); mark.transform.parent = this.transform; } for (int i = -msize / 4; i < msize / 4; i += 10) { GameObject mark = GameObject.Instantiate(marker, transform.position + new Vector3(msize / 4 + 0.2f, i), Quaternion.identity); mark.SetActive(true); mark.transform.parent = this.transform; } for (int i = -msize / 4; i < msize / 4; i += 10) { GameObject mark = GameObject.Instantiate(marker, transform.position + new Vector3(i, msize / 4 + 0.2f), Quaternion.identity); mark.SetActive(true); mark.transform.parent = this.transform; } for (int i = -msize / 4; i < msize / 4; i += 10) { GameObject mark = GameObject.Instantiate(marker, transform.position + new Vector3(i, -msize / 4 + 0.2f), Quaternion.identity); mark.SetActive(true); mark.transform.parent = this.transform; } gGraph.center = transform.position; AstarPath.active.Scan(); }
void DrawFirstSection (GridGraph graph) { DrawWidthDepthFields (graph); newNodeSize = EditorGUILayout.FloatField (new GUIContent ("Node size","The size of a single node. The size is the side of the node square in world units"),graph.nodeSize); newNodeSize = newNodeSize <= 0.01F ? 0.01F : newNodeSize; float prevRatio = graph.aspectRatio; graph.aspectRatio = EditorGUILayout.FloatField (new GUIContent ("Aspect Ratio","Scaling of the nodes width/depth ratio. Good for isometric games"),graph.aspectRatio); DrawIsometricField(graph); if (graph.nodeSize != newNodeSize || prevRatio != graph.aspectRatio) { if (!locked) { graph.nodeSize = newNodeSize; Matrix4x4 oldMatrix = graph.matrix; graph.GenerateMatrix (); if (graph.matrix != oldMatrix) { //Rescann the graphs //AstarPath.active.AutoScan (); GUI.changed = true; } } else { int tmpWidth = graph.width; int tmpDepth = graph.depth; float delta = newNodeSize / graph.nodeSize; graph.nodeSize = newNodeSize; graph.unclampedSize = RoundVector3 (new Vector2 (tmpWidth*graph.nodeSize,tmpDepth*graph.nodeSize)); Vector3 newCenter = graph.matrix.MultiplyPoint3x4 (new Vector3 ((tmpWidth/2F)*delta,0,(tmpDepth/2F)*delta)); graph.center = RoundVector3 (newCenter); graph.GenerateMatrix (); //Make sure the width & depths stay the same graph.width = tmpWidth; graph.depth = tmpDepth; AutoScan (); } } DrawPositionField(graph); graph.rotation = EditorGUILayout.Vector3Field ("Rotation", graph.rotation); if (GUILayout.Button (new GUIContent ("Snap Size","Snap the size to exactly fit nodes"), GUILayout.MaxWidth (100), GUILayout.MaxHeight (16))) { SnapSizeToNodes (graph.width,graph.depth,graph); } }
public void Heuristic(int Value) { Pathfinding.GridGraph gGraph = AstarPath.active.data.gridGraph; if (Value == 0) { gGraph.active.heuristic = Pathfinding.Heuristic.Euclidean; } else if (Value == 1) { gGraph.active.heuristic = Pathfinding.Heuristic.DiagonalManhattan; } else if (Value == 2) { gGraph.active.heuristic = Pathfinding.Heuristic.Manhattan; } else if (Value == 3) { gGraph.active.heuristic = Pathfinding.Heuristic.None; } }
public static int SetGridGraph(GridGraph graph) { if (gridGraphs == null) { gridGraphs = new GridGraph[1]; } else { for (int i=0;i<gridGraphs.Length;i++) { if (gridGraphs[i] == graph) { return i; } } GridGraph[] tmp = new GridGraph[gridGraphs.Length+1]; for (int i=0;i<gridGraphs.Length;i++) { tmp[i] = gridGraphs[i]; } gridGraphs = tmp; } gridGraphs[gridGraphs.Length-1] = graph; return gridGraphs.Length-1; }
//private bool equationCorrect; /* void Start() * { * equation = dangerEquation.GetComponentInChildren<InputField>(); * dangerEquationInfo = dangerEquation.transform.Find("Info").GetComponent<Text>(); * saveDangerEqButton = dangerEquation.GetComponentInChildren<Button>(); * * nightAtack = GameObject.FindGameObjectWithTag("DayNightSystem").GetComponent<EnemiesNightAtack>(); * equation.text= nightAtack.difficultyEquation; * IsEquationCorrect(nightAtack.difficultyEquation); * * Pathfinding.GridGraph gGraph = AstarPath.active.data.gridGraph; * diameter.value = gGraph.collision.diameter; * nearestNode.value = gGraph.active.maxNearestNodeDistance; * heuristicScale.value = gGraph.active.heuristicScale; * * }*/ private void OnEnable() { AddWorldStats(); if (dangerEquation != null) { equation = dangerEquation.GetComponentInChildren <InputField>(); dangerEquationInfo = dangerEquation.transform.Find("Info").GetComponent <Text>(); saveDangerEqButton = dangerEquation.GetComponentInChildren <Button>(); nightAtack = GameObject.FindGameObjectWithTag("DayNightSystem").GetComponent <EnemiesNightAtack>(); dayNightSystem = GameObject.FindGameObjectWithTag("DayNightSystem").GetComponent <DayNightSystem>(); equation.text = nightAtack.difficultyEquation; IsEquationCorrect(nightAtack.difficultyEquation); daySlider.value = dayNightSystem.dayTime; nightSider.value = dayNightSystem.nightTime; Pathfinding.GridGraph gGraph = AstarPath.active.data.gridGraph; diameter.value = gGraph.collision.diameter; nearestNode.value = gGraph.active.maxNearestNodeDistance; heuristicScale.value = gGraph.active.heuristicScale; } }
public static void RemoveGridGraph(GridGraph graph) { if (gridGraphs == null) { return; } for (int i=0;i<gridGraphs.Length;i++) { if (gridGraphs[i] == graph) { if (gridGraphs.Length == 1) { gridGraphs = null; return; } for (int j=i+1;j<gridGraphs.Length;j++) { GridGraph gg = gridGraphs[j]; if (gg.nodes != null) { for (int n=0;n<gg.nodes.Length;n++) { if (gg.nodes[n] != null) ((GridNode)gg.nodes[n]).SetGridIndex (j-1); } } } GridGraph[] tmp = new GridGraph[gridGraphs.Length-1]; for (int j=0;j<i;j++) { tmp[j] = gridGraphs[j]; } for (int j=i+1;j<gridGraphs.Length;j++) { tmp[j-1] = gridGraphs[j]; } return; } } }
protected void DrawMaxSlope(GridGraph graph) { graph.maxSlope = EditorGUILayout.Slider(new GUIContent("Max Slope", "Sets the max slope in degrees for a point to be walkable. Only enabled if Height Testing is enabled."), graph.maxSlope, 0, 90F); }
public override Vector3 RandomPointOnSurface() { GridGraph gg = GridNode.GetGridGraph(GraphIndex); return((Vector3)position + new Vector3(Random.value - 0.5f, 0, Random.value - 0.5f) * gg.nodeSize); }
bool IsHexagonal(GridGraph graph) { return(Mathf.Approximately(graph.isometricAngle, standardIsometric) && graph.neighbours == NumNeighbours.Six && graph.uniformEdgeCosts); }
static bool Is2D(GridGraph graph) { return(Quaternion.Euler(graph.rotation) * Vector3.up == -Vector3.forward); }
//GraphUndo undoState; //byte[] savedBytes; public override void OnSceneGUI(NavGraph target) { Event e = Event.current; GridGraph 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 UNITY_3_3 if (Tools.current == 3) { #else if (Tools.current == Tool.Scale) { #endif Vector3 p1 = Handles.Slider(center + new Vector3(extents.x, 0, 0), Vector3.right, 0.1F * HandleUtility.GetHandleSize(center + new Vector3(extents.x, 0, 0)), cap, 0); Vector3 p2 = Handles.Slider(center + new Vector3(0, 0, extents.y), Vector3.forward, 0.1F * HandleUtility.GetHandleSize(center + new Vector3(0, 0, extents.y)), cap, 0); //Vector3 p3 = Handles.Slider (center+new Vector3 (0,extents.y,0), Vector3.up, 0.1F*HandleUtility.GetHandleSize (center+new Vector3 (0,extents.y,0)),cap,0); Vector3 p4 = Handles.Slider(center + new Vector3(-extents.x, 0, 0), -Vector3.right, 0.1F * HandleUtility.GetHandleSize(center + new Vector3(-extents.x, 0, 0)), cap, 0); Vector3 p5 = Handles.Slider(center + new Vector3(0, 0, -extents.y), -Vector3.forward, 0.1F * HandleUtility.GetHandleSize(center + new Vector3(0, 0, -extents.y)), cap, 0); Vector3 p6 = Handles.Slider(center, Vector3.up, 0.1F * HandleUtility.GetHandleSize(center), cap, 0); Vector3 r1 = new Vector3(p1.x, p6.y, p2.z); Vector3 r2 = new Vector3(p4.x, p6.y, p5.z); //Debug.Log (graph.boundsMatrix.MultiplyPoint3x4 (Vector3.zero)+" "+graph.boundsMatrix.MultiplyPoint3x4 (Vector3.one)); //if (Tools.viewTool != ViewTool.Orbit) { graph.center = savedMatrix.MultiplyPoint3x4((r1 + r2) / 2F); Vector3 tmp = r1 - r2; graph.unclampedSize = new Vector2(tmp.x, tmp.z); //} #if UNITY_3_3 } else if (Tools.current == 1) { #else } else if (Tools.current == Tool.Move) { #endif if (Tools.pivotRotation == PivotRotation.Local) { center = Handles.PositionHandle(center, Quaternion.identity); if (Tools.viewTool != ViewTool.Orbit) { graph.center = savedMatrix.MultiplyPoint3x4(center); } } else { Handles.matrix = Matrix4x4.identity; center = Handles.PositionHandle(graph.center, Quaternion.identity); if (Tools.viewTool != ViewTool.Orbit) { graph.center = center; } } #if UNITY_3_3 } else if (Tools.current == 2) { #else } else if (Tools.current == Tool.Rotate) { #endif //The rotation handle doesn't seem to be able to handle different matrixes of some reason Handles.matrix = Matrix4x4.identity; Quaternion rot = Handles.RotationHandle(Quaternion.Euler(graph.rotation), graph.center); if (Tools.viewTool != ViewTool.Orbit) { graph.rotation = rot.eulerAngles; } } //graph.size.x = Mathf.Max (graph.size.x,1); //graph.size.y = Mathf.Max (graph.size.y,1); //graph.size.z = Mathf.Max (graph.size.z,1); 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 }
public override void OnInspectorGUI(NavGraph target) { GridGraph graph = target as GridGraph; //GUILayout.BeginHorizontal (); //GUILayout.BeginVertical (); Rect lockRect; GUIStyle lockStyle = AstarPathEditor.astarSkin.FindStyle("GridSizeLock"); if (lockStyle == null) { lockStyle = new GUIStyle(); } #if !UNITY_LE_4_3 || true GUILayout.BeginHorizontal(); GUILayout.BeginVertical(); int newWidth = EditorGUILayout.IntField(new GUIContent("Width (nodes)", "Width of the graph in nodes"), graph.width); int newDepth = EditorGUILayout.IntField(new GUIContent("Depth (nodes)", "Depth (or height you might also call it) of the graph in nodes"), graph.depth); GUILayout.EndVertical(); lockRect = GUILayoutUtility.GetRect(lockStyle.fixedWidth, lockStyle.fixedHeight); // Add a small offset to make it better centred around the controls lockRect.y += 3; GUILayout.EndHorizontal(); // All the layouts mess up the margin to the next control, so add it manually GUILayout.Space(2); #elif UNITY_4 Rect tmpLockRect; int newWidth = IntField(new GUIContent("Width (nodes)", "Width of the graph in nodes"), graph.width, 100, 0, out lockRect, out sizeSelected1); int newDepth = IntField(new GUIContent("Depth (nodes)", "Depth (or height you might also call it) of the graph in nodes"), graph.depth, 100, 0, out tmpLockRect, out sizeSelected2); #else Rect tmpLockRect; int newWidth = IntField(new GUIContent("Width (nodes)", "Width of the graph in nodes"), graph.width, 50, 0, out lockRect, out sizeSelected1); int newDepth = IntField(new GUIContent("Depth (nodes)", "Depth (or height you might also call it) of the graph in nodes"), graph.depth, 50, 0, out tmpLockRect, out sizeSelected2); #endif lockRect.width = lockStyle.fixedWidth; lockRect.height = lockStyle.fixedHeight; lockRect.x += lockStyle.margin.left; lockRect.y += lockStyle.margin.top; locked = GUI.Toggle(lockRect, locked, new GUIContent("", "If the width and depth values are locked, changing the node size will scale the grid which keeping the number of nodes consistent instead of keeping the size the same and changing the number of nodes in the graph"), lockStyle); //GUILayout.EndHorizontal (); if (newWidth != graph.width || newDepth != graph.depth) { SnapSizeToNodes(newWidth, newDepth, graph); } GUI.SetNextControlName("NodeSize"); newNodeSize = EditorGUILayout.FloatField(new GUIContent("Node size", "The size of a single node. The size is the side of the node square in world units"), graph.nodeSize); newNodeSize = newNodeSize <= 0.01F ? 0.01F : newNodeSize; float prevRatio = graph.aspectRatio; graph.aspectRatio = EditorGUILayout.FloatField(new GUIContent("Aspect Ratio", "Scaling of the nodes width/depth ratio. Good for isometric games"), graph.aspectRatio); if (graph.nodeSize != newNodeSize || prevRatio != graph.aspectRatio) { if (!locked) { graph.nodeSize = newNodeSize; Matrix4x4 oldMatrix = graph.matrix; graph.GenerateMatrix(); if (graph.matrix != oldMatrix) { //Rescann the graphs //AstarPath.active.AutoScan (); GUI.changed = true; } } else { float delta = newNodeSize / graph.nodeSize; graph.nodeSize = newNodeSize; graph.unclampedSize = new Vector2(newWidth * graph.nodeSize, newDepth * graph.nodeSize); Vector3 newCenter = graph.matrix.MultiplyPoint3x4(new Vector3((newWidth / 2F) * delta, 0, (newDepth / 2F) * delta)); graph.center = newCenter; graph.GenerateMatrix(); //Make sure the width & depths stay the same graph.width = newWidth; graph.depth = newDepth; AutoScan(); } } Vector3 pivotPoint; Vector3 diff; #if UNITY_LE_4_3 EditorGUIUtility.LookLikeControls(); #endif #if !UNITY_4 EditorGUILayoutx.BeginIndent(); #else GUILayout.BeginHorizontal(); #endif switch (pivot) { case GridPivot.Center: graph.center = RoundVector3(graph.center); graph.center = EditorGUILayout.Vector3Field("Center", graph.center); break; case GridPivot.TopLeft: pivotPoint = graph.matrix.MultiplyPoint3x4(new Vector3(0, 0, graph.depth)); pivotPoint = RoundVector3(pivotPoint); diff = pivotPoint - graph.center; pivotPoint = EditorGUILayout.Vector3Field("Top-Left", pivotPoint); graph.center = pivotPoint - diff; break; case GridPivot.TopRight: pivotPoint = graph.matrix.MultiplyPoint3x4(new Vector3(graph.width, 0, graph.depth)); pivotPoint = RoundVector3(pivotPoint); diff = pivotPoint - graph.center; pivotPoint = EditorGUILayout.Vector3Field("Top-Right", pivotPoint); graph.center = pivotPoint - diff; break; case GridPivot.BottomLeft: pivotPoint = graph.matrix.MultiplyPoint3x4(new Vector3(0, 0, 0)); pivotPoint = RoundVector3(pivotPoint); diff = pivotPoint - graph.center; pivotPoint = EditorGUILayout.Vector3Field("Bottom-Left", pivotPoint); graph.center = pivotPoint - diff; break; case GridPivot.BottomRight: pivotPoint = graph.matrix.MultiplyPoint3x4(new Vector3(graph.width, 0, 0)); pivotPoint = RoundVector3(pivotPoint); diff = pivotPoint - graph.center; pivotPoint = EditorGUILayout.Vector3Field("Bottom-Right", pivotPoint); graph.center = pivotPoint - diff; break; } graph.GenerateMatrix(); pivot = PivotPointSelector(pivot); #if !UNITY_4 EditorGUILayoutx.EndIndent(); EditorGUILayoutx.BeginIndent(); #else GUILayout.EndHorizontal(); #endif graph.rotation = EditorGUILayout.Vector3Field("Rotation", graph.rotation); #if UNITY_LE_4_3 //Add some space to make the Rotation and postion fields be better aligned (instead of the pivot point selector) //GUILayout.Space (19+7); #endif //GUILayout.EndHorizontal (); #if !UNITY_4 EditorGUILayoutx.EndIndent(); #endif #if UNITY_LE_4_3 EditorGUIUtility.LookLikeInspector(); #endif if (GUILayout.Button(new GUIContent("Snap Size", "Snap the size to exactly fit nodes"), GUILayout.MaxWidth(100), GUILayout.MaxHeight(16))) { SnapSizeToNodes(newWidth, newDepth, graph); } Separator(); graph.cutCorners = EditorGUILayout.Toggle(new GUIContent("Cut Corners", "Enables or disables cutting corners. See docs for image example"), graph.cutCorners); graph.neighbours = (NumNeighbours)EditorGUILayout.EnumPopup(new GUIContent("Connections", "Sets how many connections a node should have to it's neighbour nodes."), graph.neighbours); //GUILayout.BeginHorizontal (); //EditorGUILayout.PrefixLabel ("Max Climb"); graph.maxClimb = EditorGUILayout.FloatField(new GUIContent("Max Climb", "How high, relative to the graph, should a climbable level be. A zero (0) indicates infinity"), graph.maxClimb); if (graph.maxClimb < 0) { graph.maxClimb = 0; } EditorGUI.indentLevel++; graph.maxClimbAxis = EditorGUILayout.IntPopup(new GUIContent("Climb Axis", "Determines which axis the above setting should test on"), graph.maxClimbAxis, new GUIContent[3] { new GUIContent("X"), new GUIContent("Y"), new GUIContent("Z") }, new int[3] { 0, 1, 2 }); EditorGUI.indentLevel--; if (graph.maxClimb > 0 && Mathf.Abs((Quaternion.Euler(graph.rotation) * new Vector3(graph.nodeSize, 0, graph.nodeSize))[graph.maxClimbAxis]) > graph.maxClimb) { EditorGUILayout.HelpBox("Nodes are spaced further apart than this in the grid. You might want to increase this value or change the axis", MessageType.Warning); } //GUILayout.EndHorizontal (); graph.maxSlope = EditorGUILayout.Slider(new GUIContent("Max Slope", "Sets the max slope in degrees for a point to be walkable. Only enabled if Height Testing is enabled."), graph.maxSlope, 0, 90F); graph.erodeIterations = EditorGUILayout.IntField(new GUIContent("Erosion iterations", "Sets how many times the graph should be eroded. This adds extra margin to objects. This will not work when using Graph Updates, so if you can, use the Diameter setting in collision settings instead"), graph.erodeIterations); graph.erodeIterations = graph.erodeIterations < 0 ? 0 : (graph.erodeIterations > 16 ? 16 : graph.erodeIterations); //Clamp iterations to [0,16] if (graph.erodeIterations > 0) { EditorGUI.indentLevel++; graph.erosionUseTags = EditorGUILayout.Toggle(new GUIContent("Erosion Uses Tags", "Instead of making nodes unwalkable, " + "nodes will have their tag set to a value corresponding to their erosion level, " + "which is a quite good measurement of their distance to the closest wall.\nSee online documentation for more info."), graph.erosionUseTags); if (graph.erosionUseTags) { EditorGUI.indentLevel++; graph.erosionFirstTag = EditorGUILayoutx.SingleTagField("First Tag", graph.erosionFirstTag); EditorGUI.indentLevel--; } EditorGUI.indentLevel--; } 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(); showExtra = EditorGUILayout.Foldout(showExtra, "Extra"); if (showExtra) { EditorGUI.indentLevel += 2; graph.penaltyAngle = ToggleGroup(new GUIContent("Angle Penalty", "Adds a penalty based on the slope of the node"), graph.penaltyAngle); //bool preGUI = GUI.enabled; //GUI.enabled = graph.penaltyAngle && GUI.enabled; if (graph.penaltyAngle) { EditorGUI.indentLevel++; graph.penaltyAngleFactor = EditorGUILayout.FloatField(new GUIContent("Factor", "Scale of the penalty. A negative value should not be used"), graph.penaltyAngleFactor); //GUI.enabled = preGUI; HelpBox("Applies penalty to nodes based on the angle of the hit surface during the Height Testing"); EditorGUI.indentLevel--; } graph.penaltyPosition = ToggleGroup("Position Penalty", graph.penaltyPosition); //EditorGUILayout.Toggle ("Position Penalty",graph.penaltyPosition); //preGUI = GUI.enabled; //GUI.enabled = graph.penaltyPosition && GUI.enabled; if (graph.penaltyPosition) { EditorGUI.indentLevel++; graph.penaltyPositionOffset = EditorGUILayout.FloatField("Offset", graph.penaltyPositionOffset); graph.penaltyPositionFactor = EditorGUILayout.FloatField("Factor", graph.penaltyPositionFactor); HelpBox("Applies penalty to nodes based on their Y coordinate\nSampled in Int3 space, i.e it is multiplied with Int3.Precision first (" + Int3.Precision + ")\n" + "Be very careful when using negative values since a negative penalty will underflow and instead get really high"); //GUI.enabled = preGUI; EditorGUI.indentLevel--; } if (textureVisible) { DrawTextureData(graph.textureData, graph); } EditorGUI.indentLevel -= 2; } }
protected virtual void DrawJPS (GridGraph graph) { graph.useJumpPointSearch = EditorGUILayout.Toggle(new GUIContent("Use Jump Point Search", "Jump Point Search can significantly speed up pathfinding. But only works on uniformly weighted graphs"), graph.useJumpPointSearch); if (graph.useJumpPointSearch) { EditorGUILayout.HelpBox("Jump Point Search assumes that there are no penalties applied to the graph. Tag penalties cannot be used either.", MessageType.Warning); #if !ASTAR_JPS EditorGUILayout.HelpBox("JPS needs to be enabled using a compiler directive before it can be used.\n" + "Enabling this will add ASTAR_JPS to the Scriping Define Symbols field in the Unity Player Settings", MessageType.Warning); if (GUILayout.Button("Enable Jump Point Search support")) { OptimizationHandler.EnableDefine("ASTAR_JPS"); } #endif } else { #if ASTAR_JPS EditorGUILayout.HelpBox("If you are not using JPS in any scene, you can disable it to save memory", MessageType.Info); if (GUILayout.Button("Disable Jump Point Search support")) { OptimizationHandler.DisableDefine("ASTAR_JPS"); } #endif } }
private void ApplyGridGraphEndpointSpecialCase() { NavGraph[] graphs = AstarPath.active.graphs; for (int i = 0; i < graphs.Length; i++) { GridGraph graph = graphs[i] as GridGraph; if (graph != null) { GridNode[] nodes = graph.nodes; int num2 = (graph.neighbours != NumNeighbours.Four) ? ((graph.neighbours != NumNeighbours.Eight) ? 6 : 8) : 4; for (int j = 0; j < graph.depth; j++) { for (int k = 0; k < graph.width; k++) { GridNode node = nodes[(j * graph.width) + k]; if (!node.Walkable) { int num5 = node.NodeIndex * this.pivotCount; for (int m = 0; m < this.pivotCount; m++) { this.costs[num5 + m] = uint.MaxValue; } for (int n = 0; n < num2; n++) { int num8; int num9; if (graph.neighbours == NumNeighbours.Six) { num8 = k + graph.neighbourXOffsets[GridGraph.hexagonNeighbourIndices[n]]; num9 = j + graph.neighbourZOffsets[GridGraph.hexagonNeighbourIndices[n]]; } else { num8 = k + graph.neighbourXOffsets[n]; num9 = j + graph.neighbourZOffsets[n]; } if (((num8 >= 0) && (num9 >= 0)) && ((num8 < graph.width) && (num9 < graph.depth))) { GridNode node2 = graph.nodes[(num9 * graph.width) + num8]; if (node2.Walkable) { for (int num10 = 0; num10 < this.pivotCount; num10++) { uint num11 = this.costs[(node2.NodeIndex * this.pivotCount) + num10] + graph.neighbourCosts[n]; this.costs[num5 + num10] = Math.Min(this.costs[num5 + num10], num11); Debug.DrawLine((Vector3)node.position, (Vector3)node2.position, Color.blue, 1f); } } } } for (int num12 = 0; num12 < this.pivotCount; num12++) { if (this.costs[num5 + num12] == uint.MaxValue) { this.costs[num5 + num12] = 0; } } } } } } } }
void DrawPositionField (GridGraph graph) { Vector3 pivotPoint; Vector3 diff; GUILayout.BeginHorizontal (); switch (pivot) { case GridPivot.Center: graph.center = RoundVector3 ( graph.center ); graph.center = EditorGUILayout.Vector3Field ("Center",graph.center); break; case GridPivot.TopLeft: pivotPoint = graph.matrix.MultiplyPoint3x4 (new Vector3 (0,0,graph.depth)); pivotPoint = RoundVector3 ( pivotPoint ); diff = pivotPoint-graph.center; pivotPoint = EditorGUILayout.Vector3Field ("Top-Left",pivotPoint); graph.center = pivotPoint-diff; break; case GridPivot.TopRight: pivotPoint = graph.matrix.MultiplyPoint3x4 (new Vector3 (graph.width,0,graph.depth)); pivotPoint = RoundVector3 ( pivotPoint ); diff = pivotPoint-graph.center; pivotPoint = EditorGUILayout.Vector3Field ("Top-Right",pivotPoint); graph.center = pivotPoint-diff; break; case GridPivot.BottomLeft: pivotPoint = graph.matrix.MultiplyPoint3x4 (new Vector3 (0,0,0)); pivotPoint = RoundVector3 ( pivotPoint ); diff = pivotPoint-graph.center; pivotPoint = EditorGUILayout.Vector3Field ("Bottom-Left",pivotPoint); graph.center = pivotPoint-diff; break; case GridPivot.BottomRight: pivotPoint = graph.matrix.MultiplyPoint3x4 (new Vector3 (graph.width,0,0)); pivotPoint = RoundVector3 ( pivotPoint ); diff = pivotPoint-graph.center; pivotPoint = EditorGUILayout.Vector3Field ("Bottom-Right",pivotPoint); graph.center = pivotPoint-diff; break; } graph.GenerateMatrix (); pivot = PivotPointSelector (pivot); GUILayout.EndHorizontal (); }
protected virtual void DrawMiddleSection (GridGraph graph) { DrawNeighbours(graph); DrawMaxClimb(graph); DrawMaxSlope(graph); DrawErosion(graph); }
void DrawIsometricField (GridGraph graph) { var isometricGUIContent = new GUIContent ("Isometric Angle", "For an isometric 2D game, you can use this parameter to scale the graph correctly.\nIt can also be used to create a hexagon grid."); var isometricOptions = new [] {new GUIContent ("None (0°)"), new GUIContent ("Isometric (≈54.74°)"), new GUIContent("Custom")}; var isometricValues = new [] {0f, standardIsometric}; var isometricOption = 2; for (int i = 0; i < isometricValues.Length; i++) { if (Mathf.Approximately (graph.isometricAngle, isometricValues[i])) { isometricOption = i; } } var prevIsometricOption = isometricOption; isometricOption = EditorGUILayout.IntPopup (isometricGUIContent, isometricOption, isometricOptions, new [] {0, 1, 2}); if (prevIsometricOption != isometricOption) { // Change to something that will not match the predefined values above graph.isometricAngle = 45; } if (isometricOption < 2) { graph.isometricAngle = isometricValues[isometricOption]; } else { // Custom graph.isometricAngle = EditorGUILayout.FloatField (isometricGUIContent, graph.isometricAngle); } }
void DrawWidthDepthFields (GridGraph graph) { lockStyle = lockStyle ?? AstarPathEditor.astarSkin.FindStyle ("GridSizeLock") ?? new GUIStyle (); GUILayout.BeginHorizontal (); GUILayout.BeginVertical (); int newWidth = EditorGUILayout.IntField (new GUIContent ("Width (nodes)","Width of the graph in nodes"), graph.width); int newDepth = EditorGUILayout.IntField (new GUIContent ("Depth (nodes)","Depth (or height you might also call it) of the graph in nodes"), graph.depth); GUILayout.EndVertical (); Rect lockRect = GUILayoutUtility.GetRect (lockStyle.fixedWidth,lockStyle.fixedHeight); GUILayout.EndHorizontal (); // All the layouts mess up the margin to the next control, so add it manually GUILayout.Space (2); // Add a small offset to make it better centred around the controls lockRect.y += 3; lockRect.width = lockStyle.fixedWidth; lockRect.height = lockStyle.fixedHeight; lockRect.x += lockStyle.margin.left; lockRect.y += lockStyle.margin.top; locked = GUI.Toggle (lockRect,locked, new GUIContent ("", "If the width and depth values are locked, " + "changing the node size will scale the grid which keeping the number of nodes consistent " + "instead of keeping the size the same and changing the number of nodes in the graph"), lockStyle); if (newWidth != graph.width || newDepth != graph.depth) { SnapSizeToNodes (newWidth,newDepth,graph); } }
public void UpdateShortcuts() { navmesh = (NavMeshGraph)FindGraphOfType (typeof(NavMeshGraph)); gridGraph = (GridGraph)FindGraphOfType (typeof(GridGraph)); listGraph = (ListGraph)FindGraphOfType (typeof(ListGraph)); }
/** Returns randomly selected points on the specified nodes with each point being separated by \a clearanceRadius from each other. * Selecting points ON the nodes only works for TriangleMeshNode (used by Recast Graph and Navmesh Graph) and GridNode (used by GridGraph). * For other node types, only the positions of the nodes will be used. * * clearanceRadius will be reduced if no valid points can be found. */ public static List <Vector3> GetPointsOnNodes(List <GraphNode> nodes, int count, float clearanceRadius = 0) { if (nodes == null) { throw new System.ArgumentNullException("nodes"); } if (nodes.Count == 0) { throw new System.ArgumentException("no nodes passed"); } var rnd = new System.Random(); List <Vector3> pts = ListPool <Vector3> .Claim(count); // Square clearanceRadius *= clearanceRadius; if (nodes[0] is TriangleMeshNode #if !ASTAR_NO_GRID_GRAPH || nodes[0] is GridNode #endif ) { //Assume all nodes are triangle nodes or grid nodes List <float> accs = ListPool <float> .Claim(nodes.Count); float tot = 0; for (int i = 0; i < nodes.Count; i++) { var tnode = nodes[i] as TriangleMeshNode; if (tnode != null) { /** \bug Doesn't this need to be divided by 2? */ float a = System.Math.Abs(Polygon.TriangleArea2(tnode.GetVertex(0), tnode.GetVertex(1), tnode.GetVertex(2))); tot += a; accs.Add(tot); } #if !ASTAR_NO_GRID_GRAPH else { var gnode = nodes[i] as GridNode; if (gnode != null) { GridGraph gg = GridNode.GetGridGraph(gnode.GraphIndex); float a = gg.nodeSize * gg.nodeSize; tot += a; accs.Add(tot); } else { accs.Add(tot); } } #endif } for (int i = 0; i < count; i++) { //Pick point int testCount = 0; int testLimit = 10; bool worked = false; while (!worked) { worked = true; //If no valid points can be found, progressively lower the clearance radius until such a point is found if (testCount >= testLimit) { clearanceRadius *= 0.8f; testLimit += 10; if (testLimit > 100) { clearanceRadius = 0; } } float tg = (float)rnd.NextDouble() * tot; int v = accs.BinarySearch(tg); if (v < 0) { v = ~v; } if (v >= nodes.Count) { // This shouldn't happen, due to NextDouble being smaller than 1... but I don't trust floating point arithmetic. worked = false; continue; } var node = nodes[v] as TriangleMeshNode; Vector3 p; if (node != null) { // Find a random point inside the triangle float v1; float v2; do { v1 = (float)rnd.NextDouble(); v2 = (float)rnd.NextDouble(); } while (v1 + v2 > 1); p = ((Vector3)(node.GetVertex(1) - node.GetVertex(0))) * v1 + ((Vector3)(node.GetVertex(2) - node.GetVertex(0))) * v2 + (Vector3)node.GetVertex(0); } else { #if !ASTAR_NO_GRID_GRAPH var gnode = nodes[v] as GridNode; if (gnode != null) { GridGraph gg = GridNode.GetGridGraph(gnode.GraphIndex); float v1 = (float)rnd.NextDouble(); float v2 = (float)rnd.NextDouble(); p = (Vector3)gnode.position + new Vector3(v1 - 0.5f, 0, v2 - 0.5f) * gg.nodeSize; } else #endif { //Point nodes have no area, so we break directly instead pts.Add((Vector3)nodes[v].position); break; } } // Test if it is some distance away from the other points if (clearanceRadius > 0) { for (int j = 0; j < pts.Count; j++) { if ((pts[j] - p).sqrMagnitude < clearanceRadius) { worked = false; break; } } } if (worked) { pts.Add(p); break; } testCount++; } } ListPool <float> .Release(accs); } else { for (int i = 0; i < count; i++) { pts.Add((Vector3)nodes[rnd.Next(nodes.Count)].position); } } return(pts); }
/** Opens a node using Jump Point Search. * \see http://en.wikipedia.org/wiki/Jump_point_search */ public void JPSOpen(Path path, PathNode pathNode, PathHandler handler) { GridGraph gg = GetGridGraph(GraphIndex); int[] neighbourOffsets = gg.neighbourOffsets; GridNode[] nodes = gg.nodes; ushort pid = handler.PathID; int noncyclic = gridFlags & 0xFF; int cyclic = 0; for (int i = 0; i < 8; i++) { cyclic |= ((noncyclic >> i) & 0x1) << JPSCyclic[i]; } var parent = pathNode.parent != null ? pathNode.parent.node as GridNode : null; int parentDir = -1; if (parent != null) { int diff = parent != null ? parent.nodeInGridIndex - nodeInGridIndex : 0; int x2 = nodeInGridIndex % gg.width; int x1 = parent.nodeInGridIndex % gg.width; if (diff < 0) { if (x1 == x2) { parentDir = 0; } else if (x1 < x2) { parentDir = 7; } else { parentDir = 4; } } else { if (x1 == x2) { parentDir = 1; } else if (x1 < x2) { parentDir = 6; } else { parentDir = 5; } } } int cyclicParentDir = 0; // Check for -1 int forced = 0; if (parentDir != -1) { cyclicParentDir = JPSCyclic[parentDir]; // Loop around to be able to assume -X is where we came from cyclic = ((cyclic >> cyclicParentDir) | ((cyclic << 8) >> cyclicParentDir)) & 0xFF; } else { forced = 0xFF; //parentDir = 0; } bool diagonal = parentDir >= 4; int natural; if (diagonal) { for (int i = 0; i < 8; i++) { if (((cyclic >> i) & 1) == 0) { forced |= JPSForcedDiagonal[i]; } } natural = JPSNaturalDiagonalNeighbours; } else { for (int i = 0; i < 8; i++) { if (((cyclic >> i) & 1) == 0) { forced |= JPSForced[i]; } } natural = JPSNaturalStraightNeighbours; } // Don't force nodes we cannot reach anyway forced &= cyclic; natural &= cyclic; int nb = forced | natural; /*if ( ((Vector3)position - new Vector3(0.5f,0,3.5f)).magnitude < 0.5f ) { * EB.Debug.Log (noncyclic + " " + parentDir + " " + cyclicParentDir); * EB.Debug.Log (System.Convert.ToString (cyclic, 2)+"\n"+System.Convert.ToString (noncyclic, 2)+"\n"+System.Convert.ToString (natural, 2)+"\n"+System.Convert.ToString (forced, 2)); * }*/ for (int i = 0; i < 8; i++) { if (((nb >> i) & 1) != 0) { int oi = JPSInverseCyclic[(i + cyclicParentDir) % 8]; GridNode other = nodes[nodeInGridIndex + neighbourOffsets[oi]]; #if ASTARDEBUG if (((forced >> i) & 1) != 0) { Debug.DrawLine((Vector3)position, Vector3.Lerp((Vector3)other.position, (Vector3)position, 0.6f), Color.red); } if (((natural >> i) & 1) != 0) { Debug.DrawLine((Vector3)position + Vector3.up * 0.2f, Vector3.Lerp((Vector3)other.position, (Vector3)position, 0.6f) + Vector3.up * 0.2f, Color.green); } #endif if (oi < 4) { other = JPSJumpStraight(other, path, handler, JPSInverseCyclic[(i + 4 + cyclicParentDir) % 8]); } else { other = other.JPSJumpDiagonal(path, handler, JPSInverseCyclic[(i + 4 + cyclicParentDir) % 8]); } if (other != null) { //Debug.DrawLine ( (Vector3)position + Vector3.up*0.0f, (Vector3)other.position + Vector3.up*0.3f, Color.cyan); //Debug.DrawRay ( (Vector3)other.position, Vector3.up, Color.cyan); //GridNode other = nodes[nodeInGridIndex + neighbourOffsets[i]]; //if (!path.CanTraverse (other)) continue; PathNode otherPN = handler.GetPathNode(other); if (otherPN.pathID != pid) { otherPN.parent = pathNode; otherPN.pathID = pid; otherPN.cost = (uint)(other.position - position).costMagnitude; //neighbourCosts[i]; otherPN.H = path.CalculateHScore(other); other.UpdateG(path, otherPN); //Debug.Log ("G " + otherPN.G + " F " + otherPN.F); handler.PushNode(otherPN); //Debug.DrawRay ((Vector3)otherPN.node.Position, Vector3.up,Color.blue); } else { //If not we can test if the path from the current node to this one is a better one then the one already used uint tmpCost = (uint)(other.position - position).costMagnitude; //neighbourCosts[i]; if (pathNode.G + tmpCost + path.GetTraversalCost(other) < otherPN.G) { //Debug.Log ("Path better from " + NodeIndex + " to " + otherPN.node.NodeIndex + " " + (pathNode.G+tmpCost+path.GetTraversalCost(other)) + " < " + otherPN.G); otherPN.cost = tmpCost; otherPN.parent = pathNode; other.UpdateRecursiveG(path, otherPN, handler); //Or if the path from this node ("other") to the current ("current") is better } else if (otherPN.G + tmpCost + path.GetTraversalCost(this) < pathNode.G) { //Debug.Log ("Path better from " + otherPN.node.NodeIndex + " to " + NodeIndex + " " + (otherPN.G+tmpCost+path.GetTraversalCost (this)) + " < " + pathNode.G); pathNode.parent = otherPN; pathNode.cost = tmpCost; UpdateRecursiveG(path, pathNode, handler); } } } } #if ASTARDEBUG if (i == 0 && parentDir != -1 && this.nodeInGridIndex > 10) { int oi = JPSInverseCyclic[(i + cyclicParentDir) % 8]; if (nodeInGridIndex + neighbourOffsets[oi] < 0 || nodeInGridIndex + neighbourOffsets[oi] >= nodes.Length) { //Debug.LogError ("ERR: " + (nodeInGridIndex + neighbourOffsets[oi]) + " " + cyclicParentDir + " " + parentDir + " Reverted " + oi); //Debug.DrawRay ((Vector3)position, Vector3.up, Color.red); } else { GridNode other = nodes[nodeInGridIndex + neighbourOffsets[oi]]; Debug.DrawLine((Vector3)position - Vector3.up * 0.2f, Vector3.Lerp((Vector3)other.position, (Vector3)position, 0.6f) - Vector3.up * 0.2f, Color.blue); } } #endif } }
protected virtual void DrawCutCorners (GridGraph graph) { graph.cutCorners = EditorGUILayout.Toggle (new GUIContent ("Cut Corners","Enables or disables cutting corners. See docs for image example"),graph.cutCorners); }
protected virtual void DrawCutCorners (GridGraph graph) { graph.cutCorners = EditorGUILayout.Toggle(new GUIContent("Cut Corners", "Enables or disables cutting corners. See docs for image example"), graph.cutCorners); if (!graph.cutCorners && graph.useJumpPointSearch) { EditorGUILayout.HelpBox("Jump Point Search only works if 'Cut Corners' is enabled.", MessageType.Error); } }
public override void Open(Path path, PathNode pathNode, PathHandler handler) { GridGraph gg = GetGridGraph(GraphIndex); ushort pid = handler.PathID; { int[] neighbourOffsets = gg.neighbourOffsets; uint[] neighbourCosts = gg.neighbourCosts; GridNode[] nodes = gg.nodes; var index = NodeInGridIndex; for (int i = 0; i < 8; i++) { if (HasConnectionInDirection(i)) { GridNode other = nodes[index + neighbourOffsets[i]]; if (!path.CanTraverse(other)) { continue; } PathNode otherPN = handler.GetPathNode(other); uint tmpCost = neighbourCosts[i]; // Check if the other node has not yet been visited by this path if (otherPN.pathID != pid) { otherPN.parent = pathNode; otherPN.pathID = pid; otherPN.cost = tmpCost; otherPN.H = path.CalculateHScore(other); otherPN.UpdateG(path); handler.heap.Add(otherPN); } else { // Sorry for the huge number of #ifs //If not we can test if the path from the current node to this one is a better one then the one already used #if ASTAR_NO_TRAVERSAL_COST if (pathNode.G + tmpCost < otherPN.G) #else if (pathNode.G + tmpCost + path.GetTraversalCost(other) < otherPN.G) #endif { //Debug.Log ("Path better from " + NodeIndex + " to " + otherPN.node.NodeIndex + " " + (pathNode.G+tmpCost+path.GetTraversalCost(other)) + " < " + otherPN.G); otherPN.cost = tmpCost; otherPN.parent = pathNode; other.UpdateRecursiveG(path, otherPN, handler); } } } } } #if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS base.Open(path, pathNode, handler); #endif }
protected virtual void DrawNeighbours (GridGraph graph) { graph.neighbours = (NumNeighbours)EditorGUILayout.EnumPopup (new GUIContent ("Connections","Sets how many connections a node should have to it's neighbour nodes."),graph.neighbours); EditorGUI.indentLevel++; if (graph.neighbours == NumNeighbours.Eight) { DrawCutCorners(graph); } if (graph.neighbours == NumNeighbours.Six) { graph.uniformEdgeCosts = EditorGUILayout.Toggle (new GUIContent ("Hexagon connection costs", "Tweak the edge costs in the graph to be more suitable for hexagon graphs"), graph.uniformEdgeCosts); if ((!Mathf.Approximately(graph.isometricAngle, standardIsometric) || !graph.uniformEdgeCosts) && GUILayout.Button ("Configure as hexagon graph")) { graph.isometricAngle = standardIsometric; graph.uniformEdgeCosts = true; } } else { graph.uniformEdgeCosts = false; } EditorGUI.indentLevel--; }
/** Executes a diagonal jump search. * \see http://en.wikipedia.org/wiki/Jump_point_search */ GridNode JPSJumpDiagonal(Path path, PathHandler handler, int parentDir, int depth = 0) { // Indexing into the cache arrays from multiple threads like this should cause // a lot of false sharing and cache trashing, but after profiling it seems // that this is not a major concern int threadID = handler.threadID; int threadOffset = 8 * handler.threadID; // This is needed to make sure different threads don't overwrite each others results // It doesn't matter if we throw away some caching done by other threads as this will only // happen during the first few path requests if (JPSLastCacheID == null || JPSLastCacheID.Length < handler.totalThreadCount) { lock (this) { // Check again in case another thread has already created the array if (JPSLastCacheID == null || JPSLastCacheID.Length < handler.totalThreadCount) { JPSCache = new GridNode[8 * handler.totalThreadCount]; JPSDead = new byte[handler.totalThreadCount]; JPSLastCacheID = new ushort[handler.totalThreadCount]; } } } if (JPSLastCacheID[threadID] != path.pathID) { for (int i = 0; i < 8; i++) { JPSCache[i + threadOffset] = null; } JPSLastCacheID[threadID] = path.pathID; JPSDead[threadID] = 0; } // Cache earlier results, major optimization // It is important to read from it once and then return the same result, // if we read from it twice, we might get different results due to other threads clearing the array sometimes GridNode cachedResult = JPSCache[parentDir + threadOffset]; if (cachedResult != null) { //return cachedResult; } //if ( ((JPSDead[threadID] >> parentDir)&1) != 0 ) return null; // Special node (e.g end node), take care of if (handler.GetPathNode(this).flag2) { //Debug.Log ("Found end Node!"); //Debug.DrawRay ((Vector3)position, Vector3.up*2, Color.green); JPSCache[parentDir + threadOffset] = this; return(this); } #if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS // Special node which has custom connections, take care of if (connections != null && connections.Length > 0) { JPSCache[parentDir] = this; return(this); } #endif int noncyclic = gridFlags; //We don't actually need to & with this because we don't use the other bits. & 0xFF; int cyclic = 0; for (int i = 0; i < 8; i++) { cyclic |= ((noncyclic >> i) & 0x1) << JPSCyclic[i]; } int forced = 0; int cyclicParentDir = JPSCyclic[parentDir]; // Loop around to be able to assume -X is where we came from cyclic = ((cyclic >> cyclicParentDir) | ((cyclic << 8) >> cyclicParentDir)) & 0xFF; int natural; for (int i = 0; i < 8; i++) { if (((cyclic >> i) & 1) == 0) { forced |= JPSForcedDiagonal[i]; } } natural = JPSNaturalDiagonalNeighbours; /* * if ( ((Vector3)position - new Vector3(1.5f,0,-1.5f)).magnitude < 0.5f ) { * EB.Debug.Log (noncyclic + " " + parentDir + " " + cyclicParentDir); * EB.Debug.Log (System.Convert.ToString (cyclic, 2)+"\n"+System.Convert.ToString (noncyclic, 2)+"\n"+System.Convert.ToString (natural, 2)+"\n"+System.Convert.ToString (forced, 2)); * }*/ // Don't force nodes we cannot reach anyway forced &= cyclic; natural &= cyclic; if ((forced & (~natural)) != 0) { // Some of the neighbour nodes are forced JPSCache[parentDir + threadOffset] = this; return(this); } int forwardDir; GridGraph gg = GetGridGraph(GraphIndex); int[] neighbourOffsets = gg.neighbourOffsets; GridNode[] nodes = gg.nodes; { // Rotate 180 degrees - 1 node forwardDir = 3; if (((cyclic >> forwardDir) & 1) != 0) { int oi = JPSInverseCyclic[(forwardDir + cyclicParentDir) % 8]; GridNode other = nodes[nodeInGridIndex + neighbourOffsets[oi]]; //Debug.DrawLine ( (Vector3)position + Vector3.up*0.2f*(depth), (Vector3)other.position + Vector3.up*0.2f*(depth+1), Color.black); GridNode v; if (oi < 4) { v = JPSJumpStraight(other, path, handler, JPSInverseCyclic[(cyclicParentDir - 1 + 8) % 8], depth + 1); } else { v = other.JPSJumpDiagonal(path, handler, JPSInverseCyclic[(cyclicParentDir - 1 + 8) % 8], depth + 1); } if (v != null) { JPSCache[parentDir + threadOffset] = this; return(this); } } // Rotate 180 degrees + 1 node forwardDir = 5; if (((cyclic >> forwardDir) & 1) != 0) { int oi = JPSInverseCyclic[(forwardDir + cyclicParentDir) % 8]; GridNode other = nodes[nodeInGridIndex + neighbourOffsets[oi]]; //Debug.DrawLine ( (Vector3)position + Vector3.up*0.2f*(depth), (Vector3)other.position + Vector3.up*0.2f*(depth+1), Color.grey); GridNode v; if (oi < 4) { v = JPSJumpStraight(other, path, handler, JPSInverseCyclic[(cyclicParentDir + 1 + 8) % 8], depth + 1); } else { v = other.JPSJumpDiagonal(path, handler, JPSInverseCyclic[(cyclicParentDir + 1 + 8) % 8], depth + 1); } if (v != null) { JPSCache[parentDir + threadOffset] = this; return(this); } } } // Rotate 180 degrees forwardDir = 4; if (((cyclic >> forwardDir) & 1) != 0) { int oi = JPSInverseCyclic[(forwardDir + cyclicParentDir) % 8]; GridNode other = nodes[nodeInGridIndex + neighbourOffsets[oi]]; //Debug.DrawLine ( (Vector3)position + Vector3.up*0.2f*(depth), (Vector3)other.position + Vector3.up*0.2f*(depth+1), Color.magenta); var v = other.JPSJumpDiagonal(path, handler, parentDir, depth + 1); if (v != null) { JPSCache[parentDir + threadOffset] = v; return(v); } } JPSDead[threadID] |= (byte)(1 << parentDir); return(null); }
protected virtual void DrawMaxClimb (GridGraph graph) { graph.maxClimb = EditorGUILayout.FloatField (new GUIContent ("Max Climb","How high in world units, relative to the graph, should a climbable level be. A zero (0) indicates infinity"),graph.maxClimb); if ( graph.maxClimb < 0 ) graph.maxClimb = 0; EditorGUI.indentLevel++; graph.maxClimbAxis = EditorGUILayout.IntPopup (new GUIContent ("Climb Axis","Determines which axis the above setting should test on"),graph.maxClimbAxis,new [] {new GUIContent ("X"),new GUIContent ("Y"),new GUIContent ("Z")},new [] {0,1,2}); EditorGUI.indentLevel--; if ( graph.maxClimb > 0 && Mathf.Abs((Quaternion.Euler (graph.rotation) * new Vector3 (graph.nodeSize,0,graph.nodeSize))[graph.maxClimbAxis]) > graph.maxClimb ) { EditorGUILayout.HelpBox ("Nodes are spaced further apart than this in the grid. You might want to increase this value or change the axis", MessageType.Warning ); } }
public override void Open(Path path, PathNode pathNode, PathHandler handler) { GridGraph gg = GetGridGraph(GraphIndex); ushort pid = handler.PathID; { int[] neighbourOffsets = gg.neighbourOffsets; uint[] neighbourCosts = gg.neighbourCosts; GridNode[] nodes = gg.nodes; for (int i = 0; i < 8; i++) { if (GetConnectionInternal(i)) { GridNode other = nodes[nodeInGridIndex + neighbourOffsets[i]]; if (!path.CanTraverse(other)) { continue; } PathNode otherPN = handler.GetPathNode(other); // Multiply the connection cost with 1 + the average of the traversal costs for the two nodes uint tmpCost = (neighbourCosts[i] * (256 + path.GetTraversalCost(this) + path.GetTraversalCost(other))) / 128; if (otherPN.pathID != pid) { otherPN.parent = pathNode; otherPN.pathID = pid; otherPN.cost = tmpCost; otherPN.H = path.CalculateHScore(other); other.UpdateG(path, otherPN); //Debug.Log ("G " + otherPN.G + " F " + otherPN.F); handler.PushNode(otherPN); //Debug.DrawRay ((Vector3)otherPN.node.Position, Vector3.up,Color.blue); } else { // Sorry for the huge number of #ifs //If not we can test if the path from the current node to this one is a better one then the one already used if (pathNode.G + tmpCost < otherPN.G) { //Debug.Log ("Path better from " + NodeIndex + " to " + otherPN.node.NodeIndex + " " + (pathNode.G+tmpCost+path.GetTraversalCost(other)) + " < " + otherPN.G); otherPN.cost = tmpCost; otherPN.parent = pathNode; other.UpdateRecursiveG(path, otherPN, handler); //Or if the path from this node ("other") to the current ("current") is better } else if (otherPN.G + tmpCost < pathNode.G) { //Debug.Log ("Path better from " + otherPN.node.NodeIndex + " to " + NodeIndex + " " + (otherPN.G+tmpCost+path.GetTraversalCost (this)) + " < " + pathNode.G); pathNode.parent = otherPN; pathNode.cost = tmpCost; UpdateRecursiveG(path, pathNode, handler); } } } } } if (connections != null) { for (int i = 0; i < connections.Length; i++) { GraphNode other = connections[i]; if (!path.CanTraverse(other)) { continue; } PathNode otherPN = handler.GetPathNode(other); uint tmpCost = (connectionCosts[i] * (256 + path.GetTraversalCost(this) + path.GetTraversalCost(other))) / 128; if (otherPN.pathID != pid) { otherPN.parent = pathNode; otherPN.pathID = pid; otherPN.cost = tmpCost; otherPN.H = path.CalculateHScore(other); other.UpdateG(path, otherPN); //Debug.Log ("G " + otherPN.G + " F " + otherPN.F); handler.PushNode(otherPN); //Debug.DrawRay ((Vector3)otherPN.node.Position, Vector3.up,Color.blue); } else { // Sorry for the huge number of #ifs //If not we can test if the path from the current node to this one is a better one then the one already used if (pathNode.G + tmpCost < otherPN.G) { //Debug.Log ("Path better from " + NodeIndex + " to " + otherPN.node.NodeIndex + " " + (pathNode.G+tmpCost+path.GetTraversalCost(other)) + " < " + otherPN.G); otherPN.cost = tmpCost; otherPN.parent = pathNode; other.UpdateRecursiveG(path, otherPN, handler); //Or if the path from this node ("other") to the current ("current") is better } else if (otherPN.G + tmpCost < pathNode.G && other.ContainsConnection(this)) { //Debug.Log ("Path better from " + otherPN.node.NodeIndex + " to " + NodeIndex + " " + (otherPN.G+tmpCost+path.GetTraversalCost (this)) + " < " + pathNode.G); pathNode.parent = otherPN; pathNode.cost = tmpCost; UpdateRecursiveG(path, pathNode, handler); } } } } }
protected void DrawMaxSlope (GridGraph graph) { graph.maxSlope = EditorGUILayout.Slider (new GUIContent ("Max Slope","Sets the max slope in degrees for a point to be walkable. Only enabled if Height Testing is enabled."),graph.maxSlope,0,90F); }
/** Draws settings for using a texture as source for a grid. * \astarpro */ public void DrawTextureData(GridGraph.TextureData data, GridGraph graph) { if (data == null) { return; } data.enabled = ToggleGroup("Use Texture", data.enabled); //EditorGUILayout.Toggle ("Use Texture",data.enabled); //data.enabled = ToggleGroup ("Use Texture",data.enabled); if (!data.enabled) { return; } bool preGUI = GUI.enabled; GUI.enabled = data.enabled && GUI.enabled; EditorGUI.indentLevel++; //data.source = EditorGUILayout.ObjectField ("Source",data.source,typeof(Texture2D),false) as Texture2D; data.source = ObjectField("Source", data.source, typeof(Texture2D), false) as Texture2D; if (data.source != null) { string path = AssetDatabase.GetAssetPath(data.source); if (path != "") { TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter; if (!importer.isReadable) { if (FixLabel("Texture is not readable")) { importer.isReadable = true; EditorUtility.SetDirty(importer); AssetDatabase.ImportAsset(path); } } } } for (int i = 0; i < 3; i++) { string channelName = i == 0 ? "R" : (i == 1 ? "G" : "B"); data.channels[i] = (GridGraph.TextureData.ChannelUse)EditorGUILayout.Popup(channelName, (int)data.channels[i], ChannelUseNames); if (data.channels[i] != GridGraph.TextureData.ChannelUse.None) { EditorGUI.indentLevel++; data.factors[i] = EditorGUILayout.FloatField("Factor", data.factors[i]); string help = ""; switch (data.channels[i]) { case GridGraph.TextureData.ChannelUse.Penalty: help = "Nodes are applied penalty according to channel '" + channelName + "', multiplied with factor"; break; case GridGraph.TextureData.ChannelUse.Position: help = "Nodes Y position is changed according to channel '" + channelName + "', multiplied with factor"; if (graph.collision.heightCheck) { HelpBox("Getting position both from raycast and from texture. You should disable one of them"); } break; case GridGraph.TextureData.ChannelUse.WalkablePenalty: help = "If channel '" + channelName + "' is 0, the node is made unwalkable. Otherwise the node is applied penalty multiplied with factor"; break; } HelpBox(help); EditorGUI.indentLevel--; } } if (GUILayout.Button("Generate Reference")) { SaveReferenceTexture(graph); } GUI.enabled = preGUI; EditorGUI.indentLevel--; }
protected void DrawErosion (GridGraph graph) { graph.erodeIterations = EditorGUILayout.IntField (new GUIContent ("Erosion iterations","Sets how many times the graph should be eroded. This adds extra margin to objects."),graph.erodeIterations); graph.erodeIterations = graph.erodeIterations < 0 ? 0 : (graph.erodeIterations > 16 ? 16 : graph.erodeIterations); //Clamp iterations to [0,16] if ( graph.erodeIterations > 0 ) { EditorGUI.indentLevel++; graph.erosionUseTags = EditorGUILayout.Toggle (new GUIContent ("Erosion Uses Tags","Instead of making nodes unwalkable, " + "nodes will have their tag set to a value corresponding to their erosion level, " + "which is a quite good measurement of their distance to the closest wall.\nSee online documentation for more info."), graph.erosionUseTags); if (graph.erosionUseTags) { EditorGUI.indentLevel++; graph.erosionFirstTag = EditorGUILayoutx.TagField ("First Tag",graph.erosionFirstTag); EditorGUI.indentLevel--; } EditorGUI.indentLevel--; } }
protected virtual void DrawRules(GridGraph graph) { var rules = graph.rules.GetRules(); for (int i = 0; i < rules.Count; i++) { var rule = rules[i]; if (rule != null) { var ruleEditor = GetEditor(rule); var ruleType = rule.GetType(); GUILayout.BeginHorizontal(); rule.enabled = ToggleGroup(ruleHeaders.ContainsKey(ruleType) ? ruleHeaders[ruleType] : ruleType.Name, rule.enabled); if (GUILayout.Button("", AstarPathEditor.astarSkin.FindStyle("SimpleDeleteButton"))) { graph.rules.RemoveRule(rule); ruleEditorInstances.Remove(rule); rule.enabled = false; rule.DisposeUnmanagedData(); } GUILayout.EndHorizontal(); if (rule.enabled) { if (ruleEditor != null) { EditorGUI.indentLevel++; EditorGUI.BeginChangeCheck(); ruleEditor.OnInspectorGUI(graph, rule); if (EditorGUI.EndChangeCheck()) { rule.SetDirty(); } EditorGUI.indentLevel--; } else { EditorGUILayout.HelpBox("No editor found for " + rule.GetType().Name, MessageType.Error); } } } } EditorGUILayout.Separator(); GUILayout.BeginHorizontal(); GUILayout.Space(10); if (GUILayout.Button("Add Rule")) { if (ruleEditors == null) { FindRuleEditors(); } GenericMenu menu = new GenericMenu(); foreach (var type in ruleTypes) { menu.AddItem(new GUIContent(ruleHeaders.ContainsKey(type) ? ruleHeaders[type] : type.Name), false, ruleType => graph.rules.AddRule(System.Activator.CreateInstance((System.Type)ruleType) as GridGraphRule), type); } menu.ShowAsContext(); } GUILayout.Space(10); GUILayout.EndHorizontal(); }
void DrawLastSection (GridGraph graph) { GUILayout.Label (new GUIContent ("Advanced"), EditorStyles.boldLabel); DrawPenaltyModifications (graph); DrawJPS (graph); }
void DrawFirstSection(GridGraph graph) { float prevRatio = graph.aspectRatio; DrawInspectorMode(graph); Draw2DMode(graph); var normalizedPivotPoint = NormalizedPivotPoint(graph, pivot); var worldPoint = graph.CalculateTransform().Transform(normalizedPivotPoint); int newWidth, newDepth; DrawWidthDepthFields(graph, out newWidth, out newDepth); EditorGUI.BeginChangeCheck(); float newNodeSize; if (graph.inspectorGridMode == InspectorGridMode.Hexagonal) { graph.inspectorHexagonSizeMode = (InspectorGridHexagonNodeSize)EditorGUILayout.EnumPopup(new GUIContent("Hexagon dimension"), graph.inspectorHexagonSizeMode); float hexagonSize = GridGraph.ConvertNodeSizeToHexagonSize(graph.inspectorHexagonSizeMode, graph.nodeSize); hexagonSize = (float)System.Math.Round(hexagonSize, 5); newNodeSize = GridGraph.ConvertHexagonSizeToNodeSize(graph.inspectorHexagonSizeMode, EditorGUILayout.FloatField(hexagonSizeContents[(int)graph.inspectorHexagonSizeMode], hexagonSize)); } else { newNodeSize = EditorGUILayout.FloatField(new GUIContent("Node size", "The size of a single node. The size is the side of the node square in world units"), graph.nodeSize); } bool nodeSizeChanged = EditorGUI.EndChangeCheck(); newNodeSize = newNodeSize <= 0.01F ? 0.01F : newNodeSize; if (graph.inspectorGridMode == InspectorGridMode.IsometricGrid || graph.inspectorGridMode == InspectorGridMode.Advanced) { graph.aspectRatio = EditorGUILayout.FloatField(new GUIContent("Aspect Ratio", "Scaling of the nodes width/depth ratio. Good for isometric games"), graph.aspectRatio); DrawIsometricField(graph); } if ((nodeSizeChanged && locked) || (newWidth != graph.width || newDepth != graph.depth) || prevRatio != graph.aspectRatio) { graph.nodeSize = newNodeSize; graph.SetDimensions(newWidth, newDepth, newNodeSize); normalizedPivotPoint = NormalizedPivotPoint(graph, pivot); var newWorldPoint = graph.CalculateTransform().Transform(normalizedPivotPoint); // Move the center so that the pivot point stays at the same point in the world graph.center += worldPoint - newWorldPoint; graph.center = RoundVector3(graph.center); graph.UpdateTransform(); AutoScan(); } if ((nodeSizeChanged && !locked)) { graph.nodeSize = newNodeSize; graph.UpdateTransform(); } DrawPositionField(graph); DrawRotationField(graph); }
void DrawPenaltyModifications (GridGraph graph) { showExtra = EditorGUILayout.Foldout (showExtra, "Penalty Modifications"); if (showExtra) { EditorGUI.indentLevel+=2; graph.penaltyAngle = ToggleGroup (new GUIContent ("Angle Penalty","Adds a penalty based on the slope of the node"),graph.penaltyAngle); if (graph.penaltyAngle) { EditorGUI.indentLevel++; graph.penaltyAngleFactor = EditorGUILayout.FloatField (new GUIContent ("Factor","Scale of the penalty. A negative value should not be used"),graph.penaltyAngleFactor); graph.penaltyAnglePower = EditorGUILayout.Slider ("Power", graph.penaltyAnglePower, 0.1f, 10f); HelpBox ("Applies penalty to nodes based on the angle of the hit surface during the Height Testing\nPenalty applied is: P=(1-cos(angle)^power)*factor."); EditorGUI.indentLevel--; } graph.penaltyPosition = ToggleGroup ("Position Penalty",graph.penaltyPosition); if (graph.penaltyPosition) { EditorGUI.indentLevel++; graph.penaltyPositionOffset = EditorGUILayout.FloatField ("Offset",graph.penaltyPositionOffset); graph.penaltyPositionFactor = EditorGUILayout.FloatField ("Factor",graph.penaltyPositionFactor); HelpBox ("Applies penalty to nodes based on their Y coordinate\nSampled in Int3 space, i.e it is multiplied with Int3.Precision first ("+Int3.Precision+")\n" + "Be very careful when using negative values since a negative penalty will underflow and instead get really high"); EditorGUI.indentLevel--; } GUI.enabled = false; ToggleGroup (new GUIContent ("Use Texture",AstarPathEditor.AstarProTooltip),false); GUI.enabled = true; EditorGUI.indentLevel-=2; } }
public override float SurfaceArea() { GridGraph gg = GridNode.GetGridGraph(GraphIndex); return(gg.nodeSize * gg.nodeSize); }
protected virtual void DrawJPS (GridGraph graph) { // Jump point search is a pro only feature }
protected virtual void DrawCutCorners(GridGraph graph) { graph.cutCorners = EditorGUILayout.Toggle(new GUIContent("Cut Corners", "Enables or disables cutting corners. See docs for image example"), graph.cutCorners); }
public void SnapSizeToNodes (int newWidth, int newDepth, GridGraph graph) { graph.unclampedSize = new Vector2 (newWidth*graph.nodeSize,newDepth*graph.nodeSize); Vector3 newCenter = graph.matrix.MultiplyPoint3x4 (new Vector3 (newWidth/2F,0,newDepth/2F)); graph.center = newCenter; graph.GenerateMatrix (); AutoScan (); GUI.changed = true; }
protected virtual void DrawJPS(GridGraph graph) { // Jump point search is a pro only feature }
public void SaveReferenceTexture (GridGraph graph) { //GridGraph graph = target as GridGraph; if (graph.nodes == null || graph.nodes.Length != graph.width * graph.depth) { AstarPath.active.Scan (); } if (graph.nodes.Length != graph.width * graph.depth) { Debug.LogError ("Couldn't create reference image since width*depth != nodes.Length"); return; } if (graph.nodes.Length == 0) { Debug.LogError ("Couldn't create reference image since the graph is too small (0*0)"); return; } Texture2D tex = new Texture2D (graph.width,graph.depth); float maxY = float.NegativeInfinity; for (int i=0;i<graph.nodes.Length;i++) { Vector3 p = graph.inverseMatrix.MultiplyPoint ((Vector3)graph.nodes[i].position); maxY = p.y > maxY ? p.y : maxY; } Color[] cols = new Color[graph.width*graph.depth]; for (int z=0;z<graph.depth;z++) { for (int x=0;x<graph.width;x++) { GraphNode node = graph.nodes[z*graph.width+x]; float v = node.Walkable ? 1F : 0.0F; Vector3 p = graph.inverseMatrix.MultiplyPoint ((Vector3)node.position); float q = p.y / maxY; cols[z*graph.width+x] = new Color (v,q,0); } } tex.SetPixels (cols); tex.Apply (); string path = AssetDatabase.GenerateUniqueAssetPath ("Assets/gridReference.png"); using (System.IO.StreamWriter outstream = new System.IO.StreamWriter (path)) { using (System.IO.BinaryWriter outfile = new System.IO.BinaryWriter (outstream.BaseStream)) { outfile.Write (tex.EncodeToPNG ()); } } AssetDatabase.Refresh (); Object obj = AssetDatabase.LoadAssetAtPath (path,typeof (Texture)); EditorGUIUtility.PingObject (obj); }
/** Updates shortcuts to the first graph of different types. * Hard coding references to some graph types is not really a good thing imo. I want to keep it dynamic and flexible. * But these references ease the use of the system, so I decided to keep them. It is the only reference to specific graph types in the pathfinding core.\n */ public void UpdateShortcuts() { navmesh = (NavMeshGraph)FindGraphOfType(typeof(NavMeshGraph)); gridGraph = (GridGraph)FindGraphOfType(typeof(GridGraph)); pointGraph = (PointGraph)FindGraphOfType(typeof(PointGraph)); }
public void SnapSizeToNodes (int newWidth, int newDepth, GridGraph graph) { //Vector2 preSize = graph.unclampedSize; /*if (locked) { graph.unclampedSize = new Vector2 (newWidth*newNodeSize,newDepth*newNodeSize); graph.nodeSize = newNodeSize; graph.GenerateMatrix (); Vector3 newCenter = graph.matrix.MultiplyPoint3x4 (new Vector3 (newWidth/2F,0,newDepth/2F)); graph.center = newCenter; AstarPath.active.AutoScan (); } else {*/ graph.unclampedSize = new Vector2 (newWidth*graph.nodeSize,newDepth*graph.nodeSize); Vector3 newCenter = graph.matrix.MultiplyPoint3x4 (new Vector3 (newWidth/2F,0,newDepth/2F)); graph.center = newCenter; graph.GenerateMatrix (); AstarPath.active.AutoScan (); //} GUI.changed = true; }
/// <summary> /// Check if a straight path between v1 and v2 is valid. /// If both n1 and n2 are supplied it is assumed that the line goes from the center of n1 to the center of n2 and a more optimized graph linecast may be done. /// </summary> protected bool ValidateLine(GraphNode n1, GraphNode n2, Vector3 v1, Vector3 v2) { if (useRaycasting) { // Use raycasting to check if a straight path between v1 and v2 is valid if (use2DPhysics) { if (thickRaycast && thickRaycastRadius > 0 && Physics2D.CircleCast(v1 + raycastOffset, thickRaycastRadius, v2 - v1, (v2 - v1).magnitude, mask)) { return(false); } if (Physics2D.Linecast(v1 + raycastOffset, v2 + raycastOffset, mask)) { return(false); } } else { // Perform a thick raycast (if enabled) if (thickRaycast && thickRaycastRadius > 0 && Physics.SphereCast(new Ray(v1 + raycastOffset, v2 - v1), thickRaycastRadius, (v2 - v1).magnitude, mask)) { return(false); } // Perform a normal raycast // This is done even if a thick raycast is also done because thick raycasts do not report collisions for // colliders that overlapped the (imaginary) sphere at the origin of the thick raycast. // If this raycast was not done then some obstacles could be missed. if (Physics.Linecast(v1 + raycastOffset, v2 + raycastOffset, mask)) { return(false); } } } if (useGraphRaycasting) { #if !ASTAR_NO_GRID_GRAPH bool betweenNodeCenters = n1 != null && n2 != null; #endif if (n1 == null) { n1 = AstarPath.active.GetNearest(v1).node; } if (n2 == null) { n2 = AstarPath.active.GetNearest(v2).node; } if (n1 != null && n2 != null) { // Use graph raycasting to check if a straight path between v1 and v2 is valid NavGraph graph = n1.Graph; NavGraph graph2 = n2.Graph; if (graph != graph2) { return(false); } var rayGraph = graph as IRaycastableGraph; #if !ASTAR_NO_GRID_GRAPH GridGraph gg = graph as GridGraph; if (betweenNodeCenters && gg != null) { // If the linecast is exactly between the centers of two nodes on a grid graph then a more optimized linecast can be used. // This method is also more stable when raycasting along a diagonal when the line just touches an obstacle. // The normal linecast method may or may not detect that as a hit depending on floating point errors // however this method never detect it as an obstacle (and that is very good for this component as it improves the simplification). return(!gg.Linecast(n1 as GridNodeBase, n2 as GridNodeBase)); } else #endif if (rayGraph != null) { return(!rayGraph.Linecast(v1, v2, n1)); } } } return(true); }
public override void Open(Path path, PathNode pathNode, PathHandler handler) { GridGraph gg = GetGridGraph(GraphIndex); ushort pid = handler.PathID; #if ASTAR_JPS if (gg.useJumpPointSearch && !path.FloodingPath) { JPSOpen(path, pathNode, handler); } else #endif { int[] neighbourOffsets = gg.neighbourOffsets; uint[] neighbourCosts = gg.neighbourCosts; GridNode[] nodes = gg.nodes; for (int i = 0; i < 8; i++) { if (GetConnectionInternal(i)) { GridNode other = nodes[nodeInGridIndex + neighbourOffsets[i]]; if (!path.CanTraverse(other)) { continue; } PathNode otherPN = handler.GetPathNode(other); uint tmpCost = neighbourCosts[i]; if (otherPN.pathID != pid) { otherPN.parent = pathNode; otherPN.pathID = pid; otherPN.cost = tmpCost; otherPN.H = path.CalculateHScore(other); other.UpdateG(path, otherPN); //Debug.Log ("G " + otherPN.G + " F " + otherPN.F); handler.PushNode(otherPN); //Debug.DrawRay ((Vector3)otherPN.node.Position, Vector3.up,Color.blue); } else { // Sorry for the huge number of #ifs //If not we can test if the path from the current node to this one is a better one then the one already used #if ASTAR_NO_TRAVERSAL_COST if (pathNode.G + tmpCost < otherPN.G) #else if (pathNode.G + tmpCost + path.GetTraversalCost(other) < otherPN.G) #endif { //Debug.Log ("Path better from " + NodeIndex + " to " + otherPN.node.NodeIndex + " " + (pathNode.G+tmpCost+path.GetTraversalCost(other)) + " < " + otherPN.G); otherPN.cost = tmpCost; otherPN.parent = pathNode; other.UpdateRecursiveG(path, otherPN, handler); //Or if the path from this node ("other") to the current ("current") is better } #if ASTAR_NO_TRAVERSAL_COST else if (otherPN.G + tmpCost < pathNode.G) #else else if (otherPN.G + tmpCost + path.GetTraversalCost(this) < pathNode.G) #endif { //Debug.Log ("Path better from " + otherPN.node.NodeIndex + " to " + NodeIndex + " " + (otherPN.G+tmpCost+path.GetTraversalCost (this)) + " < " + pathNode.G); pathNode.parent = otherPN; pathNode.cost = tmpCost; UpdateRecursiveG(path, pathNode, handler); } } } } } #if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS if (connections != null) { for (int i = 0; i < connections.Length; i++) { GraphNode other = connections[i]; if (!path.CanTraverse(other)) { continue; } PathNode otherPN = handler.GetPathNode(other); uint tmpCost = connectionCosts[i]; if (otherPN.pathID != pid) { otherPN.parent = pathNode; otherPN.pathID = pid; otherPN.cost = tmpCost; otherPN.H = path.CalculateHScore(other); other.UpdateG(path, otherPN); //Debug.Log ("G " + otherPN.G + " F " + otherPN.F); handler.PushNode(otherPN); //Debug.DrawRay ((Vector3)otherPN.node.Position, Vector3.up,Color.blue); } else { // Sorry for the huge number of #ifs //If not we can test if the path from the current node to this one is a better one then the one already used #if ASTAR_NO_TRAVERSAL_COST if (pathNode.G + tmpCost < otherPN.G) #else if (pathNode.G + tmpCost + path.GetTraversalCost(other) < otherPN.G) #endif { //Debug.Log ("Path better from " + NodeIndex + " to " + otherPN.node.NodeIndex + " " + (pathNode.G+tmpCost+path.GetTraversalCost(other)) + " < " + otherPN.G); otherPN.cost = tmpCost; otherPN.parent = pathNode; other.UpdateRecursiveG(path, otherPN, handler); //Or if the path from this node ("other") to the current ("current") is better } #if ASTAR_NO_TRAVERSAL_COST else if (otherPN.G + tmpCost < pathNode.G && other.ContainsConnection(this)) #else else if (otherPN.G + tmpCost + path.GetTraversalCost(this) < pathNode.G && other.ContainsConnection(this)) #endif { //Debug.Log ("Path better from " + otherPN.node.NodeIndex + " to " + NodeIndex + " " + (otherPN.G+tmpCost+path.GetTraversalCost (this)) + " < " + pathNode.G); pathNode.parent = otherPN; pathNode.cost = tmpCost; UpdateRecursiveG(path, pathNode, handler); } } } } #endif }
public override bool GetPortal(GraphNode other, List <Vector3> left, List <Vector3> right, bool backwards) { if (backwards) { return(true); } GridGraph gg = GetGridGraph(GraphIndex); int[] neighbourOffsets = gg.neighbourOffsets; GridNode[] nodes = gg.nodes; for (int i = 0; i < 4; i++) { if (HasConnectionInDirection(i) && other == nodes[NodeInGridIndex + neighbourOffsets[i]]) { Vector3 middle = ((Vector3)(position + other.position)) * 0.5f; Vector3 cross = Vector3.Cross(gg.collision.up, (Vector3)(other.position - position)); cross.Normalize(); cross *= gg.nodeSize * 0.5f; left.Add(middle - cross); right.Add(middle + cross); return(true); } } for (int i = 4; i < 8; i++) { if (HasConnectionInDirection(i) && other == nodes[NodeInGridIndex + neighbourOffsets[i]]) { bool rClear = false; bool lClear = false; if (HasConnectionInDirection(i - 4)) { GridNode n2 = nodes[NodeInGridIndex + neighbourOffsets[i - 4]]; if (n2.Walkable && n2.HasConnectionInDirection((i - 4 + 1) % 4)) { rClear = true; } } if (HasConnectionInDirection((i - 4 + 1) % 4)) { GridNode n2 = nodes[NodeInGridIndex + neighbourOffsets[(i - 4 + 1) % 4]]; if (n2.Walkable && n2.HasConnectionInDirection(i - 4)) { lClear = true; } } Vector3 middle = ((Vector3)(position + other.position)) * 0.5f; Vector3 cross = Vector3.Cross(gg.collision.up, (Vector3)(other.position - position)); cross.Normalize(); cross *= gg.nodeSize * 1.4142f; left.Add(middle - (lClear ? cross : Vector3.zero)); right.Add(middle + (rClear ? cross : Vector3.zero)); return(true); } } return(false); }
/** Executes a straight jump search. * \see http://en.wikipedia.org/wiki/Jump_point_search */ static GridNode JPSJumpStraight(GridNode node, Path path, PathHandler handler, int parentDir, int depth = 0) { GridGraph gg = GetGridGraph(node.GraphIndex); int[] neighbourOffsets = gg.neighbourOffsets; GridNode[] nodes = gg.nodes; GridNode origin = node; // Indexing into the cache arrays from multiple threads like this should cause // a lot of false sharing and cache trashing, but after profiling it seems // that this is not a major concern int threadID = handler.threadID; int threadOffset = 8 * handler.threadID; int cyclicParentDir = JPSCyclic[parentDir]; GridNode result = null; // Rotate 180 degrees const int forwardDir = 4; int forwardOffset = neighbourOffsets[JPSInverseCyclic[(forwardDir + cyclicParentDir) % 8]]; // Move forwards in the same direction // until a node is encountered which we either // * know the result for (memoization) // * is a special node (flag2 set) // * has custom connections // * the node has a forced neighbour // Then break out of the loop // and start another loop which goes through the same nodes and sets the // memoization caches to avoid expensive calls in the future while (true) { // This is needed to make sure different threads don't overwrite each others results // It doesn't matter if we throw away some caching done by other threads as this will only // happen during the first few path requests if (node.JPSLastCacheID == null || node.JPSLastCacheID.Length < handler.totalThreadCount) { lock (node) { // Check again in case another thread has already created the array if (node.JPSLastCacheID == null || node.JPSLastCacheID.Length < handler.totalThreadCount) { node.JPSCache = new GridNode[8 * handler.totalThreadCount]; node.JPSDead = new byte[handler.totalThreadCount]; node.JPSLastCacheID = new ushort[handler.totalThreadCount]; } } } if (node.JPSLastCacheID[threadID] != path.pathID) { for (int i = 0; i < 8; i++) { node.JPSCache[i + threadOffset] = null; } node.JPSLastCacheID[threadID] = path.pathID; node.JPSDead[threadID] = 0; } // Cache earlier results, major optimization // It is important to read from it once and then return the same result, // if we read from it twice, we might get different results due to other threads clearing the array sometimes GridNode cachedResult = node.JPSCache[parentDir + threadOffset]; if (cachedResult != null) { result = cachedResult; break; } if (((node.JPSDead[threadID] >> parentDir) & 1) != 0) { return(null); } // Special node (e.g end node), take care of if (handler.GetPathNode(node).flag2) { //Debug.Log ("Found end Node!"); //Debug.DrawRay ((Vector3)position, Vector3.up*2, Color.green); result = node; break; } #if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS // Special node which has custom connections, take care of if (node.connections != null && node.connections.Length > 0) { result = node; break; } #endif // These are the nodes this node is connected to, one bit for each of the 8 directions int noncyclic = node.gridFlags; //We don't actually need to & with this because we don't use the other bits. & 0xFF; int cyclic = 0; for (int i = 0; i < 8; i++) { cyclic |= ((noncyclic >> i) & 0x1) << JPSCyclic[i]; } int forced = 0; // Loop around to be able to assume -X is where we came from cyclic = ((cyclic >> cyclicParentDir) | ((cyclic << 8) >> cyclicParentDir)) & 0xFF; //for ( int i = 0; i < 8; i++ ) if ( ((cyclic >> i)&1) == 0 ) forced |= JPSForced[i]; if ((cyclic & (1 << 2)) == 0) { forced |= (1 << 3); } if ((cyclic & (1 << 6)) == 0) { forced |= (1 << 5); } int natural = JPSNaturalStraightNeighbours; // Check if there are any forced neighbours which we can reach that are not natural neighbours //if ( ((forced & cyclic) & (~(natural & cyclic))) != 0 ) { if ((forced & (~natural) & cyclic) != 0) { // Some of the neighbour nodes are forced result = node; break; } // Make sure we can reach the next node if ((cyclic & (1 << forwardDir)) != 0) { node = nodes[node.nodeInGridIndex + forwardOffset]; //Debug.DrawLine ( (Vector3)position + Vector3.up*0.2f*(depth), (Vector3)other.position + Vector3.up*0.2f*(depth+1), Color.magenta); } else { result = null; break; } } if (result == null) { while (origin != node) { origin.JPSDead[threadID] |= (byte)(1 << parentDir); origin = nodes[origin.nodeInGridIndex + forwardOffset]; } } else { while (origin != node) { origin.JPSCache[parentDir + threadOffset] = result; origin = nodes[origin.nodeInGridIndex + forwardOffset]; } } return(result); }
public static void SetGridGraph(int graphIndex, GridGraph graph) { if (GridNode._gridGraphs.Length <= graphIndex) { GridGraph[] array = new GridGraph[graphIndex + 1]; for (int i = 0; i < GridNode._gridGraphs.Length; i++) { array[i] = GridNode._gridGraphs[i]; } GridNode._gridGraphs = array; } GridNode._gridGraphs[graphIndex] = graph; }
protected override void DrawCutCorners (GridGraph graph) { // No corner cutting since only 4 neighbours are possible }
/** Draws settings for using a texture as source for a grid. * \astarpro */ public void DrawTextureData (GridGraph.TextureData data, GridGraph graph) { if (data == null) { return; } data.enabled = ToggleGroup ("Use Texture",data.enabled); //EditorGUILayout.Toggle ("Use Texture",data.enabled); //data.enabled = ToggleGroup ("Use Texture",data.enabled); if (!data.enabled) { return; } bool preGUI = GUI.enabled; GUI.enabled = data.enabled && GUI.enabled; EditorGUI.indentLevel++; //data.source = EditorGUILayout.ObjectField ("Source",data.source,typeof(Texture2D),false) as Texture2D; data.source = ObjectField ("Source",data.source,typeof(Texture2D),false) as Texture2D; if (data.source != null) { string path = AssetDatabase.GetAssetPath (data.source); if (path != "") { TextureImporter importer = AssetImporter.GetAtPath (path) as TextureImporter; if (!importer.isReadable) { if (FixLabel ("Texture is not readable")) { importer.isReadable = true; EditorUtility.SetDirty (importer); AssetDatabase.ImportAsset (path); } } } } for (int i=0;i<3;i++) { string channelName = i == 0 ? "R" : (i == 1 ? "G" : "B"); data.channels[i] = (GridGraph.TextureData.ChannelUse)EditorGUILayout.Popup (channelName, (int)data.channels[i], ChannelUseNames); if (data.channels[i] != GridGraph.TextureData.ChannelUse.None) { EditorGUI.indentLevel++; data.factors[i] = EditorGUILayout.FloatField ("Factor",data.factors[i]); string help = ""; switch (data.channels[i]) { case GridGraph.TextureData.ChannelUse.Penalty: help = "Nodes are applied penalty according to channel '"+channelName+"', multiplied with factor"; break; case GridGraph.TextureData.ChannelUse.Position: help = "Nodes Y position is changed according to channel '"+channelName+"', multiplied with factor"; if (graph.collision.heightCheck) { HelpBox ("Getting position both from raycast and from texture. You should disable one of them"); } break; case GridGraph.TextureData.ChannelUse.WalkablePenalty: help = "If channel '"+channelName+"' is 0, the node is made unwalkable. Otherwise the node is applied penalty multiplied with factor"; break; } HelpBox (help); EditorGUI.indentLevel--; } } if (GUILayout.Button ("Generate Reference")) { SaveReferenceTexture (graph); } GUI.enabled = preGUI; EditorGUI.indentLevel--; }
/// <summary> /// Finds all contours of a collection of nodes in a grid graph. /// /// <code> /// var grid = AstarPath.active.data.gridGraph; /// /// // Find all contours in the graph and draw them using debug lines /// GraphUtilities.GetContours(grid, vertices => { /// for (int i = 0; i < vertices.Length; i++) { /// Debug.DrawLine(vertices[i], vertices[(i+1)%vertices.Length], Color.red, 4); /// } /// }, 0); /// </code> /// /// In the image below you can see the contour of a graph. /// [Open online documentation to see images] /// /// In the image below you can see the contour of just a part of a grid graph (when the nodes parameter is supplied) /// [Open online documentation to see images] /// /// Contour of a hexagon graph /// [Open online documentation to see images] /// /// See: <see cref="GetContours(NavGraph)"/> /// </summary> /// <param name="grid">The grid to find the contours of</param> /// <param name="callback">The callback will be called once for every contour that is found with the vertices of the contour. The contour always forms a cycle.</param> /// <param name="yMergeThreshold">Contours will be simplified if the y coordinates for adjacent vertices differ by no more than this value.</param> /// <param name="nodes">Only these nodes will be searched. If this parameter is null then all nodes in the grid graph will be searched.</param> public static void GetContours(GridGraph grid, System.Action <Vector3[]> callback, float yMergeThreshold, GridNodeBase[] nodes = null) { // Set of all allowed nodes or null if all nodes are allowed HashSet <GridNodeBase> nodeSet = nodes != null ? new HashSet <GridNodeBase>(nodes) : null; // Use all nodes if the nodes parameter is null if (grid is LayerGridGraph) { nodes = nodes ?? (grid as LayerGridGraph).nodes; } nodes = nodes ?? grid.nodes; int[] neighbourXOffsets = grid.neighbourXOffsets; int[] neighbourZOffsets = grid.neighbourZOffsets; var neighbourIndices = grid.neighbours == NumNeighbours.Six ? GridGraph.hexagonNeighbourIndices : new [] { 0, 1, 2, 3 }; var offsetMultiplier = grid.neighbours == NumNeighbours.Six ? 1 / 3f : 0.5f; if (nodes != null) { var trace = ListPool <Vector3> .Claim(); var seenStates = new HashSet <int>(); for (int i = 0; i < nodes.Length; i++) { var startNode = nodes[i]; // The third check is a fast check for if the node has connections in all grid directions, if it has then we can skip processing it (unless the nodes parameter was used in which case we have to handle the edge cases) if (startNode != null && startNode.Walkable && (!startNode.HasConnectionsToAllEightNeighbours || nodeSet != null)) { for (int startDir = 0; startDir < neighbourIndices.Length; startDir++) { int startState = (startNode.NodeIndex << 4) | startDir; // Check if there is an obstacle in that direction var startNeighbour = startNode.GetNeighbourAlongDirection(neighbourIndices[startDir]); if ((startNeighbour == null || (nodeSet != null && !nodeSet.Contains(startNeighbour))) && !seenStates.Contains(startState)) { // Start tracing a contour here trace.ClearFast(); int dir = startDir; GridNodeBase node = startNode; while (true) { int state = (node.NodeIndex << 4) | dir; if (state == startState && trace.Count > 0) { break; } seenStates.Add(state); var neighbour = node.GetNeighbourAlongDirection(neighbourIndices[dir]); if (neighbour == null || (nodeSet != null && !nodeSet.Contains(neighbour))) { // Draw edge var d0 = neighbourIndices[dir]; dir = (dir + 1) % neighbourIndices.Length; var d1 = neighbourIndices[dir]; // Position in graph space of the vertex Vector3 graphSpacePos = new Vector3(node.XCoordinateInGrid + 0.5f, 0, node.ZCoordinateInGrid + 0.5f); // Offset along diagonal to get the correct XZ coordinates graphSpacePos.x += (neighbourXOffsets[d0] + neighbourXOffsets[d1]) * offsetMultiplier; graphSpacePos.z += (neighbourZOffsets[d0] + neighbourZOffsets[d1]) * offsetMultiplier; graphSpacePos.y = grid.transform.InverseTransform((Vector3)node.position).y; if (trace.Count >= 2) { var v0 = trace[trace.Count - 2]; var v1 = trace[trace.Count - 1]; var v1d = v1 - v0; var v2d = graphSpacePos - v0; // Replace the previous point if it is colinear with the point just before it and just after it (the current point), because that point wouldn't add much information, but it would add CPU overhead if (((Mathf.Abs(v1d.x) > 0.01f || Mathf.Abs(v2d.x) > 0.01f) && (Mathf.Abs(v1d.z) > 0.01f || Mathf.Abs(v2d.z) > 0.01f)) || (Mathf.Abs(v1d.y) > yMergeThreshold || Mathf.Abs(v2d.y) > yMergeThreshold)) { trace.Add(graphSpacePos); } else { trace[trace.Count - 1] = graphSpacePos; } } else { trace.Add(graphSpacePos); } } else { // Move node = neighbour; dir = (dir + neighbourIndices.Length / 2 + 1) % neighbourIndices.Length; } } // Simplify the contour a bit around the start point. // Otherwise we might return a cycle which was not as simplified as possible and the number of vertices // would depend on where in the cycle the algorithm started to traverse the contour. if (trace.Count >= 3) { var v0 = trace[trace.Count - 2]; var v1 = trace[trace.Count - 1]; var v1d = v1 - v0; var v2d = trace[0] - v0; // Replace the previous point if it is colinear with the point just before it and just after it (the current point), because that point wouldn't add much information, but it would add CPU overhead if (!(((Mathf.Abs(v1d.x) > 0.01f || Mathf.Abs(v2d.x) > 0.01f) && (Mathf.Abs(v1d.z) > 0.01f || Mathf.Abs(v2d.z) > 0.01f)) || (Mathf.Abs(v1d.y) > yMergeThreshold || Mathf.Abs(v2d.y) > yMergeThreshold))) { trace.RemoveAt(trace.Count - 1); } } if (trace.Count >= 3) { var v0 = trace[trace.Count - 1]; var v1 = trace[0]; var v1d = v1 - v0; var v2d = trace[1] - v0; // Replace the previous point if it is colinear with the point just before it and just after it (the current point), because that point wouldn't add much information, but it would add CPU overhead if (!(((Mathf.Abs(v1d.x) > 0.01f || Mathf.Abs(v2d.x) > 0.01f) && (Mathf.Abs(v1d.z) > 0.01f || Mathf.Abs(v2d.z) > 0.01f)) || (Mathf.Abs(v1d.y) > yMergeThreshold || Mathf.Abs(v2d.y) > yMergeThreshold))) { trace.RemoveAt(0); } } var result = trace.ToArray(); grid.transform.Transform(result); callback(result); } } } } ListPool <Vector3> .Release(ref trace); } }
/** Updates shortcuts to the first graph of different types. * Hard coding references to some graph types is not really a good thing imo. I want to keep it dynamic and flexible. * But these references ease the use of the system, so I decided to keep them. It is the only reference to specific graph types in the pathfinding core.\n */ public void UpdateShortcuts () { navmesh = (NavMeshGraph)FindGraphOfType (typeof(NavMeshGraph)); gridGraph = (GridGraph)FindGraphOfType (typeof(GridGraph)); pointGraph = (PointGraph)FindGraphOfType (typeof(PointGraph)); recastGraph = (RecastGraph)FindGraphOfType (typeof(RecastGraph)); }
/** Updates shortcuts to the first graph of different types. * Hard coding references to some graph types is not really a good thing imo. I want to keep it dynamic and flexible. * But these references ease the use of the system, so I decided to keep them. It is the only reference to specific graph types in the pathfinding core.\n */ public void UpdateShortcuts () { navmesh = (NavMeshGraph)FindGraphOfType (typeof(NavMeshGraph)); #if !ASTAR_NO_GRID_GRAPH gridGraph = (GridGraph)FindGraphOfType (typeof(GridGraph)); #endif #if !ASTAR_NO_POINT_GRAPH pointGraph = (PointGraph)FindGraphOfType (typeof(PointGraph)); #endif recastGraph = (RecastGraph)FindGraphOfType (typeof(RecastGraph)); }