Other ways to add or ignore surfaces without colliders

Hello! Im making a 2D tile-based game with a very large (1500x1300px) map. Putting a collider on a tilemap of this size considerably slows the editor down where its basically unusable.

Can i instead have the a* scanner check if a tilemap contains a tile instead of putting a collider on it and doing collision testing? Is there support for custom scanning like this? For example, i have a water tilemap for collision and another for ground that is walkable. I want to check the collision tilemap for the tile, if it has it at the node, then it will not cache the node as walkable.

Also, i noticed that the scanner will not recognize a PolygonCollider2D set to “used by composite” with a CompositeCollider2D, which also slows down performance by a lot because of all the extra vertices on the collider. Is this intentional or am i missing a setting?

So i found the tut to make our own grid graphs, i tried making my own and i keep getting either this error in the screenie or one at GetGridGraph and i have no idea why. I basically copied the tutorial PolarGraph script and use GridNodes instead

here is the full script:

using System.Collections.Generic;
using UnityEngine;
using Pathfinding;
using Pathfinding.Serialization;
using Pathfinding.Util;
using UnityEngine.Tilemaps;

#if UNITY_EDITOR
using UnityEditor;
#endif

namespace Mapping
{
    // Inherit our new graph from the base graph type
    [JsonOptIn]
    // Make sure the class is not stripped out when using code stripping (see https://docs.unity3d.com/Manual/ManagedCodeStripping.html)
    [Pathfinding.Util.Preserve]
    public class TileGraph : NavGraph
    {
        [JsonMember]
        public int Width = 100;
        [JsonMember]
        public int Height = 100;

        [JsonMember]
        public Vector3 Center = Vector3.zero;

        [JsonMember]
        public float Scale = 1;

        public PointNode[] Nodes;

        private PointNode CreateNode(Vector3 pos)
        {
            PointNode node = new PointNode(active);
            node.position = (Int3)pos;
            return node;
        }

        protected override IEnumerable<Pathfinding.Progress> ScanInternal()
        {
            PointNode[][] nodes = new PointNode[Width][];

            yield return new Pathfinding.Progress(0.25f, "Creating nodes");

            // create all nodes
            for (int x = 0; x < Width; x++)
            {
                nodes[x] = new PointNode[Height];

                for (int y = 0; y < Height; y++)
                {
                    nodes[x][y] = CreateNode(new Vector3(x, y));
                }
            }

            yield return new Pathfinding.Progress(0.50f, "Creating nodes");

            // create connections
            for (int x = 0; x < Width; x++)
            {
                for (int y = 0; y < Height; y++)
                {
                    var node = nodes[x][y];

                    Connection[] conns = new Connection[8];

                    // neighbors
                    int i = -1;
                    for (int nX = -1; nX <= 1; nX++)
                    {
                        for (int nY = -1; nY <= 1; nY++)
                        {
                            // skip center node since node cannot be its own neighbor
                            if (nX == 0 && nY == 0) continue;
                            i++;

                            try
                            {
                                var neighbor = nodes[x + nX][y + nY];
                                conns[i].node = neighbor;
                            }
                            // catch out of bounds and skip them
                            catch
                            {
                                continue;
                            }
                        }
                    }

                    // assign costs
                    for (int c = 0; c < conns.Length; c++)
                    {
                        // skip invalid connections since they werent made
                        if (conns[c].node==null) continue;

                        conns[c].cost = (uint)(node.position - conns[c].node.position).costMagnitude;
                    }

                    node.connections = conns;
                }
            }

            yield return new Pathfinding.Progress(0.75f, "Storing nodes");

            // store all nodes 
            var allNodes = new List<PointNode>();
            for (int i = 0; i < nodes.Length; i++)
            {
                allNodes.AddRange(nodes[i]);
            }
            Nodes = allNodes.ToArray();

            yield return new Pathfinding.Progress(0.9f, "Making nodes walkable");

            // set all nodes walkable
            for (int i = 0; i < Nodes.Length; i++)
            {
                Nodes[i].Walkable = true;
            }

            yield break;
        }


        public override void GetNodes(System.Action<GraphNode> action)
        {
            if (Nodes == null) return;

            for (int i = 0; i < Nodes.Length; i++)
            {
                action(Nodes[i]);
            }

        }
    }


#if UNITY_EDITOR
    [CustomGraphEditor(typeof(TileGraph), "Tile Graph")]
    public class TileGraphGeneratorEditor : GraphEditor
    {
        // Here goes the GUI
        public override void OnInspectorGUI(NavGraph target)
        {
            var graph = target as TileGraph;

            graph.Width = EditorGUILayout.IntField("Width", graph.Width);
            graph.Height = EditorGUILayout.IntField("Height", graph.Height);
            graph.Scale = EditorGUILayout.FloatField("Scale", graph.Scale);
            //graph.CollisionMap = (Tilemap)EditorGUILayout.ObjectField(graph.CollisionMap, typeof(Tilemap), true);
            graph.Center = EditorGUILayout.Vector3Field("Center", graph.Center);
        }
    }
#endif

}

Hi

I think the best solution for you would be to use the beta version and implement a custom grid graph rule.
See Writing Custom Grid Graph Rules - A* Pathfinding Project

1 Like

i will give it a shot and report back if i need more help!

My grid graph rule editor will not save the assigned target.Tilemap object variable that i assign to it. It always resets on recompile or entering play mode. The tilemap im assigning to it is a scene object btw. What am i doing wrong?

Also, im not sure if it’s related, but it wont scan the bottom half of my tilemap, it only properly removes the nodes from the top half (above the origin)


#if UNITY_EDITOR
    [CustomGridGraphRuleEditor(typeof(TilemapGridRule), "Tilemap Grid Rule")]
    public class TilemapGridRuleEditor : IGridGraphRuleEditor
    {
        public void OnInspectorGUI(GridGraph graph, GridGraphRule rule)
        {
            TilemapGridRule target = rule as TilemapGridRule;
            target.Tilemap = (Tilemap)EditorGUILayout.ObjectField(target.Tilemap, typeof(Tilemap), true);
            target.HasTile = EditorGUILayout.Toggle("Has Tile", target.HasTile);

        }

        public void OnSceneGUI(GridGraph graph, GridGraphRule rule) { }
    }
#endif

I fixed the 2nd half of my issue, it was just the Z depth being something other than zero for the bottom half (below Y zero) of my map for whatever reason.

Its still not maintaing the Tilemap variable i assign to it after recompiling/entering play mode. The tilemap does have the UnityRefHelper script which is automatically assigned to it for the grid rule (i assume), perhaps its not using it correctly? Therefore it wont scan correctly on awake so im forced to use the cache

Or maybe the editor script i added in the last post, i need to use the SerializedProperty for the ObjectField, but idk how i would get that from the IGridGraphRuleEditor since its not a base editor

bump. still cant figure out why my custom editor is not saving the tilemap variables

bump, same issue. i tried using [SerializeField] and [JsonMember] attributes over the tilemap object field and it just wont save. please lmk if this is an internal issue

Hi

Sorry. In the current beta theres a limitation that the json serializer cannot serialize references to objects in the scene (only to assets). I’ll fix this in the next update. For now you could hard code the reference in your rule using for example a unity tag lookup.

alright, thanks for letting me know