Prevent instantiation on top of other objects

So this question has been asked a lot but I can’t seem to grasp the solution. I am generating an object (for a 2d game) at a random position (within a given area) every 3 seconds. The problem is that there is nothing currently preventing the generated object from being created on top of each other. A lot of the posts I have read mention the Physics2D.OverlapCircle() method. I think I currently have the syntax right for the code using the OverlabCircle() method but I am having trouble trying to figure out how to check if two of my objects are overlapping. I thought maybe using the OnTriggerEnter2D() method would work but again can’t seem to figure out how I would implement that.

Currently here is my code for the random generation

  //Random Generation Method  
  void randomSpawn()
  {
  Vector2 randomSpawnPosition = new Vector2(Random.Range(-7.5f, 7.5f), Random.Range(-7.5f, 7.5f));
  
  instantiatedObj = (GameObject)Instantiate(spawnedObject, randomSpawnPosition, Quaternion.identity);
  
  
  if (Physics2D.OverlapCircle(instantiatedObj.transform.position, 0.64f) == Physics2D.OverlapCircle(instantiatedObj.transform.position, 0.64f))
  {
  Debug.Log ("Overlapping object");
  }
  
  } //End of randomSpawn()

I get the message “Overlapping Object” sent to the console every time an object is generated so I know that my if statement isn’t working properly.

Thanks,

  • Patrick

Alright so I managed to find this solution: [SOLVED] Physics2D OverlapCircle - Questions & Answers - Unity Discussions

Basically, he is using a Collider2D array to manage every overlap that occurs and then check to see if any overlap is valid.

Collider2D[] colliders = Physics2D.OverlapCircleAll(location, radius, layerMask);

Then check to see if there are colliders colliding.

if (colliders.Length > 0)
    Debug.Log("Colliding with layerMask!");

You can even cycle through to determine what exactly you’re hitting.

for (Collider2D col in colliders)
{
    if (col.gameObject.tag == "Enemy")
        Debug.Log("An enemy touched you!");
    else if (col.gameObject.tag == "HealthOrb")
        Debug.Log("You picked up a healing orb!");
    else
        Debug.Log("Who cares what you touched?")
}

I was recently working on a 2D top down game with random generation as well and I was using CircleCast as my method which is probably a less apt solution but it worked.

Another nifty source: What is the difference between CircleCast and OverlapCircle - Questions & Answers - Unity Discussions

Thanks for the reply and suggestions. I tried the first approach using an array of Collider2D but with no luck. Haven’t tried your other suggestions yet. Going to take a break from it this week and come back to it with a fresh look on Monday and give the other suggestions a shot.

  • Patrick

Below is the entirety of my simple world generator script for a top-down 2D game. It is in no way the most efficient but for the world size I have it does what I need it to do!

A little bit of background: I have an object I call “Validity Checker” which manages where the OverlapCircle gets called. It just teleports to a random location and then tests to see (based on a provided radius depending on an object) if it is indeed a valid location. If not the GetRandomLocation() method is recursively called.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class WorldGenerator : MonoBehaviour
{
    public Transform validityChecker;

    [Header("Chunk Details")]
    public GameObject chunk;
    [Range(1, 10)]
    public int chunkRows = 2;
    [Range(1,10)]
    public int chunkColumns = 2;
    public int chunkSize = 100;
    private float chunkTextureSize = 15;

    [Header("Building Details")]
    public GameObject[] buildings;
    public int numberOfBuildings = 5;

    [Header("Tree Details")]
    public GameObject tree;
    public int numberOfTrees = 150;

    [Header("Rock Details")]
    public GameObject[] rocks;
    public int numberOfRocks = 60;

    [Header("Foliage Details")]
    public GameObject foliage;
    public int numberOfFoliage = 150;

    private List<GameObject> chunkList = new List<GameObject>();

    private void Start() {
        chunkTextureSize = 20; // chunkSize * (2 / 3);

        for (int k = 0; k < chunkRows; k++) {
            for (int g = 0; g < chunkColumns; g++) {
                GameObject c = Instantiate(
                    chunk, new Vector3(k * chunkSize, g * chunkSize, 10),
                    Quaternion.identity) as GameObject;
                c.transform.localScale = new Vector3(chunkSize, chunkSize, 1);
                chunkList.Add(c);
            }
        }
       
        for (int j = 0; j < chunkList.Count; j++)
        { 
            int buildingsNormalized = Random.Range(0, numberOfBuildings);
            for (int i = 0; i < buildingsNormalized; i++)
            {
                float offset = Random.Range(0, 360);
                int buildingIndex = Random.Range(0, buildings.Length);
                Quaternion randomRotation = transform.rotation * Quaternion.Euler(0, 0, offset);
                Vector3 randomLocation = GetRandomLocation(chunkList[j].transform, 2);
                GameObject tmp = Instantiate(buildings[buildingIndex], randomLocation, randomRotation) as GameObject;
                tmp.transform.SetParent(chunkList[j].transform);
            }

            for (int i = 0; i < numberOfTrees; i++)
            {
                float offset = Random.Range(0, 360);
                Quaternion randomRotation = transform.rotation * Quaternion.Euler(0, 0, offset);
                Vector3 randomLocation = GetRandomLocation(chunkList[j].transform, 2);
                GameObject tmp = Instantiate(tree, randomLocation, randomRotation) as GameObject;
                float scale = Random.Range(0.9f, 1.4f);
                tmp.transform.localScale = new Vector3(scale, scale, 1);
                tmp.transform.SetParent(chunkList[j].transform);
            }

            for (int i = 0; i < numberOfRocks; i++)
            {
                float offset = Random.Range(0, 360);
                int rockIndex = Random.Range(0, rocks.Length);
                Quaternion randomRotation = transform.rotation * Quaternion.Euler(0, 0, offset);
                Vector3 randomLocation = GetRandomLocation(chunkList[j].transform, 1.2f);
                GameObject tmp = Instantiate(rocks[rockIndex], randomLocation, randomRotation) as GameObject;
                float scale = Random.Range(0.9f, 1.4f);
                tmp.transform.localScale = new Vector3(scale, scale, 1);
                tmp.transform.SetParent(chunkList[j].transform);
            }

            for (int i = 0; i < numberOfFoliage; i++)
            {
                float offset = Random.Range(0, 360);
                Quaternion randomRotation = transform.rotation * Quaternion.Euler(0, 0, offset);
                Vector3 randomLocation = GetRandomLocation(chunkList[j].transform, 0.2f);
                GameObject tmp = Instantiate(foliage, randomLocation, randomRotation) as GameObject;
                tmp.transform.localScale = new Vector3(1, 1, 1);
                tmp.transform.SetParent(chunkList[j].transform);
            }
        }

        /*
        for (int j = 0; j < chunkList.Count; j++)
        {
           
        }

        for (int j = 0; j < chunkList.Count; j++)
        {
           
        }

        for (int j = 0; j < chunkList.Count; j++)
        {
           
        } */
    }

    private Vector2 GetRandomLocation(Transform chunk, float radius) {
        Vector2 location = Vector2.zero;
        location = new Vector2
            ((float)Random.Range((-chunkSize / 2) + 1, (chunkSize / 2) - 1) + chunk.position.x,
            (float)Random.Range((-chunkSize / 2) + 1, (chunkSize / 2) - 1) + chunk.position.y);
        validityChecker.position = location;

        Collider2D[] hitColliders = Physics2D.OverlapCircleAll(validityChecker.position, radius);

        if (hitColliders.Length == 0)
            return location;
        else
            return GetRandomLocation(chunk, radius);
    }
}

Check out my post in this other thread about a similar topic:

1 Like

Hey Jeff I had been following that other thread as well and finally sat down to look at the responses. I took a look at your code and tried to understand and apply it to my game the best I could. However I still have the same problem. I am sure there is something that I am not doing right as there are a few things that I don’t fully understand.

Here is my modified code derived from your code.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Random_Generation : MonoBehaviour
{


    //private GameObject instantiatedObj; //Holds my "cloned/spawned" objects. Keep for now but is unneeded at the moment

    // list of prefabs to spawn
    public List<GameObject> spawnableObjects;

    //Layer that spawned objects are on
    public LayerMask collisionTest;

    public GameObject spawnedObject;


    // Use this for initialization
    void Start()
    {

        //Randomly Spawns Game Objects at set intervals
        //This code works!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        InvokeRepeating("randomSpawn", 3.0f, 3.0f); // Kepp this code
    }

    //Random Generation Method
    //This code works!!!!!!!!!!!!!!!!!!!!!!!!!!!
    //Errors in overlaping instantiated objects

    void randomSpawn()
    {

        //int counter;

        //Vector2 randomSpawnPosition = new Vector2(Random.Range(-7.5f, 7.5f), Random.Range(-7.5f, 7.5f)); // Keep this code, TEMP COMMENTED OUT

        //Pick the next Prefab randomly to spawn, COMMENTED OUT (DON'T NEED)
        //int randomIndex = (int)(Random.value * (spawnableObjects.Count - 1)); //Why -1?????? (DON'T NEED)

        //GameObject prefabToSpawn = spawnableObjects[randomIndex]; //COMMENTED OUT (DON'T NEED)

        //Choose a position that doesn't overlap on the specified layermask

        Vector2 randomSpawnPosition = getValidSpawnLocation(spawnedObject);


        // Spawns my object
        Instantiate(spawnedObject, randomSpawnPosition, Quaternion.identity); //Keep this code, changed spawnedObject to prefabToSpawn for this test


        //Below code currently seems to be unneeded at the moment but keep it just in case. Not sure why I needed it in the first place.
        //instantiatedObj = (GameObject)Instantiate(spawnedObject, randomSpawnPosition, Quaternion.identity); // Keep this code

    } //End of randomSpawn()

    private Vector2 getValidSpawnLocation(GameObject go)
    {
        Collider2D objectCollider = go.GetComponent<Collider2D>();// Creates a variable called objectCollider and assigns the 2D Collider Component from the Game Object that is being passed into the Method.

        Vector3 newPosition = Vector2.zero; //sets the new postion to (0,0,0). Changed to Vector 3 3 in my code because of the objectCollider.bounds.extents which is a Vector 3

        bool validPosition = false; //Boolian for getting out of the Do/While loop

        do
        {
            // get a random position for the X and Y positional coordanintes on screen
            newPosition.x = Random.Range(-7.5f, 7.5f);

            newPosition.y = Random.Range(-7.5f, 7.5f);

            //??????BELOW CODE?????????
            // get the corners of the object at the new position
            Vector3 min = newPosition - objectCollider.bounds.extents; //What does this do and why do I need it

            Vector3 max = newPosition + objectCollider.bounds.extents; //What does this do and why do I need it

            // !!!!!!!!!!!!!!!!CHECKS FOR OVERLAPING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            Collider2D[] overlapObjects = Physics2D.OverlapAreaAll(min, max, collisionTest);

            //if it does not overlap any objects in the layermask

            if (overlapObjects.Length == 0)
            {
                Debug.Log("good");

                //break out of the Do/While loop
                validPosition = true;
            }

            else
                Debug.Log("Overlapping has occured");

        } while (!validPosition);



        return newPosition;
    }

} // End of Random_Generation class

The console will out put the message “good” every time an object spawns but it will still spawn an object on top of another one and it will not display the message “Overlapping has occurred”.

Hey there, sorry that you’re having problems with the code. I will test it out as soon as I can, and come back with an explanation as to what was going wrong and what is happening in the code.

Hey there. So it looks like prefabs do not know their bounds size until they are spawned. So there’s two ways to go about this. Spawn it off screen, then place it, or define the size beforehand in the inspector.

Here are those two solutions. (Note that I changed your hard-coded 7.5 random range for spawning to use the screen bounds. Feel free to change it back if that’s not desired.

Remember your object must have a collider and the LayerMask must contain the layer of the spawned object.

Spawn first, then position:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Random_Generation : MonoBehaviour {


    //private GameObject instantiatedObj; //Holds my "cloned/spawned" objects. Keep for now but is unneeded at the moment

    // list of prefabs to spawn
    public List<GameObject> spawnableObjects;

    //Layer that spawned objects are on
    public LayerMask collisionTest;

    public GameObject spawnedObject;


    // Use this for initialization
    void Start() {

        //Randomly Spawns Game Objects at set intervals
        //This code works!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        InvokeRepeating("randomSpawn", 3.0f, 3.0f); // Kepp this code
    }

    //Random Generation Method
    //This code works!!!!!!!!!!!!!!!!!!!!!!!!!!!
    //Errors in overlaping instantiated objects

    void randomSpawn() {

        //int counter;

        //Vector2 randomSpawnPosition = new Vector2(Random.Range(-7.5f, 7.5f), Random.Range(-7.5f, 7.5f)); // Keep this code, TEMP COMMENTED OUT

        //Pick the next Prefab randomly to spawn, COMMENTED OUT (DON'T NEED)
        //int randomIndex = (int)(Random.value * (spawnableObjects.Count - 1));
        // Why -1?????? (DON'T NEED)
        // -1 because an index starts at 0.  If you have a count of 10, the final index will be 9.

        //GameObject prefabToSpawn = spawnableObjects[randomIndex]; //COMMENTED OUT (DON'T NEED)

        // Spawns my object off screen
        GameObject newObject = (GameObject)Instantiate(spawnedObject, new Vector3(-100, -100, -100), Quaternion.identity); //Keep this code, changed spawnedObject to prefabToSpawn for this test


        //Choose a position that doesn't overlap on the specified layermask
        Vector2 randomSpawnPosition = getValidSpawnLocation(newObject);
        if(randomSpawnPosition == Vector2.zero) {
            Debug.Log("no more room!");
            Destroy(newObject);
        } else {
            newObject.transform.position = randomSpawnPosition;
        }

        //Below code currently seems to be unneeded at the moment but keep it just in case. Not sure why I needed it in the first place.
        //instantiatedObj = (GameObject)Instantiate(spawnedObject, randomSpawnPosition, Quaternion.identity); // Keep this code

    } //End of randomSpawn()

    private Vector2 getValidSpawnLocation(GameObject go) {
        Collider2D objectCollider = go.GetComponent<Collider2D>();// Creates a variable called objectCollider and assigns the 2D Collider Component from the Game Object that is being passed into the Method.

        Vector3 newPosition = Vector2.zero; //sets the new postion to (0,0,0). Changed to Vector 3 3 in my code because of the objectCollider.bounds.extents which is a Vector 3

        bool validPosition = false; //Boolian for getting out of the Do/While loop

        // failsafe to prevent infinite loops
        int failureLimit = 100;
        int fails = 0;

        do {
            // get the bottom left corner of the screen in world space
            Vector2 screenMin = Camera.main.ViewportToWorldPoint(Vector3.zero); // Vector3.zero = same as new Vector3(0,0,0)
            // get the top right corner of the screen in world space
            Vector2 screenMax = Camera.main.ViewportToWorldPoint(Vector3.one); // Vector3.one = same as new Vector3(1,1,1)

            // get a random position for the X and Y positional coordanintes on screen
            newPosition.x = Random.Range(screenMin.x, screenMax.x);
            newPosition.y = Random.Range(screenMin.y, screenMax.y);

            // these next two values are passed to the "OverlapAreaAll" function to tell it
            // what area to check for collisions.  They are opposite corners of a square.

            // we add and subtract the object collider's half-width (extents) from the new position
            // to get the opposing corners of the square area that the object will take up
            // at the new position
            Vector3 min = newPosition - objectCollider.bounds.extents;
            Vector3 max = newPosition + objectCollider.bounds.extents;

            // !!!!!!!!!!!!!!!!CHECKS FOR OVERLAPING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            Collider2D[] overlapObjects = Physics2D.OverlapAreaAll(min, max, collisionTest);

            //if it does not overlap any objects in the layermask

            if(overlapObjects.Length == 0) {
                Debug.Log("good");

                //break out of the Do/While loop
                validPosition = true;

                // reset the failsafe
                fails = 0;
            } else {
                Debug.Log("Overlapping has occured");

                fails++;
            }

            // added a fail-safe to prevent infinite looping if there is no space left
        } while(!validPosition && fails < failureLimit);

        return newPosition;
    }

} // End of Random_Generation class

Define the size in the inspector:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Random_Generation : MonoBehaviour {


    //private GameObject instantiatedObj; //Holds my "cloned/spawned" objects. Keep for now but is unneeded at the moment

    // list of prefabs to spawn
    public List<GameObject> spawnableObjects;

    //Layer that spawned objects are on
    public LayerMask collisionTest;

    public GameObject spawnedObject;

    public Vector2 spawnedObjectSize;

    private Vector2 spawnedObjectHalfSize;

    // Use this for initialization
    void Start() {
        spawnedObjectHalfSize = spawnedObjectSize * 0.5f;
        //Randomly Spawns Game Objects at set intervals
        //This code works!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        InvokeRepeating("randomSpawn", 3.0f, 3.0f); // Kepp this code
    }

    //Random Generation Method
    //This code works!!!!!!!!!!!!!!!!!!!!!!!!!!!
    //Errors in overlaping instantiated objects

    void randomSpawn() {

        //int counter;

        //Vector2 randomSpawnPosition = new Vector2(Random.Range(-7.5f, 7.5f), Random.Range(-7.5f, 7.5f)); // Keep this code, TEMP COMMENTED OUT

        //Pick the next Prefab randomly to spawn, COMMENTED OUT (DON'T NEED)
        //int randomIndex = (int)(Random.value * (spawnableObjects.Count - 1));
        // Why -1?????? (DON'T NEED)
        // -1 because an index starts at 0.  If you have a count of 10, the final index will be 9.

        //GameObject prefabToSpawn = spawnableObjects[randomIndex]; //COMMENTED OUT (DON'T NEED)


        //Choose a position that doesn't overlap on the specified layermask
        Vector2 randomSpawnPosition = getValidSpawnLocation(spawnedObject);

        if(randomSpawnPosition == Vector2.zero) {
            Debug.Log("no more room!");
        } else {
            // Spawns my object
            Instantiate(spawnedObject, randomSpawnPosition, Quaternion.identity); //Keep this code, changed spawnedObject to prefabToSpawn for this test
        }

        //Below code currently seems to be unneeded at the moment but keep it just in case. Not sure why I needed it in the first place.
        //instantiatedObj = (GameObject)Instantiate(spawnedObject, randomSpawnPosition, Quaternion.identity); // Keep this code

    } //End of randomSpawn()

    private Vector2 getValidSpawnLocation(GameObject go) {
        Collider2D objectCollider = go.GetComponent<Collider2D>();// Creates a variable called objectCollider and assigns the 2D Collider Component from the Game Object that is being passed into the Method.

        Vector3 newPosition = Vector2.zero; //sets the new postion to (0,0,0). Changed to Vector 3 3 in my code because of the objectCollider.bounds.extents which is a Vector 3

        bool validPosition = false; //Boolian for getting out of the Do/While loop

        // failsafe to prevent infinite loops
        int failureLimit = 100;
        int fails = 0;

        do {
            // get the bottom left corner of the screen in world space
            Vector2 screenMin = Camera.main.ViewportToWorldPoint(Vector3.zero); // Vector3.zero = same as new Vector3(0,0,0)
            // get the top right corner of the screen in world space
            Vector2 screenMax = Camera.main.ViewportToWorldPoint(Vector3.one); // Vector3.one = same as new Vector3(1,1,1)

            // get a random position for the X and Y positional coordanintes on screen
            newPosition.x = Random.Range(screenMin.x, screenMax.x);
            newPosition.y = Random.Range(screenMin.y, screenMax.y);

            // these next two values are passed to the "OverlapAreaAll" function to tell it
            // what area to check for collisions.  They are opposite corners of a square.

            // we add and subtract the object collider's half-width (extents) from the new position
            // to get the opposing corners of the square area that the object will take up
            // at the new position
            Vector3 min = newPosition - (Vector3)spawnedObjectHalfSize;
            Vector3 max = newPosition + (Vector3)spawnedObjectHalfSize;

            // !!!!!!!!!!!!!!!!CHECKS FOR OVERLAPING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            Collider2D[] overlapObjects = Physics2D.OverlapAreaAll(min, max, collisionTest);

            //if it does not overlap any objects in the layermask

            if(overlapObjects.Length == 0) {
                Debug.Log("good");

                //break out of the Do/While loop
                validPosition = true;

                // reset the failsafe
                fails = 0;
            } else {
                Debug.Log("Overlapping has occured");

                fails++;
            }

            // added a fail-safe to prevent infinite looping if there is no space left
        } while(!validPosition && fails < failureLimit);

        return newPosition;
    }

} // End of Random_Generation class
1 Like

Thanks for looking into the problem a little deeper. I will be getting back to this game and my other one with the color switching blocks tomorrow. I will let you know how it works out.

  • Patrick