How to define a 2D array for obstacle detection?

I am making a 2D procedurally generated game using Unity’s tilemap system.

However, I have not seen any documentation about working with the Tilemaps. What I have been doing so far is defining a 2D bool[,] array for each position in my map, where true represents an obstacle and false a walkable tile. Then when I was finding a path, I would check this array instead of using colliders which was more simple and worked with the tilemap.

Now that I’ve switched to A* Pathfinding Project, is there any equivalent functionality to this? If not, how do I work with tilemaps? I have multiple tilemaps on a single grid and some are only walkable, some are only obstacles and some tilemaps have both walkable and obstacle tiles.

Any help or suggestions are appreciated. Thank you!

Hi

If you want to provide your own info to the pathfinding system the easiest way is probably to use the ITraversalProvider interface. It allows you to hook into the system and provide a method for if a node is walkable or not.

See https://arongranberg.com/astar/docs/turnbased.html#ITraversalProvider

You can cast the node to a GridNodeBase and then use GridNodeBase.X/ZCoordinateInGrid to query your array. https://arongranberg.com/astar/docs/gridnodebase.html#XCoordinateInGrid

Alternatively, and especially if you need more heavy processing of the data you may want to write a custom grid graph rule. This is possible in the beta version. Take a look at this page for more information: https://www.arongranberg.com/astar/documentation/dev_4_3_41_5623d52b/gridrules.html

However usually you work with tilemaps by making them generate colliders, and then you use the included collision checking functionality of the grid graph.

Hey,

Thank you for the super quick reply. Would you be able to clarify more / give me a concrete code example on how I can do this? Perhaps I should’ve posted this in Beginner questions, because I have no knowledge of where to even begin.

  1. Do I need to make a class (e.g. AstarNode : GridNodeBase) that inherits GridNodeBase and implement all it’s members?
  2. Should only one instance of the ITraversalProvider exist, or should a new instance be created every time I want to calculate a path?
  3. How do I actually construct the path? What’s the difference between ABPath.Construct and AstarPath.StartPath? Which one returns the path that should be used by my Seeker unit?
  4. On top of all this, I don’t know how to query the array with the mentioned GridNodeBase.X/ZCoordinateInGrid.

I know this is a lot, but this might be helpful to other people who also want to do this in the future.

  1. No, GridNodeBase is the type of all the nodes in the grid graph
  2. That’s up to you. Easiest is probably to create one every time you request a path.
  3. ABPath.Construct creates the path object but doesn’t calculate the path. AstarPath.StartPath calculates the path. Usually you will use Seeker.StartPath which is very similar to AstarPath.StartPath but which allows you to easily use path modifiers. Take a look at https://arongranberg.com/astar/docs/callingpathfinding.html for more info.
  4. XCoordinateInGrid and ZCoordinateInGrid are the coordinates of the node. So (0,0) is the top-left node in the grid, (1,0) is the node to the right of that. You will have to index into your boolean array to figure out if the node is supposed to be walkable or not.

Btw, if you don’t need graph updates or anything complex, then it might be easier just to assign the walkability data after everything has been generated. Here’s an example of that: https://arongranberg.com/astar/docs/graph-updates.php#direct

I feel like I have a better understanding of how this works now. Here’s what I got so far:

What I completely don’t understand is what you wrote in point 4 - how and where do I call and connect XCoordinateInGrid with my 2D array of bools.

I am making a Rimworld-like game, so that 2D array may change very often when pawns start building walls or mining into caves, which means I need to update it often.

Ah, if you have mining and caves and such then I strongly recommend that you write a grid graph rule instead.
The reason is that if the data is actually baked into the grid graph then some other machinery can use that to optimize queries. For example queries of the type “Is this point reachable from that point” (which is used before every path calculation).

Your 2D array of booleans is laid out in a grid, yes? So for example you index it using myArray[xcoordinate, zcoordinate] (or something similar)? Then you will have to use node.XCoordinateInGrid as your xcoordinate when you index into the array. But you need to make sure the arrays line up correctly, or that you translate between the coordinates coorectly. E.g. a node at x coordinate 5 may not necessarily line up with a tile at x coordinate 5 in your array.

EDIT: I just realized node is accessed via the TraversalProvider. I think I understand how to do this now, will try and see if it works. :slight_smile:

Ah, I actually implemented the mentioned checking “Is point A reachable from point B” using my own systems, so if that’s the only thing custom grid graph rules are used for, I think I’ll skip them for now. I still don’t understand where exactly I am supposed to use node.XCoordinateInGrid.

For simplicity, let’s assume when the map is generated I assign either true or false for each position (and stored this information into a 100x100 bool[,] positions array, and let’s also assume the arrays line up correctly.

Now that the array has been filled, and my Seeker requests a path from point A to point B, where do I use the node.XCoordinateInGrid? If node is of type GridNodeBase, do I need to create new ones? Or am I accessing the list of those nodes from the NodeGrid of your asset? Where do I compare node.Xcoordinate & node.Zcoordinate with my positions[x,z] array, in the TraversalProvider, or perhaps CalculatePath method?

I am sorry if this is awfully obvious to you, but I’ve only ever written my own A* scripts (which were trivially simple) and this is all completely new to me.

(some pseudocode would really help me understand this!) :smiley:

After some trial and error, I did it, and everything works! Just one question:

When the game starts and gizmos are turned on, we can see the initial bake of the grid (blue area is walkable). However, my entire map is “walkable” which means this didn’t take into account my TraversalProvider. How can I tell it to use it and update the walkable grid preview accordingly?

An ITraversalProvider is provided per path. That means every path could theoretically have its own ITraversalProvider which is completely different from all the others. A graph is thus not tied to any given ITraversalProvider and no data from it is actually stored in the graph. This is why the graph just looks all blue: the ITraversalProvider doesn’t affect the graph, only how the path is calculated.
On the other hand if you’d use any of the other approaches I mentioned, those would update the graph and you would see it visualized.

What if there’s two types of units with their own ITraversalProviders - Ground (walk only on walkable tiles) and Air (fly anywhere).

Would it still make sense to use the other approach (I’m assuming that means writing my own grid graph rules)?

In that case, what sort of visualization would be shown if there’s more than one provider?

If the different units are very different (flying and walking are very different) then it often makes sense to have two different graphs. See this tutorial for an example: https://arongranberg.com/astar/docs/multipleagenttypes.html