Home > Software design >  OverlapBox returns null almost every time
OverlapBox returns null almost every time

Time:09-28

I've tried to make a script to spawn objects at a random position without them colliding with one another. It doesn't work properly as the OverlapBox returns almost every time null even when it touches a square.

Here is the script:

var quadBoundaries = quad.GetComponent<MeshCollider>().bounds;
var squareRadius = new Vector2(1, 1);

        foreach (var square in squaresToSpawn)
        {
            _isOverlapping = true;
            while (_isOverlapping)
            {
                _spawnPoint = new Vector2(Random.Range(quadBoundaries.min.x   1.5f, quadBoundaries.max.x - 1.5f), 
                    Random.Range(quadBoundaries.min.y   1.5f, quadBoundaries.max.y - 1.5f));

                _collisionWithSquare = Physics2D.OverlapBox(_spawnPoint, squareRadius, 
                    0, LayerMask.GetMask("Square Layer"));

                if (_collisionWithSquare is null)
                {
                    square.transform.position = _spawnPoint;
                    _isOverlapping = false;
                }
            }
        }   

The quadBoundaries are the boundaries of a quad I placed so the squares will randomly spawn in a limited space.
My understanding is that I am generating a random point in the quad boundaries and then I check if on that point a square of scale (1,1) will fit without touching any other thing that has a collider and is on the square layer. if it touches then I generate a new point until the collision is null so I can place the square at the designated position.
But a bunch of things that I don't understand are happening.
First, the squares are touching each other. Second, just a few specific squares are registering a collision but even those are being touched by other squares. Third, when I scale up the square radius (for example 10,10) I get a big split between the squares (shown in the picture bellow).

radius= (10,10)

I must add that all squares have a collider, are all on the square layer and the quad is on a different layer.
Can anybody explain to me what I'm not getting here? Thanks a lot!

CodePudding user response:

Before the answer I'd like to say, that such algorithm of spawning is very dangerous, because you can enteran infinity loop, when there will no be place for new square. Minimum, that you can do to make this code more safe is to add a retries count for finding a place to spawn. But I will leave it on your conscience.

To make this algorithm work, you should understand that all physics in Unity are updated in fixed update. So all operations, that you do with Phisycs or Phisics2D are working with the state of objects, that was performed in the last Pyhsics update. When you change the position of the object, physics core don't capture this changes instantly. As a workaround you can spawn each object in the fixed update separately. Like this:

public class Spawner : MonoBehaviour
{

    [SerializeField] private GameObject[] _squaresToSpawn;
    [SerializeField] private GameObject _quad;
    
    // Start is called before the first frame update
    void Start()
    {
        StartCoroutine(Spawn());
    }

    private IEnumerator Spawn()
    {
        var quadBoundaries = _quad.GetComponent<MeshCollider>().bounds;
        var squareRadius = new Vector2(1, 1);
        Vector2 spawnPoint;
        
        foreach (var square in _squaresToSpawn)
        {
            yield return new WaitForFixedUpdate();
            var isOverlapping = true;
            var retriesCount = 10;
            while (isOverlapping && retriesCount > 0)
            {
                
                spawnPoint = new Vector2(Random.Range(quadBoundaries.min.x   1.5f, quadBoundaries.max.x - 1.5f), 
                    Random.Range(quadBoundaries.min.y   1.5f, quadBoundaries.max.y - 1.5f));

                var hit = Physics2D.OverlapBox(spawnPoint, squareRadius,
                    0, LayerMask.GetMask("Square"));
                
                if (hit is null)
                {
                    square.transform.position = spawnPoint;
                    isOverlapping = false;
                }
                else
                {
                    retriesCount--;
                    if (retriesCount == 0)
                    {
                        Debug.LogError("Can't find place to spawn the object!!");
                    }
                }
            }
        }  
    }
}

But such code will have effect of continious spawning: enter image description here

To make objects spawning properly within one frame. You should manualy store all spawned objects bounding boxes inside your code, and manualy check if your new spawn bounding box collide with previously spawned objects.

  • Related