FloodPath (FloodPathTracer) and Seeker tagPenalties are being set properly but not being implemented

I have a manager script that generates a FloodPath. Multiple Seekers will request the path (FloodPathTracer) when they spawn and head towards the target. This code works properly.

How I generate the FloodPath:

_floodPath = FloodPath.Construct(destination.transform.position, null);
AstarPath.StartPath(_floodPath);
_floodPath.BlockUntilCalculated();

How I generate the FloodPathTracer:

FloodPathTracer fpathTrace = FloodPathTracer.Construct(startPosition, _floodPath, null);

This all works properly. But what I want to do is have the Seekers randomly choose a fork in the path. There’s a spot where the path goes around a space to the left and the right. I want the Seekers to randomly pick one of those. The image below shows what I am trying to do. The Seeker follows the red arrow from the left and then needs to go left or right (to the orange or red tile).

How I was implementing it was through tags and tagPenalties. The tags are set properly (I can see the colors in the inspector) and I think I am setting the tagPenalties correctly, but they seem to be getting ignored.

I have tried applying tagPenalties to the FloodPathTracer and the Seeker (and both) and the Seekers all take the same route, which is the one they took before attempting to add this “forking” concept.

Testing with a randomly generated penalty (at the FloodPathTracer level):

int[] tagPenalties = new int[32];
switch (randomPathIndex)
{
    case 0:
        tagPenalties[2] = 20000000;
        break;
    case 1:
        tagPenalties[1] = 20000000;
        break;
}
fpathTrace.tagPenalties = tagPenalties;

And testing at the Seeker level:

int randomPathIndex = UnityEngine.Random.Range(0, 2);
int[] tagPenalties = new int[32];
switch (randomPathIndex)
{
    case 0:
        tagPenalties[2] = 20000000;
        break;
    case 1:
        tagPenalties[1] = 20000000;
        break;
}

seeker.tagPenalties = tagPenalties;
path = AIPathManager.instance.GetPathToBase(gameObject.transform.position);
path.nnConstraint.graphMask = seeker.graphMask;
path.tagPenalties = seeker.tagPenalties;
path.enabledTags = seeker.traversableTags;
seeker.StartPath(path, OnPathComplete);

Again, nothing I do seems to affect the paths. Any ideas of what I am doing wrong here?

I have tried adding a single object with a “GraphUpdateScene” component, set Modify Tag to true and the tag value to non-default tag. A* shows a purple color in the path where I place it. Which is great.

Here’s a screenshot of the GraphUpdateScene details:

image

I go to the Seeker and set the tag penalty for the matching tag to 50000000. The Seeker walks right over the areas like nothing is there when it should be going to the other side.

Here’s a screenshot of the Seeker component:

image

I feel like this is something simple but for the life of me I can’t figure it out.

Hi

A FloodPathTracer path only uses precalculated information from the FloodPath. This means it will use the same tags/penalties are the original FloodPath. If you want to use other tag penalties or path settings you will need to recalculate a FloodPath with those settings.

I’ll update the documentation to clarify this.

Thanks for the quick reply!

One path I tried before this is to create multiple FloodPaths, each with different penalties. So in the above example, there would be 2: one with a large penalty for the orange tile and one for the red tile. I would set a GraphUpdateScene on the orange tile, then start the FloodPath and add it to a list. I would then reset that value back to 0 and set a large value for the red tile and add that to the same list. Then I would use one of the 2 FloodPaths when generating the FloodPathTracer. However, that seemed to never work and the Seekers all went the same path, which I believe was the most recently-generated one. I am guessing that is not a supported approach, assuming I wasn’t making any other mistake.

Is there a recommended way to do what I want? Should I be creating multiple grid graphs instead?
My ultimate goal is to create these forks and have the Seekers go left or right in a seemingly random way. There will potentially be a lot of Seekers (50, 100 or possibly more at a time) all heading towards a single target so FloodPath seemed to be the most efficient way to handle that. If there is an equally-efficient method, though, I am open to it.

Hi

If it’s only 50-100 agents, I would say just make them calculate their own individual paths :slight_smile: Sure, it’s not as efficient, but it’s much better to get something working and then later maybe optimizing it if it turns out to be a problem.

Possibly what happened with your graph updates before (I haven’t seen your code so it’s hard to tell) is that the paths weren’t calculated until after both your graph updates had been done. Or alternatively that the graph updates weren’t actually applied until after your paths had been calculated. I’d recommend using the GraphUpdateScene component to set a tag instead, and then start 2 flood paths with different tag penalties.

I think that might be what I initially tried, but it’s possible I was missing a step. The UpdateGraphs call might also need to be above StartPath. This is an example of what I was attempting:

        // Activate and set the penalty for the GraphUpdateScene
        _graphUpdateScene.enabled = true;
        _graphUpdateScene.penaltyDelta = 20000000;

        // Generate the path
        FloodPath floodPath = FloodPath.Construct(target.transform.position, null);
        AstarPath.StartPath(floodPath);
        floodPath.BlockUntilCalculated();

        // Update the graphs using the GraphUpdateScene
        AstarPath.active.UpdateGraphs(_graphUpdateScene.GetBounds());

        // Add the path to the list
        floodPaths.Add(floodPath);

The call that asks for a path then looks like this:

    // Return a random path
    int randomPathIndex = UnityEngine.Random.Range(0, _floodPaths.Count);
    FloodPathTracer fpathTrace = FloodPathTracer.Construct(startPosition, 
        _floodPaths[randomPathIndex], null);

    return fpathTrace;

But this code seemed to always return the same path or at least the Seekers moved the exact same way.

Hi

Two things:

  1. Graph updates are also queued. So you will need to call AstarPath.active.FlushGraphUpdates(); if you want them to be applied immediately.
  2. The GraphUpdateScene only applies a graph update at start/scan and if you call the Apply function. So instead of your AstarPath.active.UpdateGraphs, you should call call _graphUpdateScene.Apply().
_graphUpdateScene.penaltyDelta = ...;
_graphUpdateScene.Apply();
AstarPath.active.FlushGraphUpdates();
AstarPath.StartPath(...);
path.BlockUntilCalculated();

// Same thing for the other path

Though it should be easier to use tags as I mentioned before. Then you don’t have to do this whole dance of updating and reverting the graph updates.

Thank you! I think what I was missing was the Apply() and FlushGraphUpdates() calls.
I though that, by having ApplyOnStart + ApplyOnScan both enabled that Apply() was redundant. And I missed the FlushGraphUpdates completely. I think I have it working properly now.

One thing I am noticing is that I need to apply the penalty to both the GraphUpdateScene component on the tile itself as well as add the penalty to tagPenalties that is applied to the FloodPath before calling FlushGraphUpdates/StartPath.

A over-simplified example so you can see what I am doing:

// For the orange and red tiles
_graphUpdateScene.enabled = true;
_graphUpdateScene.Apply();

// When creating each FloodPath
int[] tagPenalties = new int[32];
// This is for testing, there are only 2 options so it runs the top part the 1st time, and the bottom the 2nd
if (index == 0)
{
    tagPenalties[3] = traversalPenalty;
    index++;
}
else
{
    tagPenalties[4] = traversalPenalty;
}
floodPath.tagPenalties = tagPenalties;

AstarPath.active.FlushGraphUpdates();
AstarPath.StartPath(floodPath);
floodPath.BlockUntilCalculated();

This appears to give me the expected results. Utilizing the tags like you had mentioned was what I was attempting to do but I was not properly applying the values before generating the path.

Thank you again for the help I really appreciate it.

1 Like