AI simply stops working when there are large amounts of them

As the title says, when there is even (what I would call) a minute number of AI (20+) they simply freeze or behave very strangely.

How it is expected to behave (and does so properly with just 1)
Apparently I can’t put images in posts since I’m new soooo:
Note: they’re just gifs of the player running around the enemies
*http://i.gyazo.com/9b7b3e844af318b0635c6fd545353721.gif

And how it behaves with <20
*http://i.gyazo.com/61d590415e09cc54313046535575a5e1.gif

As you can see they simply don’t respond, and the Gizmo that shows the pathing shows that the path doesn’t get updated.
This occurs with both the example script and with my own.

Using a NavMesh graph if it matters.

The output for the successful completion of 1 path:

Path Completed : Computation Time 3.002 ms Searched Nodes 16 Path Length 9
Search Iterations 1
End Node
    G: 3277
    H: 101
    F: 3378
    Point: (-199.8, 94.1, 0.0)
    Graph: 0
Start Node
    Point: (-199.9, 91.6, 0.0)
    Graph: 0
Path Number 2103

And when there’s a ton of them the console loses its mind and spews out thousands of this, even after pausing execution:

Path Failed : Computation Time 0.000 ms Searched Nodes 0
Error: Canceled path because a new one was requested.
This happens when a new path is requested from the seeker when one was already being calculated.
For example if a unit got a new order, you might request a new path directly instead of waiting for the now invalid path to be calculated. Which is probably what you want.
If you are getting this a lot, you might want to consider how you are scheduling path requests.
Path Number 2041

Code that pertains to the pathing:

private void Start()
{
    Seeker = GetComponent<Seeker>();

    Seeker.pathCallback += OnPathComplete;

    //This coroutine has nothing to do with pathing but maybe multiple coroutines causes issues?
    UpdateAttackCoroutine = StartCoroutine(UpdateAttack());
    UpdatePathCoroutine = StartCoroutine(UpdatePath());
}

private void FixedUpdate()
{
    Vector2 rdir = (TargetOverride.position - EntityBody.transform.position).normalized;
    Debug.DrawRay(EntityBody.position, rdir, Color.red);
    RaycastHit2D hit = Physics2D.Raycast(EntityBody.position, rdir, 15, RaycastLayers);
    if (hit.collider == TargetOverride.GetComponentInParent<Collider2D>())
        RaycastSuccess = true;
    else
        RaycastSuccess = false;

    if (Path == null)
    {
        EntityBody.velocity = new Vector2(0, EntityBody.velocity.y);
        return;
    }

    if (CurrentWaypoint >= Path.vectorPath.Count)
    {
        if (IsPathEnded)
            return;
        IsPathEnded = true;
    }
    else
    {
        IsPathEnded = false;

        Vector2 dir = (TargetOverride.transform.position - Path.vectorPath[CurrentWaypoint]).normalized;

        dir *= Acceleration * Time.fixedDeltaTime;
        if (!RangedAggro)
            dir.y = 0;

        EntityBody.AddForce(dir, FMode);
        EntityBody.velocity = new Vector2(Mathf.Clamp(EntityBody.velocity.x, -MaxSpeed, MaxSpeed), EntityBody.velocity.y);

        //Debug.Log(string.Format("TP: {0} - VP: {1} - DIR: {2} - VEL: {3}", TargetOverride.transform.position, Path.vectorPath[CurrentWaypoint], dir, EntityBody.velocity));

        float dist = Vector3.Distance(TargetOverride.transform.position, Path.vectorPath[CurrentWaypoint]);

        //Debug.Log(string.Format("DIST: {0} - CW: {1} - MTW: {2}", dist, CurrentWaypoint, MaxToWaypoint));

        if (dist < 0.5f)
            EntityBody.velocity = new Vector2(0, EntityBody.velocity.y);

        if (dist < MaxToWaypoint)
            CurrentWaypoint++;
    }
}

IEnumerator UpdatePath()
{
    if (RaycastSuccess)
    {
        //float dist = Vector2.Distance(EntityBody.position, TargetOverride.position);
        //float xdist = Mathf.Abs(EntityBody.position.x - TargetOverride.position.x);
        //float ydist = Mathf.Abs(EntityBody.position.y - TargetOverride.position.y);
        //if (xdist < 15 && ydist < 5)
            Seeker.StartPath(EntityBody.position, TargetOverride.position);
    }
    else
        Path = null;
    yield return new WaitForSeconds((1f / UpdateRate) + Random.value);
    StartCoroutine(UpdatePath());
}

Hi

What is happening is that you are requesting paths in loop. But if the time it takes for a path to complete is greater than the delay between each iteration in the loop the paths will just get cancelled all the time (which the error message that you get from the path says). Before you search for a path, check the Seeker. IsDone method to see if the previous path is done calculating.

Also you might want to enable multithreading in the settings to get better performance.

Thank you that corrected the problem, but they now have a bit of a delay in their reaction time. Would this be fixed if I were to have it on many more threads in the paid version? Any way to resolve it in the free version?

Hi

The wait would be reduced on computers with more than 2 cores with more threads.

How does your graph look?
If you can reduce the number of nodes in it that will help a lot.
Also you can take a look at http://arongranberg.com/astar/docs_dev/optimization.php

Since you are using a NavMeshGraph then your map is likely static so using heuristic optimizations might be a good fit (see page above).

The graph is currently very simple as I’m just testing things and trying to get the basics done.
*http://i.gyazo.com/de6ef0ea34f731bd28a064e43e358b62.png
For reference, a player is roughly 2 of the squares large.

What exactly do you mean by “reduce the number of nodes”, and how much do you believe it would help?
According to the page you linked I would have to purchase the Pro version, which, unfortunately, is currently out of my budget.

As an estimate, how much of a performance increase do you believe these Pro features would supply?

Hi

Reducing the number of nodes in the graph as in reducing the resolution of it (e.g by reducing the width or depth and increasing the node size). If there are fewer nodes to search then the searches will be faster.

You could also try to make the agents search for a new path less often by default (which reduces the load on the system), but when you give them an order directly you call a method to update the path immediately, that will likely improve their reaction time a lot.

It is very hard to say how much the pro version would increase performance. In raw pathfinding speed it could probably at least double the performance. However I think trying to change the way the paths are requested (see previous section) could get you a long way.

Okay thank you, I’ll be looking into this for some time.