public void Start () { if ( AstarPath.active == null ) throw new System.Exception ("There is no AstarPath object in the scene"); graph = AstarPath.active.astarData.gridGraph; if ( graph == null ) throw new System.Exception ("The AstarPath object has no GridGraph"); UpdateGraph (); }
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); } }
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); }
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); } }
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 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); }
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 ); } }
/** Draws settings for using a texture as source for a grid. * \astarpro */ protected virtual void DrawTextureData (GridGraph.TextureData data, GridGraph graph) { if (data == null) { return; } 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 = ObjectField ("Source",data.source,typeof(Texture2D),false) as Texture2D; if (data.source != null) { string path = AssetDatabase.GetAssetPath (data.source); if (path != "") { var 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 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 } }
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); } }
protected override void DrawCutCorners (GridGraph graph) { // No corner cutting since only 4 neighbours are possible }
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(); }
protected override void DrawTextureData (GridGraph.TextureData data, GridGraph graph) { // No texture data for layered grid graphs }
void DrawLastSection (GridGraph graph) { GUILayout.Label (new GUIContent ("Advanced"), EditorStyles.boldLabel); DrawPenaltyModifications (graph); DrawJPS (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--; } DrawTextureData (graph.textureData, graph); EditorGUI.indentLevel-=2; } }
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 (); }
static void SaveReferenceTexture (GridGraph graph) { 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; } var 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; } var 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 (var outstream = new System.IO.StreamWriter (path)) { using (var outfile = new System.IO.BinaryWriter (outstream.BaseStream)) { outfile.Write (tex.EncodeToPNG ()); } } AssetDatabase.Refresh (); Object obj = AssetDatabase.LoadAssetAtPath (path,typeof (Texture)); EditorGUIUtility.PingObject (obj); }
protected virtual void DrawMiddleSection (GridGraph graph) { DrawNeighbours(graph); DrawMaxClimb(graph); DrawMaxSlope(graph); DrawErosion(graph); }
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 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); } }
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--; if ( graph.neighbours != NumNeighbours.Eight && graph.useJumpPointSearch ) { EditorGUILayout.HelpBox ("Jump Point Search only works for 8 neighbours.", MessageType.Error); } }
protected override void DrawJPS (GridGraph graph) { // No JPS for layered grid graph }