I'm working with C# in Unity, and I'm making a top-down space shooter game in which there are many different AI-controlled ships flying around. The problem is that these ships often fly on top of one another in passing, and sometimes even will fly the same route on top of one another if given the same waypoint. I want to add a system to steer my AI ships away from each other as well as other obstacles so that instead of overlapping one another when flying the same path they will fly side-by-side, and will also try to avoid things that pass in front of them.

My current system essentially works like this: The AI looks at all of the current objectives, points of interests, etc, and decides where it wants to be on the map (a coordinate). The AI then sends an input direction to the ship, which then applies acceleration in that direction and changes the velocity. Therefore, any solution to alter my ship's pathing would need to alter this input direction somehow.

So my first idea was to check if there are any obstacles (ships, asteroids, structures, etc) within a radius of the ship, increasing this radius based on the ship's velocity (higher velocity = larger "view" radius):

Collider2D[] hits = Physics2D.OverlapCircleAll(transform.position, ship.velocity.magnitude);

If there is something nearby, calculate the direction to that collision, then calculate the opposite direction and scale it to the reciprocal, which also has the added effect of making the aversion stronger the shorter the distance to the collision is:

foreach (Collider2D hit in hits)

    if (hit.gameObject == gameObject) continue; // exclude self from the search

    Vector2 v = hit.ClosestPoint(transform.position) - (Vector2)transform.position;
    v = (1 / v.magnitude) * -v.normalized;

I would perform this for every nearby obstacle and add all of these together to get the avoidance vector:

    avoidanceVector += v;

Then I add the total avoidance vector to the AI's input direction and normalize the result:

inputDirection += avoidanceVector;

However, when i add this avoidance vector to my input direction, my ships exhibit a strange behavior where sometimes they will simply stop moving completely. commenting out the line where i add them together stops this behavior.

I'm really not sure what could be going wrong here, but I think it has to do with my approach altogether. Can anyone help me figure out a clean, simple way to make my ships steer away from other objects?

  • \$\begingroup\$ Which magnitude has the input direction vector and the avoidance vector in different circumstances? (I think that 1 in v = (1 / ... is too high and avoidance vector has too large magnitude). \$\endgroup\$ – trollingchar Sep 6 at 10:27
  • \$\begingroup\$ Wait, do you have a check if your ship and a ship that you avoid are one and the same? Strange things may happen if you try to flee from yourself. \$\endgroup\$ – trollingchar Sep 6 at 10:29
  • \$\begingroup\$ It sounds like you're developing a flocking algorithm. A good keyword to google here is boids. \$\endgroup\$ – BlueRaja - Danny Pflughoeft Sep 6 at 19:13
  • \$\begingroup\$ @trollingchar yes, at the beginning of the loop I have: if (hit.gameObject == gameObject) continue; I'll edit my post to reflect that. Also, in regards to using 1 / x, the avoidance vector is actually too small unless the ship is very close to the collision, which makes sense - you don't need to steer away from something too hard unless you're about to hit it. \$\endgroup\$ – Agent Tatsu Sep 6 at 19:44
  • \$\begingroup\$ @BlueRaja-DannyPflughoeft yeah, I actually was inspired by Sebastian Lague's recent video on boids. I'll go back and look again at it, as since it was a few days ago that i watched it i may have missed a step in the algorithm, but I'm pretty sure I've got the right idea. I just can't seem to write the proper code for it. \$\endgroup\$ – Agent Tatsu Sep 6 at 19:48

The problem with my code was with the way Unity's built-in function Collider2D.ClosestPoint(Vector2 position) works. The point returned by the function is not necessarily a point along the edge of the collider like I assumed it was - instead, if position is inside the collider, then it returns the same point. This creates a NaN when you divide by the magnitude of the distance between the two calculated points, which is zero.

Changing hit.ClosestPoint(transform.position) to hit.transform.position works well enough to steer them out of the area, but it does not take into account the fastest way to get out of the area.


Your Answer

