Tag masks and NNconstraints

Hi,

For example’s sake imagine a ground plane completely surrounded by unwalkable areas that has a wide river in the middle completely dividing it into two areas.

If I create a gridgraph that has the ground as walkable and river unwalkable and then attempt to create a path to a point in the middle of the river I get (with path.nnConstraint.constrainDistance = false) a path to the nearest position to the target position. Perfect! This is the desired behavior

However I’ve now made the whole area walkable and tagged the river and ground since some units can go through both types of terrain and some on only the ground or the water. A car can only travel on the ground. Now however, whatever I try to do, if i try to create a path to the middle of the river for a car I get errors:

Path Failed : Computation Time 28.00 ms Searched Nodes 10074
Error: Searched whole area but could not find target
Path Number 3 (unique id)

(The car navigates the ground fine)

I saw your answer to the following post. I tried it but it makes no difference.

I also tried setting the path’s NNConstraint like this:

path.nnConstraint.constrainTags = true;
path.nnConstraint.tags = walkableTagMask;

that makes no difference.

Any ideas how I can get the desired behavior?

Thanks

Hi

Maybe you would be interested in this thread: Path failed: Searched whole area but could not find target
TL:DR. It returns that error because otherwise it would be very easy to fall off a performance cliff without noticing why.

Howdi,

I’ve not started work yet but I’ve read over that thread and I’ll try it out.

What I’m curious about is why (in my example, since it’s easy to discuss) the river tag is not simply treated in the same way as an unwalkable node when I call the search. (because it is, after all, unwalkable)

Are the unwalkable nodes utilized to preprocess something in your library? And then you use that preprocessed data to determine the closest node to the target at runtime (for unwalkables)?

If you could help me understand the difference I can sort out a solution myself. Where should I be looking?

Cheers Aaron

Hi

Yes, unwalkable nodes are used for preprocessing the graph to calculate a number of different ‘areas’. These are the colors on the nodes that you can see if you set Path Debug Mode = Areas in the inspector. Assume that the river was instead unwalkable, when a search for the end point of a path was done in the middle of the river, it would notice that one side of the river had the same area as the start node of the path, and the other side of the river had another area. That two nodes are in different areas means that there is no valid path between those nodes, so the nearest node search will know which side of the river it can actually reach and will pick the end node on that side. If the river was only tagged, it would not know which side was reachable and it might pick the wrong side, in which case it would not be able to find the end node when searching. The preprocessing cannot be done because there is a very large number of possible tag combinations that could be used.

Okay, I understand where you’re coming from. However, during the search there will always be a node that’s closest to the target, regardless of any constraints. That’s the node I want.

I’ve been looking at why path.calculatePartial = true; isn’t doing anything since I can see in CalculateStep that partialBestTarget is being set correctly. This is the node i want. But it wasn’t returning a path to it.

I’ve stepped through your code and I see that this problem (for my maps anyway) can be solved by editing CalculateStep.

Change this block:

             if (pathHandler.HeapEmpty()) {
				Error();
				LogError("Searched whole area but could not find target");
				return;
			}

To this:

            if (pathHandler.HeapEmpty())
            {
                if (calculatePartial && partialBestTarget != null)
                {
                    CompleteState = PathCompleteState.Partial;
                    Trace(partialBestTarget);
                }
                else
                {
                    Error();
                    LogError("Searched whole area but could not find target");
                }

                return;
            }

And it all works as expected. (although this skips the trace profiling at the end of the method)

Yeah, it could be very expensive but at least it does what I want! My graphs are fairly small so it’s not a problem.

Thanks again Aaron!