Making nodes unwalkable with tilemap

  • A* version: 5.3.1
  • Unity version: 2022.3.48f1

I’m running into an issue where only the nodes above y = 0 are getting updated with my custom rule.

I only want nodes where land exists to be walkable and this works fine but doesn’t apply to nodes generated below y = 0.

        var nodeWalkable = context.data.nodes.walkable;
        var nodePositions = context.data.nodes.positions;

        for (int i = 0; i < nodePositions.Length; i++)
        {
            var position = nodePositions[i];
            DebugHelper.DrawBox(position, .25f, .25f, Color.blue, 5f);
            nodeWalkable[i] &= TileMapController.Instance.DoesTileExist(position, Enumerations.TileMapLayers.Land);
        }

I understand it has something to do with the i = 0, but my interpretation is that if I have a nodePosition[i] and set nodeWalkable[i] = false… the position at nodePosition[i] will not be walkable.

Picture for context

Hi

Would you mind posting your whole code?

Here it is! Its went through a couple iterations trying to diagnose the issue.

using UnityEngine;
using Pathfinding;
using Pathfinding.Graphs.Grid.Rules;

// Mark with the Preserve attribute to ensure that this class is not removed when bytecode stripping is used. See https://docs.unity3d.com/Manual/IL2CPP-BytecodeStripping.html
[Pathfinding.Util.Preserve]
public class CustomPathFinderRule : GridGraphRule
{


    public override void Register(GridGraphRules rules)
    {
        // The Register method will be called once the first time the rule is used
        // and it will be called again if any settings for the rule changes in the inspector.
        // Use this part to do any precalculations that you need later.

        // Hook into the grid graph's calculation code
        rules.AddMainThreadPass(Pass.BeforeConnections, context =>
        {
            if (TileMapController.Instance == null)
                return;

            // This callback is called when scanning the graph and during graph updates.
            // Here you can modify the graph data as you wish.
            // The context.data object contains all the node data as NativeArrays
            // Not all data is valid for all passes since it may not have been calculated at that time.
            // You can find more info about that on the documentation for the GridGraphScanData object.

            // Get the data arrays we need
            var nodeWalkable = context.data.nodes.walkable;
            var nodePositions = context.data.nodes.positions;

            for (int i = 0; i < nodePositions.Length; i++)
            {
                var position = nodePositions[i];
                var doesTileExist = TileMapController.Instance.DoesTileExist(position, Enumerations.TileMapLayers.Land);
                nodeWalkable[i] = doesTileExist;
            }
        });
    }
}

TileMapController Method

public bool DoesTileExist(Vector3Int tilePosition, Enumerations.TileMapLayers layer)
    {
        var tileMap = GetTileMap(layer);
        var tile = tileMap.GetTile(tilePosition);

        return tile != null;
    }

Any idea why this would be happening or is there anything else I can provide you?

You are passing a Vector3 world position to the method, but it expects a Vector3Int cell index. That’s the most likely issue I think.

Aw. I have that method overloaded so I don’t have to handle converting it everywhere. :frowning:

    public bool DoesTileExist(Vector3 tilePosition, Enumerations.TileMapLayers layer)
    {
        var vector3Int = tilePosition.ToVector3Int();
        return DoesTileExist(vector3Int, layer);
    }

    public bool DoesTileExist(Vector3Int tilePosition, Enumerations.TileMapLayers layer)
    {
        var tileMap = GetTileMap(layer);
        var tile = tileMap.GetTile(tilePosition);

        return tile != null;
    }

You can’t just convert it to a Vector3Int by rounding it or something (which I would expect ToVector3Int to do). You need to convert from world space to local tilemap space.

I believe that would be true if my grid wasn’t centered at 0,0. But it is and should always be.

My approach is that if I have a building at 1.5, 1.5… then I call that .ToVector3Int() method and it returns me 1,1 which is a location on the grid.

Aw I figured it out. The issue is my ToVector3Int() function just floors all coordinates, including the z coordinate. Adjusting that to always return 0 instead of the potential -1 due to floating point numbers… it works now.

Thanks for the help!

1 Like