RichAi - Generate a new path if an object is between the Ai and player

Hello,

I am using Recast Graph and I am wondering if I can generate a new path if there’s something between the player and the AI even though the AI reaches it’s destination. Because sometimes this will happen there will be a tree between them and the AI will stop moving but the player will be behind the tree so it doesnt make sense the AI to attack the player from there

EDIT : It actually finds a good path but then I stop the AI and check for distance in order to chase the target again like this:

    private bool IsTargetAway()
    {
        return Math.Round(Vector3.Distance(transform.position, Context.TargetTransform.position)) > _distanceToAttack;
    }

The problem is that while the AI is on it’s last position I can slightly move behind a tree while still keeping a small distance between the AI and the player, so is there a way to detect if the player is behind a tree or maybe a better way to check if the player is away because the Vector3.Distance just casts a ray to the target and ignores the unwalkable areas.

Also if the player is in between 2 trees and the AI cannot reach it due to the tiny space, is there a way to check that the path is blocked and there’s no possible way of reaching the destination?

Hi

So it sounds like you might want to do e.g a raycast from the player to the enemy to check for line of sight as well as distance, and only stop if the ai has both of them.

Another approach which I have found very useful is to calculate a set of points around the target as candidate positions where the AI would want to stand (e.g 6 or 8 points in a circle around the target), then you can check if those points are “free” (e.g using Physics.OverlapSphere) and make the AI move to the closest one that is free and isn’t already used by another enemy (this will get you a nice surround behavior).

Example 4 skeletons around their target, each skeleton standing on one of the 6 target points:

This idea is great but I will still have cases where the player can “hide” and increasing the character radius from the A* settings helps the AI to not be able to go too close to objects, and the player will get pretty close to a tree, or inside an object thats not walkable for the AI (like a house?) and instead of the AI waiting in front of that house I’d like it to perform an action (maybe growl or run away), so I was just looking for a way to check when that AI cannot reach the player, because the player is inside the “unwalkable” zone.

A better image that I posted was in the other thread you’ve seen:

Hi

The best way to do such a check is probably something like:

if (ai.reachedEndOfPath && !ai.pathPending && Vector3.Distance(transform.position, ai.destination) > someThreshold) {}

If you use a grid graph you could also check if there is a valid path to that point using e.g PathUtilities.IsPathPossible (this will not work for a recast/navmesh graph because if the player is in a region where there is no navmesh, the nearest node to that point may still be reachable by the AI even though it is not close to the player at all).

I will try to do something like this. The recast graph also leaves a lot of useless holes on the terrain because it bakes on awake so my enemies and their colliders are active and the area below them is excluded and unwalkable.

I know there is an option to set scan on awake to false but then it throws errors. I also don’t want a special Layer for my enemies just to exclude them from the recast graph baking.

You really should put your enemies (or the rest of the static meshes) in a separate layer and then configure the layer mask setting in the recast graph to only include the layers that you want in the mesh. I suppose you could set the layer mask to nothing and then add the RecastMeshObj component to everything you want included in the scan, but that seems more tedious.

Why not just exporting the graph and loading it in runtime?

Depends on if you have enemies in the scene outside of play mode. If you don’t, sure.
Also you can do this in an easier way by using the cache functionality.
See https://www.arongranberg.com/astar/docs/save-load-graphs.php

Using this

if (ai.reachedEndOfPath && !ai.pathPending && Vector3.Distance(transform.position, playerPosition) < someThreshold) 
{
Attack();
}
else
{
 Hide();
}

Sometimes as the player is moving back it calculates that the distance is let’s say 4.3 and my threshold(to attack) is 4, it will be less than 4 if the player stops but if they’re moving backwards as I said sometimes it will be bigger than 4 and that doesn’t mean the player cannot be reached and we should hide, because it’s on unwalkable for the AI zone. Isn’t it possible to check if the player is on an unwalkable zone / can be reached ?

I want the AI to hide only if it cannot reach the player because the player is on the unwalkable zone and the distance between the AI (when it reaches end of path) and the player is bigger than some threshold

You can check if the player is on the navmesh using something like this:

var graph = AstarPath.active.data.recastGraph;
// Check if the player is on a node in the graph (the method call will return that node, or null if the player is not on a node)
var playerNode = graph.PointOnNavmesh(player.position, null);
if (playerNode != null) {
    // Ok, the player is on the graph, but is the player reachable?
    var node = AstarPath.active.GetNearest(transform.position).node;
    if (PathUtilities.IsPathPossible(node, playerNode)) {
          // The player is on the graph and we can reach that point on the graph
    }
}

Alternatively you might want to have some margin, so you could do a check like this

NNConstraint nn = NNConstraint.Default;
nn.distanceXZ = true;
var closest = AstarPath.active.GetNearest(player.position, nn);

// Check if any node could be found (usually yes, unless the player is veeery far away from the graph)
if (closest.node != null) {
    var direction = closest - player.position;
    direction.y = 0;
    if (direction.magnitude < 1f) {
        // Player is either on the navmesh or within one unit of the border.
        // But is the player reachable?
        var node = AstarPath.active.GetNearest(transform.position).node;
        if (PathUtilities.IsPathPossible(node, playerNode)) {
            // The player is on the graph and we can reach that point on the graph
        }
    }
}
1 Like

The first solution seems to be working for me, is that code fast though? I won’t be calling it often maybe once every 5 seconds after the enemy hides to check if I can it can reach the player.

The other solution with Vector3 distance checked also worked but I had to use the same “Y”, now I will be using the latest one after the AI hides to see if the player is reachable.

Hi

That should definitely be fast enough for once every 5 seconds. I haven’t got exact profiling data right now, but I expect somewhere on the order of 0.01 ms, maybe a bit more, maybe a bit less depending on your graph.
The IsPathPossible method call is pretty much instant.

1 Like