1
\$\begingroup\$

I am making a simple group selection and movement system in Unity and so far it is great. However, when I move a group of units to a location, they all bunch up on top of each other.

So I instead picked a point around the location and moved all of the units to their own location at an offset from the position they were told to move to. This also works okay and I'm happy enough with this approach.

However, on their way to the destination, my sprites will overlap and crossover each other. I would instead prefer they respect each others colliders and move around each other in order to reach their destination.

I'm sure there's some better approaches to this (to which I'm all ears if you have any suggestions), however my initial thought was to first try to predict a collision and make a unit stop moving until the coast was clear.

My issues are that I can't seem to properly predict when a collision might occur. Here is the general approach I have taken:

private void Update() {
    if (moving) {
        // stop moving if we're close
        if (Vector3.Distance(moveDestination, transform.position) < 0.05f) {
            moveDirection = Vector3.zero;
            moving = false;
        }

        var moveDelta = moveDirection * moveSpeed * Time.deltaTime;
        var newPosition = transform.position + moveDelta;

        // only move if we're not going to collide with something
        if (!HitTest(newPosition)) {
            transform.position = newPosition;
        }
    }
}

private bool HitTest(Vector3 newPosition) {
    // Find any colliders close by that aren't MY collider
    var colliders = Physics2D.OverlapBoxAll(newPosition, boxCollider.size, 0f).Where(x => x != boxCollider).ToList();
    Debug.Log(colliders.Count());
    return colliders.Count() > 0;
}  

The result is that the units seem to stop in completely random ways. They will stop moving .. even when moving away from a collider thats in the other direction. I am unable to figure out why the collider API's for overlapping aren't producing the results I desire. Even if I use one of the others (like Ray Casting in a direction) it still seems to return colliders that aren't in that direction.

Is there something obvious I am misunderstanding about these collision APIs? Can my idea be improved? I am open to any and all suggestions.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Here are some general tips and advice on how to troubleshoot this problem yourself:

  • Using Physics functions like OverlapBoxAll() is not the best way to predict collisions. OverlapBoxAll() tells you where obstacles are now, not where they'll be at some point in the future. Depending on how far into the future you predict, this can give you wildly inaccurate results. True prediction involves estimating where other obstacles will be at that point in the future.
  • It looks like you're predicting one frame in the future, every frame. This can give jittery, unrealistic looking results. In my experience, it's usually better to predict at least a few frames into the future; the exact window depends on how close together your units are.
  • When you're having trouble understanding what's going on with collision prediction code, use visualizations to help. For example:
private bool HitTest(Vector3 newPosition) {
    // Find any colliders close by that aren't MY collider
    var colliders = Physics2D.OverlapBoxAll(newPosition, boxCollider.size, 0f);
    //draw a red line from me to any predicted obstacles. make sure gizmos are enabled
    if (drawDebugLines) { //new serialized field
        for (int i = 0; i < colliders.Length; i++) {
            var other = colliders[i];
            if (other != myCollider) {
                Debug.DrawLine(transform.position, other.transform.position, Color.red, .1f);
            }
        }
    }

    Debug.Log(colliders.Length);
    //much more efficient way to check the colliders:
    if (colliders.Length > 1) return true;
    if (colliders.Length == 0) return false;
    return colliders[0] != myCollider;
}  

By the way, the method you're using to check collisions is terribly inefficient:

var colliders = Physics2D.OverlapBoxAll(newPosition, boxCollider.size, 0f).Where(x => x != boxCollider).ToList();

  • OverlapBoxAll() allocates an array. You should generally use OverlapBoxNonAlloc() instead
  • The Linq Where() extension method will most likely allocate some type of object (see the source code) and probably isn't optimal for maximum performance.
  • ToList() allocates yet another object. All of these allocations generate memory garbage which in turn means that expensive garbage collection occurs more frequently.

If you know that OverlapBoxAll() will always include your own collider, you can just subtract 1 from the count. Even if you're not sure, it's easy to check without using Linq. See my modified HitTest() function above.

\$\endgroup\$
2
  • \$\begingroup\$ Thank you! I appreciate you mentioning the allocations as it's likely I would've just flippantly thrown these things into my code without thinking about it. These debug things are great - thanks so much! \$\endgroup\$ Jan 29 at 1:11
  • \$\begingroup\$ @SimonWhitehead Glad to help. \$\endgroup\$
    – Kevin
    Jan 29 at 1:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .