How to ensure that spawned targets do not overlap ?

Hi Everyone,

I wrote a small method which clones a specific object 3 assign each one of them a color {red, blue and yellow} and spawns them at random locations within a square of size {12,12,12}. These targets can however overlap and I was curious if there is a way to ensure that they always maintain a minimal distance from each other, so that they do not spawn within each other. Since each objects has a collider I figured something like Physics.CheckSphere could do the trick. Any concrete examples that could help me understand how this could work ?
Id be happy to learn more.

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


public class Gen_Target : MonoBehaviour
{
    // defines a  Gameobject "Targetprefab" to be selected in the Unity envir.
    // that is to be generated/cloned n number of times (see int numTargets=3)
    public GameObject Targetobj;
    public Game_Manager GameManager;
    // defines a Gameobject "goal", which is the initial green goal object in the ML agents reacher example
    public GameObject goal;
    public GameObject agent;
    // defines the condition whether target generation is at random locations or fixed to be select in Unity envir.
    public bool random_location = false;

    // define two vectors of size 3 to store x,y,z coordinates, centered and sized
    // according to the size of the empty game object, which defines all possible
    // location in 3D space for 4 targets to spawn randomly
    // center used to determine middle point of the 3D cubic spawning area

    public Vector3 center;
    public Vector3 size;

    // Defines parameters for targets, since we start with 1 inital target, if
    // total number of targets desired is 4, numTargets = 3 new targets + 1 initial target = 4 total
    int numTargets = 3;
    int i = 0;

    void Start()
    {
    }

    public void init()
    {

        if (random_location == true)
        {
            //randomize location of initial goal/target object
            Vector3 pos = center + new Vector3(Random.Range(-size.x / 2, size.x / 2), Random.Range(-size.y / 2, size.y / 2), Random.Range(-size.z / 2, size.z / 2));
            goal.transform.position = pos;
        }
        else if (random_location == false)
        {
            Vector3 pos = new Vector3(8.5f + agent.transform.position.x, -3.1f + agent.transform.position.y, 0.9f + agent.transform.position.z);
        }

        while (i != numTargets)
        {
            SpawnTarget(i);
            i = i + 1;
        }
    }

    // define a color map for targets
    Color[] colors = { Color.yellow, Color.blue, Color.red };
    string[] colorNames = { "Yellow", "Blue", "Red" };

    public void SpawnTarget(int i)
    {
        if (random_location == true)
        {
            // takes center of empty square game object, plus minus max ranged divided 2 for x,y,z to generate
            // numTargets within the space at random location
            Vector3 pos = center + new Vector3(Random.Range(-size.x / 2, size.x / 2), Random.Range(-size.y / 2, size.y / 2), Random.Range(-size.z / 2, size.z / 2));
            GameObject newObject = Instantiate(Targetobj, pos, Quaternion.identity);
            // newObject sets parent to parent of Targetobj
            newObject.transform.parent = Targetobj.transform.parent;
            // assignes tag = "Target" for each generated target
            newObject.tag = "Target";
            // change color of Targets
            newObject.GetComponent<Renderer>().material.color = colors[i];
            newObject.transform.name = colorNames[i];
        }
        else if (random_location == false)
        {
            // takes center of empty square game object, plus minus max ranged divided 2 for x,y,z to generate
            // numTargets within the space at random location
            //Vector3 pos = center + new Vector3((size.x / 2 )+i, (size.y / 2)+i, (size.z / 2)+i);
            Vector3 pos = new Vector3(8.5f + agent.transform.position.x, -3.1f + agent.transform.position.y, 0.9f + agent.transform.position.z);
            switch (i)
            {
                case 0:
                    pos = new Vector3(-8.5f + agent.transform.position.x, -3.1f + agent.transform.position.y, 0.9f + agent.transform.position.z);
                    //pos = center + new Vector3(-8 , 0, 0);
                    break;
                case 1:
                    //pos = center + new Vector3(0, 0, -12 );
                    pos = new Vector3(0 + agent.transform.position.x, -3.1f + agent.transform.position.y, -8.5f + agent.transform.position.z);
                    break;
                case 2:
                    //pos = center + new Vector3(0 , 0, 12);
                    pos = new Vector3(0 + agent.transform.position.x, -3.1f + agent.transform.position.y, 8.5f + agent.transform.position.z);
                    break;
            }
            //Vector3 pos = center + new Vector3(8+i, -3, 1+i);
            GameObject newObject = Instantiate(Targetobj, pos, Quaternion.identity);
            // newObject sets parent to parent of Targetobj
            newObject.transform.parent = Targetobj.transform.parent;
            // assignes tag = "Target" for each generated target
            newObject.tag = "Target";
            // change color of Targets
            newObject.GetComponent<Renderer>().material.color = colors[i];
            newObject.transform.name = colorNames[i];
        }
    }

    // Defines color, cube object form of the empty game object
    void OnDrawGizmosSelected()
    {
        Gizmos.color = new Color(1, 0, 0, 0.5f);
        Gizmos.DrawCube(center, size);
    }

    void Update()
    {
        //Debug.Log(GameManager.sequence_end + "!!!");
        if (GameManager.sequence_end == true)
        {
            Vector3 pos = center + new Vector3(Random.Range(-size.x / 2, size.x / 2), Random.Range(-size.y / 2, size.y / 2), Random.Range(-size.z / 2, size.z / 2));
            goal.transform.position = pos;
            //Debug.Log(GameManager.sequence_end + "!!!!");
            GameManager.sequence_end = false;
        }
    }
}

Tons of ways… best to work through some tutorials to see what I mean.

Most straightforward way is keep track of where each one is randomly spawned, then when you do the next one, check all the previous existing ones and if it’s too close, rechoose.

Be careful you don’t spawn so many that you cannot fit even one more because your loop would never exit and Unity will freeze.

1 Like

Kurt-Dekker can you provide an example please? I am in a similar situation and can’t seem to figure this out… Many thanks.

Hi i am begginer so maybe i am not right but something like this should work.

using System.Collections.Generic;
using UnityEngine;

namespace Assets.Scripts
{
    public class NewMonoBehaviour : MonoBehaviour
    {
        public GameObject objectForSpawn;
        private List<SpawnObject> spawnObjectList;
        private int ammountObjectsYouWant = 50;

        private void Start()
        {
            spawnObjectList = new List<SpawnObject>();
            CreateObjects();


        }
        void CreateObjects()
        {
            for (int i = 0; i < ammountObjectsYouWant; i++)
            {
                Vector3 objectPosition = new Vector3(Random.Range(1, 100), Random.Range(1, 100), Random.Range(1, 100));
                SpawnObject sO = new SpawnObject(objectPosition);
                spawnObjectList.Add(sO);
            }
            SpawnGameObject();
            void SpawnGameObject()
            {

                List<SpawnObject> spawnedObjects = new List<SpawnObject>();
                List<SpawnObject> notSpawnedObjects = new List<SpawnObject>();

                foreach (SpawnObject item in spawnObjectList)
                {
               Vector3 spawnPosition = new Vector3(Random.Range(1, 100), Random.Range(1, 100), Random.Range(1, 100));
                    if (item.objectPosition != spawnPosition)
                    {
                        Instantiate(objectForSpawn, spawnPosition, transform.rotation);
                        spawnedObjects.Add(item);
                    }
                    else
                    {
                        notSpawnedObjects.Add(item);
                    }
                }
                Debug.Log("Object spawned :" + spawnedObjects.Count);
                Debug.Log("Object notSpawned :" + notSpawnedObjects.Count);
            }
        }
 

    }
    public class SpawnObject
    {
        public Vector3 objectPosition;
        public SpawnObject(Vector3 objectPosition)
        {
            this.objectPosition = objectPosition;
        }

    }
}

Problem here can be if your loop will generate random number with same seed then you can get same result but that is a different story.Thread.Sleep can help for example.

Also if you want handle overlaping just add some offset Vector3 with size of your game objects.

If you’re using colliders, you could SphereCast at the position you intend to spawn. If nothing is found you spawn there, if another spawned object is found, you pick another random spawn location. Limit yourself to a certain number of tries before you give up that frame, so you don’t get stuck in an infinite loop if every possible spawn location is full.

A trick often used is to make an object that checks if that spot is free like a mark. if there is nothing there the spawned object gets placed. Or make the new spawned object only visible if nothing is overlapping.

1 Like

I think it is better to check it without unity first because if you will have 1000000 gameobjects your solution will need 1000000 colliders.

I dont know what you try to achiev but i do something similar in my game and my code give you this.In case position is same object dont spawn

using System.Collections.Generic;
using UnityEngine;

namespace Assets.Scripts
{
    public class NewMonoBehaviour : MonoBehaviour
    {

 
        public GameObject objectForSpawn;
        private List<SpawnObject> spawnObjectList;
        private int objectCount = 50;

        private void Start()
        {
            CreateCollections();
            Generate(objectCount);
        }
        void CreateCollections()
        {
            spawnObjectList = new List<SpawnObject>();
        }
        void Generate(int objectCount)
        {
            CreateObjects();
            void CreateObjects()
            {
                for (int i = 0; i < objectCount; i++)
                {
                    Vector3 objectPosition = GetPosition();
                    SpawnObject sO = new SpawnObject(objectPosition);
                    spawnObjectList.Add(sO);
                }
            }     
            SpawnGameObjects();
            void SpawnGameObjects()
            {
            
                List<SpawnObject> spawnedObjects = new List<SpawnObject>();
                List<SpawnObject> notSpawnedObjects = new List<SpawnObject>();

                foreach (SpawnObject item in spawnObjectList)
                {
                    Vector3 spawnPosition = GetPosition();
                    if (item.objectPosition != spawnPosition)
                    {
                        Instantiate(objectForSpawn, spawnPosition, transform.rotation);
                        spawnedObjects.Add(item);
                    }
                    else
                    {
                        notSpawnedObjects.Add(item);
                    }
                }
                Debug.Log("Object spawned :" + spawnedObjects.Count);
                Debug.Log("Object notSpawned :" + notSpawnedObjects.Count);
            }
        }
        Vector3 GetPosition()
        {
            Vector3 randmPosition;
            randmPosition.x = Random.Range(1, 100);
            randmPosition.y = Random.Range(1, 100);
            randmPosition.z = Random.Range(1, 100);
            return randmPosition;
        }
    }
    public class SpawnObject
    {
        public Vector3 objectPosition;
        public SpawnObject(Vector3 objectPosition)
        {
            this.objectPosition = objectPosition;
        }

    }
In case you want serialize this position for example for save you better use float[3] for position then vector.
}

I recreated the trick i mentioned earlier. Scripts can be found at my place if you can use them.

where is your script i wanted to check it take so long to spawn few objects but i cant find them in video.
I still think it is better to validate position only by comparing vectors or float[ ] then check collider with raycast but maybe i miss something

nvm found it on your web

Good to hear i hope it helps you out. just follow my profile link.