Example #1
0
        public override void OnInspectorGUI()
        {
            DrawDefaultInspector();

            var script = target as Seeker;

            Undo.RecordObject(script, "modify settings on Seeker");

            // Show a dropdown selector for the tags that this seeker can traverse
            // A callback is necessary because Unity's GenericMenu uses callbacks
            EditorGUILayoutx.TagMaskField(new GUIContent("Valid Tags"), script.traversableTags, result => script.traversableTags = result);

            EditorGUI.indentLevel = 0;
            tagPenaltiesOpen      = EditorGUILayout.Foldout(tagPenaltiesOpen, new GUIContent("Tag Penalties", "Penalties for each tag"));
            if (tagPenaltiesOpen)
            {
                EditorGUI.indentLevel = 2;
                string[] tagNames = AstarPath.FindTagNames();
                for (int i = 0; i < script.tagPenalties.Length; i++)
                {
                    int tmp = EditorGUILayout.IntField((i < tagNames.Length ? tagNames[i] : "Tag " + i), (int)script.tagPenalties[i]);
                    if (tmp < 0)
                    {
                        tmp = 0;
                    }

                    // If the new value is different than the old one
                    // Update the value and mark the script as dirty
                    if (script.tagPenalties[i] != tmp)
                    {
                        script.tagPenalties[i] = tmp;
                        EditorUtility.SetDirty(target);
                    }
                }
                if (GUILayout.Button("Edit Tag Names..."))
                {
                    AstarPathEditor.EditTags();
                }
            }
            EditorGUI.indentLevel = 1;

            if (GUI.changed)
            {
                EditorUtility.SetDirty(target);
            }
        }
Example #2
0
        public override void OnInspectorGUI(NavGraph target)
        {
            var graph = target as PointGraph;

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

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

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

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

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

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

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

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

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

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

            EditorGUILayout.Popup(new GUIContent("Nearest node queries find closest"), 0, nearestNodeDistanceModeLabels);
        }
Example #3
0
        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--;
            }
        }
Example #4
0
        void DrawTagField()
        {
            if (PropertyField("modifyTag"))
            {
                var tagValue = FindProperty("setTag");
                EditorGUI.indentLevel++;
                EditorGUI.showMixedValue = tagValue.hasMultipleDifferentValues;
                EditorGUI.BeginChangeCheck();
                var newTag = EditorGUILayoutx.TagField("Tag Value", tagValue.intValue);
                if (EditorGUI.EndChangeCheck())
                {
                    tagValue.intValue = newTag;
                }

                if (GUILayout.Button("Tags can be used to restrict which units can walk on what ground. Click here for more info", "HelpBox"))
                {
                    Application.OpenURL(AstarUpdateChecker.GetURL("tags"));
                }
                EditorGUI.indentLevel--;
            }
        }
Example #5
0
        void DrawTagField()
        {
            EditorGUILayout.PropertyField(modifyTag, new GUIContent("Modify Tag", "Should the tags of the nodes be modified"));
            if (!modifyTag.hasMultipleDifferentValues && modifyTag.boolValue)
            {
                EditorGUI.indentLevel++;
                EditorGUI.showMixedValue = tagValue.hasMultipleDifferentValues;
                EditorGUI.BeginChangeCheck();
                var newTag = EditorGUILayoutx.TagField("Tag Value", tagValue.intValue);
                if (EditorGUI.EndChangeCheck())
                {
                    tagValue.intValue = newTag;
                }
                EditorGUI.indentLevel--;
            }

            if (GUILayout.Button("Tags can be used to restrict which units can walk on what ground. Click here for more info", "HelpBox"))
            {
                Application.OpenURL(AstarUpdateChecker.GetURL("tags"));
            }
        }
        //public GameObject meshRenderer;

        public override void OnInspectorGUI(NavGraph target)
        {
            NavMeshGraph graph = target as NavMeshGraph;

            /*
             #if UNITY_3_3
             *              graph.sourceMesh = EditorGUILayout.ObjectField ("Source Mesh",graph.sourceMesh,typeof(Mesh)) as Mesh;
             #else
             *              graph.sourceMesh = EditorGUILayout.ObjectField ("Source Mesh",graph.sourceMesh,typeof(Mesh), true) as Mesh;
             #endif
             */
            graph.sourceMesh = ObjectField("Source Mesh", graph.sourceMesh, typeof(Mesh), false) as Mesh;

        #if UNITY_LE_4_3
            EditorGUIUtility.LookLikeControls();
            EditorGUILayoutx.BeginIndent();
        #endif
            graph.offset = EditorGUILayout.Vector3Field("Offset", graph.offset);

        #if UNITY_LE_4_3
            EditorGUILayoutx.EndIndent();

            EditorGUILayoutx.BeginIndent();
        #endif
            graph.rotation = EditorGUILayout.Vector3Field("Rotation", graph.rotation);

        #if UNITY_LE_4_3
            EditorGUILayoutx.EndIndent();
            EditorGUIUtility.LookLikeInspector();
        #endif

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

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

            bool preEnabled = GUI.enabled;

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

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

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

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

            GUI.enabled = false;
            EditorGUILayout.LabelField(new GUIContent("Width (voxels)", "Based on the cell size and the bounding box"), new GUIContent(estWidth.ToString()));

            EditorGUILayout.LabelField(new GUIContent("Depth (voxels)", "Based on the cell size and the bounding box"), new GUIContent(estDepth.ToString()));
            GUI.enabled = preEnabled;

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

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

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

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

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

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

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

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

            if (graph.characterRadius < graph.cellSize * 2)
            {
                EditorGUILayout.HelpBox("For best navmesh quality, it is recommended to keep the character radius at least 2 times as large as the cell size. Smaller cell sizes will give you higher quality navmeshes, but it will take more time to scan the graph.", MessageType.Warning);
            }

            graph.maxSlope      = EditorGUILayout.Slider(new GUIContent("Max Slope", "Approximate maximum slope"), graph.maxSlope, 0F, 90F);
            graph.maxEdgeLength = EditorGUILayout.FloatField(new GUIContent("Max Border Edge Length", "Maximum length of one border edge in the completed navmesh before it is split. A lower value can often yield better quality graphs, but don't use so low values so that you get a lot of thin triangles."), graph.maxEdgeLength);
            graph.maxEdgeLength = graph.maxEdgeLength < graph.cellSize ? graph.cellSize : graph.maxEdgeLength;

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

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

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

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

            if (graph.rasterizeMeshes && graph.rasterizeColliders)
            {
                EditorGUILayout.HelpBox("You are rasterizing both meshes and colliders, this might just be duplicating the work that is done if the colliders and meshes are similar in shape. You can use the RecastMeshObj component" +
                                        " to always include some specific objects regardless of what the above settings are set to.", MessageType.Info);
            }

            Separator();

            graph.forcedBoundsCenter = EditorGUILayout.Vector3Field("Center", graph.forcedBoundsCenter);
            graph.forcedBoundsSize   = EditorGUILayout.Vector3Field("Size", graph.forcedBoundsSize);
            // Make sure the bounding box is not infinitely thin along any axis
            graph.forcedBoundsSize = Vector3.Max(graph.forcedBoundsSize, Vector3.one * 0.001f);
            graph.rotation         = EditorGUILayout.Vector3Field("Rotation", graph.rotation);

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

            Separator();

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

            graph.enableNavmeshCutting = EditorGUILayout.Toggle(new GUIContent("Affected by navmesh cuts", "Makes this graph affected by NavmeshCut and NavmeshAdd components. See the documentation for more info."), graph.enableNavmeshCutting);

            Separator();

            GUILayout.BeginHorizontal();
            GUILayout.Space(18);
            graph.showMeshSurface     = GUILayout.Toggle(graph.showMeshSurface, new GUIContent("Show surface", "Toggles gizmos for drawing the surface of the mesh"), EditorStyles.miniButtonLeft);
            graph.showMeshOutline     = GUILayout.Toggle(graph.showMeshOutline, new GUIContent("Show outline", "Toggles gizmos for drawing an outline of the nodes"), EditorStyles.miniButtonMid);
            graph.showNodeConnections = GUILayout.Toggle(graph.showNodeConnections, new GUIContent("Show connections", "Toggles gizmos for drawing node connections"), EditorStyles.miniButtonRight);
            GUILayout.EndHorizontal();


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

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

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

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

            if (graph.nearestSearchOnlyXZ && (Mathf.Abs(graph.rotation.x) > 1 || Mathf.Abs(graph.rotation.z) > 1))
            {
                EditorGUILayout.HelpBox("Nearest node queries in XZ space is not recommended for rotated graphs since XZ space no longer corresponds to the ground plane", MessageType.Warning);
            }
        }
        /// <summary>Draws the inspector for a \link Pathfinding.GraphCollision GraphCollision class \endlink</summary>
        protected virtual void DrawCollisionEditor(GraphCollision collision)
        {
            collision = collision ?? new GraphCollision();

            DrawUse2DPhysics(collision);

            collision.collisionCheck = ToggleGroup("Collision testing", collision.collisionCheck);
            if (collision.collisionCheck)
            {
                string[] colliderOptions = collision.use2D ? new [] { "Circle", "Point" } : new [] { "Sphere", "Capsule", "Ray" };
                int[]    colliderValues  = collision.use2D ? new [] { 0, 2 } : new [] { 0, 1, 2 };
                // In 2D the Circle (Sphere) mode will replace both the Sphere and the Capsule modes
                // However make sure that the original value is still stored in the grid graph in case the user changes back to the 3D mode in the inspector.
                var tp = collision.type;
                if (tp == ColliderType.Capsule && collision.use2D)
                {
                    tp = ColliderType.Sphere;
                }
                EditorGUI.BeginChangeCheck();
                tp = (ColliderType)EditorGUILayout.IntPopup("Collider type", (int)tp, colliderOptions, colliderValues);
                if (EditorGUI.EndChangeCheck())
                {
                    collision.type = tp;
                }

                // Only spheres and capsules have a diameter
                if (collision.type == ColliderType.Capsule || collision.type == ColliderType.Sphere)
                {
                    collision.diameter = EditorGUILayout.FloatField(new GUIContent("Diameter", "Diameter of the capsule or sphere. 1 equals one node width"), collision.diameter);
                }

                if (!collision.use2D)
                {
                    if (collision.type == ColliderType.Capsule || collision.type == ColliderType.Ray)
                    {
                        collision.height = EditorGUILayout.FloatField(new GUIContent("Height/Length", "Height of cylinder or length of ray in world units"), collision.height);
                    }

                    collision.collisionOffset = EditorGUILayout.FloatField(new GUIContent("Offset", "Offset upwards from the node. Can be used so that obstacles can be used as ground and at the same time as obstacles for lower positioned nodes"), collision.collisionOffset);
                }

                collision.mask = EditorGUILayoutx.LayerMaskField("Obstacle Layer Mask", collision.mask);
            }

            GUILayout.Space(2);

            if (collision.use2D)
            {
                EditorGUI.BeginDisabledGroup(collision.use2D);
                ToggleGroup("Height testing", false);
                EditorGUI.EndDisabledGroup();
            }
            else
            {
                collision.heightCheck = ToggleGroup("Height testing", collision.heightCheck);
                if (collision.heightCheck)
                {
                    collision.fromHeight = EditorGUILayout.FloatField(new GUIContent("Ray length", "The height from which to check for ground"), collision.fromHeight);

                    collision.heightMask = EditorGUILayoutx.LayerMaskField("Mask", collision.heightMask);

                    collision.thickRaycast = EditorGUILayout.Toggle(new GUIContent("Thick Raycast", "Use a thick line instead of a thin line"), collision.thickRaycast);

                    if (collision.thickRaycast)
                    {
                        EditorGUI.indentLevel++;
                        collision.thickRaycastDiameter = EditorGUILayout.FloatField(new GUIContent("Diameter", "Diameter of the thick raycast"), collision.thickRaycastDiameter);
                        EditorGUI.indentLevel--;
                    }

                    collision.unwalkableWhenNoGround = EditorGUILayout.Toggle(new GUIContent("Unwalkable when no ground", "Make nodes unwalkable when no ground was found with the height raycast. If height raycast is turned off, this doesn't affect anything"), collision.unwalkableWhenNoGround);
                }
            }
        }
Example #9
0
        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;
            }
        }
Example #10
0
        /** Draws the inspector for a \link Pathfinding.GraphCollision GraphCollision class \endlink */
        public void DrawCollisionEditor(GraphCollision collision)
        {
            if (collision == null)
            {
                collision = new GraphCollision();
            }

            /*GUILayout.Space (5);
            *  Rect r = EditorGUILayout.BeginVertical (AstarPathEditor.graphBoxStyle);
            *  GUI.Box (r,"",AstarPathEditor.graphBoxStyle);
            *  GUILayout.Space (2);*/
            Separator();

            collision.use2D = EditorGUILayout.Toggle(new GUIContent("Use 2D Physics", "Use the Physics2D API for collision checking"), collision.use2D);

#if UNITY_LE_4_3
            if (collision.use2D)
            {
                EditorGUILayout.HelpBox("2D Physics is only supported from Unity 4.3 and up", MessageType.Error);
            }
#endif

            /*GUILayout.BeginHorizontal ();
             * GUIStyle boxHeader = AstarPathEditor.astarSkin.FindStyle ("CollisionHeader");
             * GUILayout.Label ("Collision testing",boxHeader);
             * collision.collisionCheck = GUILayout.Toggle (collision.collisionCheck,"");
             *
             * bool preEnabledRoot = GUI.enabled;
             * GUI.enabled = collision.collisionCheck;
             * GUILayout.EndHorizontal ();*/
            collision.collisionCheck = ToggleGroup("Collision testing", collision.collisionCheck);
            bool preEnabledRoot = GUI.enabled;
            GUI.enabled = collision.collisionCheck;

            //GUILayout.BeginHorizontal ();
            collision.type = (ColliderType)EditorGUILayout.EnumPopup("Collider type", collision.type);
            //new string[3] {"Sphere","Capsule","Ray"}

            bool preEnabled = GUI.enabled;
            if (collision.type != ColliderType.Capsule && collision.type != ColliderType.Sphere)
            {
                GUI.enabled = false;
            }
            collision.diameter = EditorGUILayout.FloatField(new GUIContent("Diameter", "Diameter of the capsule or sphere. 1 equals one node width"), collision.diameter);

            GUI.enabled = preEnabled;

            if (collision.type != ColliderType.Capsule && collision.type != ColliderType.Ray)
            {
                GUI.enabled = false;
            }
            collision.height = EditorGUILayout.FloatField(new GUIContent("Height/Length", "Height of cylinder or length of ray in world units"), collision.height);
            GUI.enabled      = preEnabled;

            collision.collisionOffset = EditorGUILayout.FloatField(new GUIContent("Offset", "Offset upwards from the node. Can be used so that obstacles can be used as ground and at the same time as obstacles for lower positioned nodes"), collision.collisionOffset);

            //collision.mask = 1 << EditorGUILayout.LayerField ("Mask",Mathf.Clamp ((int)Mathf.Log (collision.mask,2),0,31));

            collision.mask = EditorGUILayoutx.LayerMaskField("Mask", collision.mask);

            GUILayout.Space(2);


            GUI.enabled = preEnabledRoot;

            if (collision.use2D)
            {
                GUI.enabled = false;
            }

            collision.heightCheck = ToggleGroup("Height testing", collision.heightCheck);
            GUI.enabled           = collision.heightCheck && GUI.enabled;

            /*GUILayout.BeginHorizontal ();
             * GUILayout.Label ("Height testing",boxHeader);
             * collision.heightCheck = GUILayout.Toggle (collision.heightCheck,"");
             * GUI.enabled = collision.heightCheck;
             * GUILayout.EndHorizontal ();*/

            collision.fromHeight = EditorGUILayout.FloatField(new GUIContent("Ray length", "The height from which to check for ground"), collision.fromHeight);

            collision.heightMask = EditorGUILayoutx.LayerMaskField("Mask", collision.heightMask);
            //collision.heightMask = 1 << EditorGUILayout.LayerField ("Mask",Mathf.Clamp ((int)Mathf.Log (collision.heightMask,2),0,31));

            collision.thickRaycast = EditorGUILayout.Toggle(new GUIContent("Thick Raycast", "Use a thick line instead of a thin line"), collision.thickRaycast);

            editor.GUILayoutx.BeginFadeArea(collision.thickRaycast, "thickRaycastDiameter");

            if (editor.GUILayoutx.DrawID("thickRaycastDiameter"))
            {
                EditorGUI.indentLevel++;
                collision.thickRaycastDiameter = EditorGUILayout.FloatField(new GUIContent("Diameter", "Diameter of the thick raycast"), collision.thickRaycastDiameter);
                EditorGUI.indentLevel--;
            }

            editor.GUILayoutx.EndFadeArea();

            collision.unwalkableWhenNoGround = EditorGUILayout.Toggle(new GUIContent("Unwalkable when no ground", "Make nodes unwalkable when no ground was found with the height raycast. If height raycast is turned off, this doesn't affect anything"), collision.unwalkableWhenNoGround);

            GUI.enabled = preEnabledRoot;

            //GUILayout.Space (2);
            //EditorGUILayout.EndVertical ();
            //GUILayout.Space (5);
        }
Example #11
0
        public override void OnInspectorGUI(NavGraph target)
        {
            RecastGraph graph = target as RecastGraph;

            bool preEnabled = GUI.enabled;

            //if (graph.forceBounds) {

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

            if (estWidth * estDepth >= 1024 * 1024 || estDepth >= 1024 * 1024 || estWidth >= 1024 * 1024)
            {
                GUIStyle helpBox = GUI.skin.FindStyle("HelpBox");
                if (helpBox == null)
                {
                    helpBox = GUI.skin.FindStyle("Box");
                }

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

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

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

            EditorGUILayout.LabelField("Depth (samples)", estDepth.ToString());

            /*} else {
             *      GUI.enabled = false;
             *      EditorGUILayout.LabelField ("Width (samples)","undetermined");
             *      EditorGUILayout.LabelField ("Depth (samples)","undetermined");
             * }*/
            GUI.enabled = preEnabled;

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

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

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

            EditorGUI.BeginDisabledGroup(!graph.useTiles);

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

            EditorGUI.EndDisabledGroup();

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

            graph.walkableHeight  = EditorGUILayout.FloatField(new GUIContent("Walkable Height", "Minimum distance to the roof for an area to be walkable"), graph.walkableHeight);
            graph.walkableClimb   = EditorGUILayout.FloatField(new GUIContent("Walkable Climb", "How high can the character climb"), graph.walkableClimb);
            graph.characterRadius = EditorGUILayout.FloatField(new GUIContent("Character Radius", "Radius of the character, it's good to add some margin though"), graph.characterRadius);

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

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

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

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

            graph.rasterizeMeshes    = EditorGUILayout.Toggle(new GUIContent("Rasterize Meshes", "Should meshes be rasterized and used for building the navmesh"), graph.rasterizeMeshes);
            graph.rasterizeColliders = EditorGUILayout.Toggle(new GUIContent("Rasterize Colliders", "Should colliders be rasterized and used for building the navmesh"), graph.rasterizeColliders);
            if (graph.rasterizeColliders)
            {
                EditorGUI.indentLevel++;
                graph.colliderRasterizeDetail = EditorGUILayout.FloatField(new GUIContent("Collider Detail", "Controls the detail of the generated collider meshes. Increasing does not necessarily yield better navmeshes, but lowering will speed up scan"), graph.colliderRasterizeDetail);
                EditorGUI.indentLevel--;
            }


            Separator();

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

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

            Separator();

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

            Separator();

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

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


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

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

            graph.nearestSearchOnlyXZ = EditorGUILayout.Toggle(new GUIContent("Nearest node queries in XZ space",
                                                                              "Recomended for single-layered environments.\nFaster but can be inacurate esp. in multilayered contexts."), graph.nearestSearchOnlyXZ);
            //graph.mask = 1 << EditorGUILayout.LayerField ("Mask",(int)Mathf.Log (graph.mask,2));
        }
Example #12
0
        public override void OnInspectorGUI(NavGraph target)
        {
            PointGraph graph = target as PointGraph;

            /*
             #if UNITY_3_3
             *              graph.root = (Transform)EditorGUILayout.ObjectField (new GUIContent ("Root","All childs of this object will be used as nodes, if it is not set, a tag search will be used instead (see below)"),graph.root,typeof(Transform));
             #else
             *              graph.root = (Transform)EditorGUILayout.ObjectField (new GUIContent ("Root","All childs of this object will be used as nodes, if it is not set, a tag search will be used instead (see below)"),graph.root,typeof(Transform),true);
             #endif
             */
            //Debug.Log (EditorGUI.indentLevel);

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

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

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

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

        #if UNITY_LE_4_3
            EditorGUIUtility.LookLikeControls();
        #endif
        #if UNITY_4
            graph.limits = EditorGUILayout.Vector3Field("Max Distance (axis aligned)", graph.limits);
        #else
            EditorGUILayoutx.BeginIndent();
            graph.limits = EditorGUILayout.Vector3Field("Max Distance (axis aligned)", graph.limits);
            EditorGUILayoutx.EndIndent();
        #endif
        #if UNITY_LE_4_3
            EditorGUIUtility.LookLikeInspector();
        #endif

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

            //EditorGUILayoutx.FadeArea fade = editor.GUILayoutx.BeginFadeArea (graph.raycast,"raycast");
            //if ( fade.Show () ) {
            if (graph.raycast)
            {
                EditorGUI.indentLevel++;

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

                //editor.GUILayoutx.BeginFadeArea (graph.thickRaycast,"thickRaycast");
                if (graph.thickRaycast)
                {
                    graph.thickRaycastRadius = EditorGUILayout.FloatField(new GUIContent("Raycast Radius", "The radius in world units for the thick raycast"), graph.thickRaycastRadius);
                }
                //editor.GUILayoutx.EndFadeArea ();

                //graph.mask = 1 << EditorGUILayout.LayerField ("Mask",(int)Mathf.Log (graph.mask,2));
                graph.mask = EditorGUILayoutx.LayerMaskField(/*new GUIContent (*/ "Mask" /*,"Used to mask which layers should be checked")*/, graph.mask);
                EditorGUI.indentLevel--;
            }

            //editor.GUILayoutx.EndFadeArea ();
        }
        /** 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);
                        }
                    }
                }
            }

            string[] channelNames = { "R", "G", "B", "A", "All" };
            for (int i = 0; i < data.channels.Length; i++)
            {
                string channelName = channelNames[i];
                data.channels[i] = (GridGraph.TextureData.ChannelUse)EditorGUILayout.Popup(channelName, (int)data.channels[i], ChannelUseNames);

                if (data.channels[i] != GridGraph.TextureData.ChannelUse.None)
                {
                    EditorGUI.indentLevel++;

                    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;

                    case GridGraph.TextureData.ChannelUse.Tag:
                        help = "factor need be AstarPath.TagNames";
                        break;
                    }
                    if (data.channels[i] != GridGraph.TextureData.ChannelUse.Tag)
                    {
                        data.factors[i] = EditorGUILayout.FloatField("Factor", data.factors[i]);
                    }
                    else
                    {
                        data.factors[i] = EditorGUILayoutx.TagField("Tag Value", (int)data.factors[i]);
                    }
                    HelpBox(help);

                    EditorGUI.indentLevel--;
                }
            }

            if (GUILayout.Button("Generate Reference"))
            {
                SaveReferenceTexture(graph);
            }

            GUI.enabled = preGUI;
            EditorGUI.indentLevel--;
        }
        /** Draws the inspector for a \link Pathfinding.GraphCollision GraphCollision class \endlink */
        public void DrawCollisionEditor(GraphCollision collision)
        {
            if (collision == null)
            {
                collision = new GraphCollision();
            }

            Separator();

            collision.use2D = EditorGUILayout.Toggle(new GUIContent("Use 2D Physics", "Use the Physics2D API for collision checking"), collision.use2D);

            collision.collisionCheck = ToggleGroup("Collision testing", collision.collisionCheck);
            bool preEnabledRoot = GUI.enabled;

            GUI.enabled = collision.collisionCheck;

            collision.type = (ColliderType)EditorGUILayout.EnumPopup("Collider type", collision.type);

            bool preEnabled = GUI.enabled;

            if (collision.type != ColliderType.Capsule && collision.type != ColliderType.Sphere)
            {
                GUI.enabled = false;
            }
            collision.diameter = EditorGUILayout.FloatField(new GUIContent("Diameter", "Diameter of the capsule or sphere. 1 equals one node width"), collision.diameter);

            GUI.enabled = preEnabled;

            if (collision.type != ColliderType.Capsule && collision.type != ColliderType.Ray)
            {
                GUI.enabled = false;
            }
            collision.height = EditorGUILayout.FloatField(new GUIContent("Height/Length", "Height of cylinder or length of ray in world units"), collision.height);
            GUI.enabled      = preEnabled;

            collision.collisionOffset = EditorGUILayout.FloatField(new GUIContent("Offset", "Offset upwards from the node. Can be used so that obstacles can be used as ground and at the same time as obstacles for lower positioned nodes"), collision.collisionOffset);

            collision.mask = EditorGUILayoutx.LayerMaskField("Mask", collision.mask);

            GUILayout.Space(2);


            GUI.enabled = preEnabledRoot;

            if (collision.use2D)
            {
                GUI.enabled = false;
            }

            collision.heightCheck = ToggleGroup("Height testing", collision.heightCheck);
            GUI.enabled           = collision.heightCheck && GUI.enabled;

            collision.fromHeight = EditorGUILayout.FloatField(new GUIContent("Ray length", "The height from which to check for ground"), collision.fromHeight);

            collision.heightMask = EditorGUILayoutx.LayerMaskField("Mask", collision.heightMask);

            collision.thickRaycast = EditorGUILayout.Toggle(new GUIContent("Thick Raycast", "Use a thick line instead of a thin line"), collision.thickRaycast);

            editor.GUILayoutx.BeginFadeArea(collision.thickRaycast, "thickRaycastDiameter");

            if (editor.GUILayoutx.DrawID("thickRaycastDiameter"))
            {
                EditorGUI.indentLevel++;
                collision.thickRaycastDiameter = EditorGUILayout.FloatField(new GUIContent("Diameter", "Diameter of the thick raycast"), collision.thickRaycastDiameter);
                EditorGUI.indentLevel--;
            }

            editor.GUILayoutx.EndFadeArea();

            collision.unwalkableWhenNoGround = EditorGUILayout.Toggle(new GUIContent("Unwalkable when no ground", "Make nodes unwalkable when no ground was found with the height raycast. If height raycast is turned off, this doesn't affect anything"), collision.unwalkableWhenNoGround);

            GUI.enabled = preEnabledRoot;
        }