Movement cost in 2D Grid Graph




I have checked the example file “Example14_TurnBased_Hexagon” then convert them into 2D scene.
The maxGScore should work same as movement cost if I understand it correctly, but the node path become wrong when movement higher.
If anyone has worked with this problem, please give me some advice.

Hi

The movement cost for diagonals is 1414 (sqrt(2)*1000), compared to the normal axis aligned cost of 1000 (for a node size of 1). I assume that your movement points are multiplied by 1000 before being used as the g score.
In that case the screenshot looks correct. To reach the crossed out nodes the cost would be around 2*1414 = 2828 which is less than 3000.
What is the cost that you want to have?

Thank you for your reply.

Let me make it clear first so you will understand my goal just in case I explain it wrong.
My goal is to make the movement like fire emblem.
Some unit can only walk on ground, and some unit can fly over water and building.
I already can do that using my own pathfinding but then I saw your asset, look perfect for me so I bought it and try to learn more about it.

Currently, I’m on the stage of making highlight moveable tile base on move range of unit.

As you see in the picture of Movements Points: 2, the highlight shows good.
But on Movements Point: 3, the highlight tile I put the red cross on should not be there.

So how do I solve that ?

This is the code I’m using

void GeneratePossibleMoves(UnitController unit) {
        var path = ConstantPath.Construct(unit.transform.position, unit.MovementPoints * 1000 + 1);

        

        path.traversalProvider = unit.TraversalProvider;

        // Schedule the path for calculation
        AstarPath.StartPath(path);

        // Force the path request to complete immediately
        // This assumes the graph is small enough that
        // this will not cause any lag
        path.BlockUntilCalculated();

        foreach (var node in path.allNodes) {
            if (node != path.startNode) {
                // Create a new node prefab to indicate a node that can be reached
                // NOTE: If you are going to use this in a real game, you might want to
                // use an object pool to avoid instantiating new GameObjects all the time
                var go = Instantiate(nodePrefab, (Vector3)node.position, Quaternion.identity);
                possibleMoves.Add(go);

                go.GetComponent<OverlayTile>().node = node;
            }
        }
    }

This very much depends on your definition of movement points. What is the definition of movement points that you want to use?
I have not played fire emblem, so I’m not sure what kind of movement is used there.

The definition of movement point I want to use is the range of tile they can move.
In the attached image, unit can move 3 tile each turn, so it’s the movement point I use.
4

Ok. So it looks like you do not want to consider diagonals.
Have you tried disabling diagonals in the grid graph setttings by changing the Neighbours setting to Four, instead of the default Eight?

Thank you, I will try it now. :smiling_face_with_three_hearts:


It look perfect now, thank you so much.
Also I have seen you replied my review on asset, so yeah I have problem on using CanTraverse and GetTraversalCost. (I will change it to 5 star now :blush:)
If I understand correctly, these 2 function will be used for the goal of Some unit can only walk on ground, and some unit can fly over water and building. I mention above right ?

I would like to know where to call these 2 functions and how to use it in detail:

  1. I draw tile map using Tile Palette, beside unwalkable tile which can be choose on Obstacle Layer Mask, there will be some special tile like swamp which still walkable but cost higher move point for some Unit. (How do I scan graph and detect those specific nodes?)
  2. Example like Ground unit moving on a Swamp Node will cost 2 move point instead of 1, or Flying Unit can fly over Swamp will still cost 1 move point only. (How do I use traversalProvider to solve this? Must be relate to step 1 marking those specific nodes with some tag or something)

Hi

For your use case, I think using tags is much simpler.
You can set a tag on each node (e.g. tagging nodes as Water). Then on the Seeker component you can specify which tags a unit can traverse, and at what cost.

Take a look at this page for more info: Working with tags - A* Pathfinding Project

Currently, there’s no built-in support to calculate the tag from a tilemap. So you’d have to write that code yourself. You can find similar code here: Graph Updates during Runtime - A* Pathfinding Project
That code changes the walkability, but you can instead make it change the tag.

You may want to use a separate graph for flying units, if they are supposed to be able to traverse basically anything. This is because when a node is fully unwalkable, the system can use a few other optimizations to make things behave better. When nodes are just tagged, it cannot make as many assumptions.
Take a look at Multiple agent types - A* Pathfinding Project for more information about that.

Yeah, I already read that Tag post.

I have tried to learn how to Tag node but I’m still stuck.

The video quality is too old and low quality, I can’t see anything at all. Sorry, my eye is bad.

How do I mark some node in the graph with tag ?
I can only do that by asset to graph data in code and set it by position ? or I can do that in Unity Editor?

I’d iterate over all nodes in the graph (like in the first link in my previous post) when the game starts.

AstarPath.active.AddWorkItem(() => {
    var gg = AstarPath.active.data.gridGraph;
    for (int z = 0; z < gg.depth; z++) {
        for (int x = 0; x < gg.width; x++) {
            var node = gg.GetNode(x, z);
            node.Tag = 1; // Do something here to calculate the tag
        }
    }
}));

So the only way to change node tag is Using Script and I must know which position in the Graph is Water/Swamp/etc…

I can’t get my head over of how to know exactly which node position in graph is related to special tiles in TileMap.

Hmm… I must calculate the position of those special tiles by custom script like access to Tilemap data and get Cell position and store them into an array, then access to graph and use cell position to get node ? I don’t know if I understand it correctly.

You can also do it using the GraphUpdateScene component. But for your use case, using scripting seems like the better solution imo.

You probably want to use Unity - Scripting API: GridLayout.WorldToCell

Yeah, I use WorldToCell a lot to interact mouse with TileMap.

What I mean, is the position of Node in Graph also same position as cell position in Tilemap ?
If yes, then I can call WorldToCell using Node position to know on that cell position has which tile.

That depends on how you have configured your grid graph.
The node.position field is an Int3 coordinate in world space. You can convert it to a Vector3 world position using (Vector3)node.position that you can then pass to WorldToCell.

1 Like

Thank you for information, it’s enough information for now, that helps me a lot.

I will try to tackle them again, really like your asset. :blush:

See you later when I face another problem which I can’t solve by myself.

1 Like

Hi Aron,

I have succeed pairing tilemap and graph to mark node with tag, then using below code to generate possible move node

var path = ConstantPath.Construct(unit.transform.position, unit.MovementPoints * 1000 + 1);

        path.traversalProvider = unit.TraversalProvider;

        // Schedule the path for calculation
        unit.GetSeeker().StartPath(path);

And the log for each GraphNode is correct also (Tag: 0 is grass ground, Tag: 1 is rock ground)
image

What I’m trying to do next are:

  1. Test prevent Unit walk on Rock Ground.
  2. Test Unit can walk on Rock Ground but cost higher move point.

So, I have some question:

  • I have used Seeker.StartPath instead of AstarPath.StartPath to work with Tag, I can prevent traversable by untick it, but how do I apply penalty point correctly for move point cost? At penalty 0-point, Unit with 4 move point can move 4 nodes, how many penalty point do I need if Unit with 4 move point can only move 2 nodes on that penalty node? (I have played with it but can’t get the correct result)
  • Beside Seeker + Tag are simple way “but it is also very limited in what it can do” like you said, please teach me how to use ITraversalProvider, I feel like this one is really powerful and flexible but because of lacking example and document about it, I can’t use it at all :face_with_spiral_eyes:

Also I have found another problem, I’m trying to use AILerp component attached to unit but the moment this code call

void GeneratePossibleMoves(UnitController unit) {
        var path = ConstantPath.Construct(unit.transform.position, unit.MovementPoints * 1000 + 1);

        path.traversalProvider = unit.TraversalProvider;

        // Schedule the path for calculation
        unit.GetSeeker().StartPath(path);

        // Force the path request to complete immediately
        // This assumes the graph is small enough that
        // this will not cause any lag
        path.BlockUntilCalculated();

        foreach (var node in path.allNodes) {
            if (node != path.startNode) {
                Vector2Int roundedPosition = new Vector2Int(Mathf.FloorToInt(((Vector3)node.position).x), Mathf.FloorToInt(((Vector3)node.position).y));

                if (MapManager.Instance.Map.ContainsKey(roundedPosition)) {
                    OverlayTile overlayTile = MapManager.Instance.Map[roundedPosition];
                    overlayTile.Show();
                    possibleMoves.Add(overlayTile);

                    overlayTile.Node = node;
                }
            }
        }
    }

I will instantly get Exception: This function only handles ABPaths, do not use special path types even though I haven’t call ai.destination or ai.SetPath() at all.
The only place I call AILerp is this function in UnitController

public void MoveToNode(GraphNode node) {

        var path = ABPath.Construct(transform.position, (Vector3)node.position);

        path.traversalProvider = TraversalProvider;

        _ai.SetPath(path);
    }

How do I get around with this ?

The AILerp script listens to any paths that the Seeker component calculates, and will try to follow them.
Generally if you are calculating paths which are not for the agent to follow, you should do it using AstarPath.StartPath.

var path = ...;
// Copy some settings from the Seeker
path.enabledTags = seeker.traversableTags;
path.tagPenalties = seeker.tagPenalties;
AstarPath.StartPath(path);

won’t that also bypass modifiers attached to the seeker? If so - how to include them when requesting path?