Seeks all walking to the edge of the graph

Hello,

I am facing this odd issue where all the seekers decide to move to the edge of the grid graph simultaneously upon instantiation. what do you think could be the likely causes of the problem.


below is the initialization code for the AI (from the path-finding perspective):

  • the path-finding is controlled from behavior tree nodes. here’s the code for the default wandering node & all the path-finding code from each entity that handles it.

//basic code for movement

public override void OnAwake()
{
// cache for quick lookup
aiPathAgent = gameObject.GetComponent();
aiPathAgent.targetVector = transform.position; // the target for the seeker
}

then when we need to set a new target we call this function…

protected override bool SetDestination(Vector3 target)
{
aiPathAgent.targetVector = target;
return true;
}

now for wandering code:

SetDestination(GetTarget());

Get Target is a function that returns a valid target, targets are are determined valid in this order:

  1. random point near the ai on the graph
  2. if the random point is not in a walkable area, check the same point in the opposite direction (-point basically)
  3. if both points are invalid, check if it’s possible to go to the spawn point.
  4. If not all of this, check if it’s possible to go to the center of the area you spawned in.
  5. if none of that works stand still. below is the code

protected Vector3 GetTarget()
{
var target = Vector3.zero;
var direction = transform.forward + Random.insideUnitSphere * wanderRate.Value;

        if (TargetIsValid(transform.position, direction))
        {
            target = transform.position + direction.normalized * wanderDistance.Value;


            if (entityWanderingDebug)
                Debug.Log("Target point is valid....for: " + gameObject.name);
        }
        else if(TargetIsValid(transform.position, -direction))
        {
            target = transform.position + -direction.normalized * wanderDistance.Value;


            if (entityWanderingDebug)
                Debug.Log("Opposite Target point is valid....for: " + gameObject.name);
        }
        else if(TargetIsValid(transform.position, aiPathAgent.spawnPoint.spawnPosition))
        {
            target = transform.position + aiPathAgent.spawnPoint.spawnPosition.normalized * wanderDistance.Value;


            if (entityWanderingDebug)
                Debug.Log("Spawn point is valid....for: " + gameObject.name);
        }

        else if(aiPathAgent.spawnPoint is POISpawner && 
            TargetIsValid(transform.position, poi.transform.position + Random.insideUnitSphere * wanderRate.Value))
        {
            target = transform.position + (poi.transform.position + Random.insideUnitSphere * wanderRate.Value).normalized * wanderDistance.Value;

            if (entityWanderingDebug)
                Debug.Log("Poi center is valid....for: " + gameObject.name);
        }

        else 
        {
            target = transform.position;


            if (entityWanderingDebug)
                Debug.Log("No valid target, standing still....for: " + gameObject.name);
        }

        if (entityWanderingDebug)
            Debug.Log("Target vector is: " + target);
       
        return target;
    }

lastly we validate the targets based on 2 conditions:

  1. a path is possible between both nodes
  2. the target is within the bounds of the AI’s spawn area. here’s the code for that.

protected override bool TargetIsValid(Vector3 currentPos, Vector3 targetPos)
{
var targetValid = false;
GraphNode currentPosNode = AstarPath.active.GetNearest(currentPos).node;
GraphNode targetPosNode = AstarPath.active.GetNearest(targetPos).node;

        if (poi != null)
        {
            float poiDistanceThreshold = poiSizePercentage * poiSize;
            targetValid = PathUtilities.IsPathPossible(currentPosNode, targetPosNode) && Vector3.Distance(targetPos, poi.transform.position)
                < poiDistanceThreshold;


            if (entityWanderingDebug)
            {
                Debug.Log("path possible:  " + PathUtilities.IsPathPossible(currentPosNode, targetPosNode) + " For: "+gameObject.name);
                Debug.Log("point less that poi threshold:  " + (Vector3.Distance(targetPos, poi.transform.position)
                < poiDistanceThreshold));
            }
        }
        else
        {
            targetValid = PathUtilities.IsPathPossible(currentPosNode, targetPosNode);

            if (entityWanderingDebug)
               Debug.Log("path possible:  " + PathUtilities.IsPathPossible(currentPosNode, targetPosNode));
        }

        return targetValid;
    }

Refactored code segments:

protected Vector3 GetTarget()
{
var validTargetType = DirectionType.None;

        if (TargetIsValid(transform.position, GetDirection(DirectionType.RandomPoint)))
            validTargetType = DirectionType.RandomPoint;

        else if (TargetIsValid(transform.position, GetDirection(DirectionType.OppositePoint)))
            validTargetType = DirectionType.OppositePoint;

        else if (TargetIsValid(transform.position, GetDirection(DirectionType.SpawnPoint)))
            validTargetType = DirectionType.SpawnPoint;

        else if (TargetIsValid(transform.position, GetDirection(DirectionType.POICentered)))
            validTargetType = DirectionType.POICentered;

        else if (TargetIsValid(transform.position, GetDirection(DirectionType.OwnPosition)))
            validTargetType = DirectionType.OwnPosition;


        return transform.position + GetDirection(validTargetType) * wanderDistance.Value;
    }


    protected Vector3 GetDirection(DirectionType type)
    {
        switch(type)
        {
            case DirectionType.RandomPoint:
                return transform.forward + Random.insideUnitSphere * wanderRate.Value;
               
            case DirectionType.OppositePoint:
                return transform.forward + - (Random.insideUnitSphere * wanderRate.Value);

            case DirectionType.SpawnPoint:
                return transform.forward + aiPathAgent.spawnPoint.spawnPosition * wanderRate.Value;

            case DirectionType.POICentered:
                return transform.forward + poi.transform.position * wanderRate.Value;

            case DirectionType.OwnPosition:
                return transform.forward + transform.position * wanderRate.Value;

            default:
                return transform.position;
        }
    }

Updated Screenshot which is more clear:

One thing that stands out is that your GetDirection method does not always seem to return directions but instead positions sometimes.

For example here (but it happens in almost all of the cases)

Since you use it like this:

It will often add 2 position to each other which is definitely not what you want.

here’s an updated GetDirection() function:

protected Vector3 GetDirection(DirectionType type)
{
switch(type)
{
case DirectionType.RandomPoint:
return transform.forward + Random.insideUnitSphere * wanderRate.Value;

            case DirectionType.OppositePoint:
                return transform.forward + - (Random.insideUnitSphere * wanderRate.Value);

            case DirectionType.SpawnPoint:
                return transform.forward + (transform.position - aiPathAgent.spawnPoint.spawnPosition) * wanderRate.Value;

            case DirectionType.POICentered:
                return transform.forward + (transform.position - poi.transform.position) * wanderRate.Value;

            default:
                return transform.position;
        }
    }

is this what you meant?

here’s the updated GetTarget() Function as well.

    protected Vector3 GetTarget()
    {
        var validTargetType = DirectionType.None;

        if (TargetIsValid(transform.position, GetDirection(DirectionType.RandomPoint)))
            validTargetType = DirectionType.RandomPoint;

        else if (TargetIsValid(transform.position, GetDirection(DirectionType.OppositePoint)))
            validTargetType = DirectionType.OppositePoint;

        else if (TargetIsValid(transform.position, GetDirection(DirectionType.SpawnPoint)))
            validTargetType = DirectionType.SpawnPoint;

        else if (TargetIsValid(transform.position, GetDirection(DirectionType.POICentered)))
            validTargetType = DirectionType.POICentered;

        return transform.position + GetDirection(validTargetType) * wanderDistance.Value;
    }

Hi

No. Look at the default case for example. It returns transform.position.

Since you use it like

return transform.position + GetDirection(validTargetType) * wanderDistance.Value;

That would evaluate to

return transform.position + transform.position * wanderDistance.Value;

Which is not what you want. You would (probably) want the GetDirection method to return Vector3.zero in that case.

Alright I fixed the default case, I also forgot to normalize so here’s the updated code:

protected Vector3 GetDirection(DirectionType type)
{
switch(type)
{
case DirectionType.RandomPoint:
return transform.forward + Random.insideUnitSphere * wanderRate.Value;

            case DirectionType.OppositePoint:
                return transform.forward + -(Random.insideUnitSphere * wanderRate.Value);

            case DirectionType.SpawnPoint:
                return transform.forward + (transform.position - aiPathAgent.spawnPoint.spawnPosition) * wanderRate.Value;

            case DirectionType.POICentered:
                return transform.forward + (transform.position - poi.transform.position) * wanderRate.Value;

            default:
                return Vector3.zero;
        }
    }

protected Vector3 GetTarget()
{
var validTargetType = DirectionType.None;

        if (TargetIsValid(transform.position, GetDirection(DirectionType.RandomPoint)))
            validTargetType = DirectionType.RandomPoint;

        else if (TargetIsValid(transform.position, GetDirection(DirectionType.OppositePoint)))
            validTargetType = DirectionType.OppositePoint;

        else if (TargetIsValid(transform.position, GetDirection(DirectionType.SpawnPoint)))
            validTargetType = DirectionType.SpawnPoint;

        else if (TargetIsValid(transform.position, GetDirection(DirectionType.POICentered)))
            validTargetType = DirectionType.POICentered;

        else if (TargetIsValid(transform.position, GetDirection(DirectionType.OwnPosition)))
            validTargetType = DirectionType.OwnPosition;

        return transform.position + GetDirection(validTargetType).normalized * wanderDistance.Value;

Hi

Ok. It looks good now from what I can see.