Search Unity

Question How to ensure that spawned targets do not overlap ?

Discussion in 'Scripting' started by weight_theta, Apr 16, 2021.

  1. weight_theta

    weight_theta

    Joined:
    Aug 23, 2020
    Posts:
    65
    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.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5.  
    6. public class Gen_Target : MonoBehaviour
    7. {
    8.     // defines a  Gameobject "Targetprefab" to be selected in the Unity envir.
    9.     // that is to be generated/cloned n number of times (see int numTargets=3)
    10.     public GameObject Targetobj;
    11.     public Game_Manager GameManager;
    12.     // defines a Gameobject "goal", which is the initial green goal object in the ML agents reacher example
    13.     public GameObject goal;
    14.     public GameObject agent;
    15.     // defines the condition whether target generation is at random locations or fixed to be select in Unity envir.
    16.     public bool random_location = false;
    17.  
    18.     // define two vectors of size 3 to store x,y,z coordinates, centered and sized
    19.     // according to the size of the empty game object, which defines all possible
    20.     // location in 3D space for 4 targets to spawn randomly
    21.     // center used to determine middle point of the 3D cubic spawning area
    22.  
    23.     public Vector3 center;
    24.     public Vector3 size;
    25.  
    26.     // Defines parameters for targets, since we start with 1 inital target, if
    27.     // total number of targets desired is 4, numTargets = 3 new targets + 1 initial target = 4 total
    28.     int numTargets = 3;
    29.     int i = 0;
    30.  
    31.     void Start()
    32.     {
    33.     }
    34.  
    35.     public void init()
    36.     {
    37.  
    38.         if (random_location == true)
    39.         {
    40.             //randomize location of initial goal/target object
    41.             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));
    42.             goal.transform.position = pos;
    43.         }
    44.         else if (random_location == false)
    45.         {
    46.             Vector3 pos = new Vector3(8.5f + agent.transform.position.x, -3.1f + agent.transform.position.y, 0.9f + agent.transform.position.z);
    47.         }
    48.  
    49.         while (i != numTargets)
    50.         {
    51.             SpawnTarget(i);
    52.             i = i + 1;
    53.         }
    54.     }
    55.  
    56.     // define a color map for targets
    57.     Color[] colors = { Color.yellow, Color.blue, Color.red };
    58.     string[] colorNames = { "Yellow", "Blue", "Red" };
    59.  
    60.     public void SpawnTarget(int i)
    61.     {
    62.         if (random_location == true)
    63.         {
    64.             // takes center of empty square game object, plus minus max ranged divided 2 for x,y,z to generate
    65.             // numTargets within the space at random location
    66.             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));
    67.             GameObject newObject = Instantiate(Targetobj, pos, Quaternion.identity);
    68.             // newObject sets parent to parent of Targetobj
    69.             newObject.transform.parent = Targetobj.transform.parent;
    70.             // assignes tag = "Target" for each generated target
    71.             newObject.tag = "Target";
    72.             // change color of Targets
    73.             newObject.GetComponent<Renderer>().material.color = colors[i];
    74.             newObject.transform.name = colorNames[i];
    75.         }
    76.         else if (random_location == false)
    77.         {
    78.             // takes center of empty square game object, plus minus max ranged divided 2 for x,y,z to generate
    79.             // numTargets within the space at random location
    80.             //Vector3 pos = center + new Vector3((size.x / 2 )+i, (size.y / 2)+i, (size.z / 2)+i);
    81.             Vector3 pos = new Vector3(8.5f + agent.transform.position.x, -3.1f + agent.transform.position.y, 0.9f + agent.transform.position.z);
    82.             switch (i)
    83.             {
    84.                 case 0:
    85.                     pos = new Vector3(-8.5f + agent.transform.position.x, -3.1f + agent.transform.position.y, 0.9f + agent.transform.position.z);
    86.                     //pos = center + new Vector3(-8 , 0, 0);
    87.                     break;
    88.                 case 1:
    89.                     //pos = center + new Vector3(0, 0, -12 );
    90.                     pos = new Vector3(0 + agent.transform.position.x, -3.1f + agent.transform.position.y, -8.5f + agent.transform.position.z);
    91.                     break;
    92.                 case 2:
    93.                     //pos = center + new Vector3(0 , 0, 12);
    94.                     pos = new Vector3(0 + agent.transform.position.x, -3.1f + agent.transform.position.y, 8.5f + agent.transform.position.z);
    95.                     break;
    96.             }
    97.             //Vector3 pos = center + new Vector3(8+i, -3, 1+i);
    98.             GameObject newObject = Instantiate(Targetobj, pos, Quaternion.identity);
    99.             // newObject sets parent to parent of Targetobj
    100.             newObject.transform.parent = Targetobj.transform.parent;
    101.             // assignes tag = "Target" for each generated target
    102.             newObject.tag = "Target";
    103.             // change color of Targets
    104.             newObject.GetComponent<Renderer>().material.color = colors[i];
    105.             newObject.transform.name = colorNames[i];
    106.         }
    107.     }
    108.  
    109.     // Defines color, cube object form of the empty game object
    110.     void OnDrawGizmosSelected()
    111.     {
    112.         Gizmos.color = new Color(1, 0, 0, 0.5f);
    113.         Gizmos.DrawCube(center, size);
    114.     }
    115.  
    116.     void Update()
    117.     {
    118.         //Debug.Log(GameManager.sequence_end + "!!!");
    119.         if (GameManager.sequence_end == true)
    120.         {
    121.             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));
    122.             goal.transform.position = pos;
    123.             //Debug.Log(GameManager.sequence_end + "!!!!");
    124.             GameManager.sequence_end = false;
    125.         }
    126.     }
    127. }
    128.  
     
    Last edited: Apr 17, 2021
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,735
    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.
     
    weight_theta likes this.
  3. philly1164

    philly1164

    Joined:
    Apr 3, 2020
    Posts:
    7
    Kurt-Dekker can you provide an example please? I am in a similar situation and can't seem to figure this out... Many thanks.
     
  4. MartinMa_

    MartinMa_

    Joined:
    Jan 3, 2021
    Posts:
    455
    Hi i am begginer so maybe i am not right but something like this should work.
    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. namespace Assets.Scripts
    5. {
    6.     public class NewMonoBehaviour : MonoBehaviour
    7.     {
    8.         public GameObject objectForSpawn;
    9.         private List<SpawnObject> spawnObjectList;
    10.         private int ammountObjectsYouWant = 50;
    11.  
    12.         private void Start()
    13.         {
    14.             spawnObjectList = new List<SpawnObject>();
    15.             CreateObjects();
    16.  
    17.  
    18.         }
    19.         void CreateObjects()
    20.         {
    21.             for (int i = 0; i < ammountObjectsYouWant; i++)
    22.             {
    23.                 Vector3 objectPosition = new Vector3(Random.Range(1, 100), Random.Range(1, 100), Random.Range(1, 100));
    24.                 SpawnObject sO = new SpawnObject(objectPosition);
    25.                 spawnObjectList.Add(sO);
    26.             }
    27.             SpawnGameObject();
    28.             void SpawnGameObject()
    29.             {
    30.  
    31.                 List<SpawnObject> spawnedObjects = new List<SpawnObject>();
    32.                 List<SpawnObject> notSpawnedObjects = new List<SpawnObject>();
    33.  
    34.                 foreach (SpawnObject item in spawnObjectList)
    35.                 {
    36.                Vector3 spawnPosition = new Vector3(Random.Range(1, 100), Random.Range(1, 100), Random.Range(1, 100));
    37.                     if (item.objectPosition != spawnPosition)
    38.                     {
    39.                         Instantiate(objectForSpawn, spawnPosition, transform.rotation);
    40.                         spawnedObjects.Add(item);
    41.                     }
    42.                     else
    43.                     {
    44.                         notSpawnedObjects.Add(item);
    45.                     }
    46.                 }
    47.                 Debug.Log("Object spawned :" + spawnedObjects.Count);
    48.                 Debug.Log("Object notSpawned :" + notSpawnedObjects.Count);
    49.             }
    50.         }
    51.  
    52.  
    53.     }
    54.     public class SpawnObject
    55.     {
    56.         public Vector3 objectPosition;
    57.         public SpawnObject(Vector3 objectPosition)
    58.         {
    59.             this.objectPosition = objectPosition;
    60.         }
    61.  
    62.     }
    63. }
    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.
     

    Attached Files:

    Last edited: Apr 24, 2021
  5. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    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.

    https://docs.unity3d.com/ScriptReference/Physics.SphereCast.html
     
  6. Realspawn1

    Realspawn1

    Joined:
    Jun 8, 2015
    Posts:
    128
    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.
     
    DonPuno likes this.
  7. MartinMa_

    MartinMa_

    Joined:
    Jan 3, 2021
    Posts:
    455
    I think it is better to check it without unity first because if you will have 1000000 gameobjects your solution will need 1000000 colliders.
     
  8. MartinMa_

    MartinMa_

    Joined:
    Jan 3, 2021
    Posts:
    455
    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
    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. namespace Assets.Scripts
    5. {
    6.     public class NewMonoBehaviour : MonoBehaviour
    7.     {
    8.  
    9.  
    10.         public GameObject objectForSpawn;
    11.         private List<SpawnObject> spawnObjectList;
    12.         private int objectCount = 50;
    13.  
    14.         private void Start()
    15.         {
    16.             CreateCollections();
    17.             Generate(objectCount);
    18.         }
    19.         void CreateCollections()
    20.         {
    21.             spawnObjectList = new List<SpawnObject>();
    22.         }
    23.         void Generate(int objectCount)
    24.         {
    25.             CreateObjects();
    26.             void CreateObjects()
    27.             {
    28.                 for (int i = 0; i < objectCount; i++)
    29.                 {
    30.                     Vector3 objectPosition = GetPosition();
    31.                     SpawnObject sO = new SpawnObject(objectPosition);
    32.                     spawnObjectList.Add(sO);
    33.                 }
    34.             }    
    35.             SpawnGameObjects();
    36.             void SpawnGameObjects()
    37.             {
    38.            
    39.                 List<SpawnObject> spawnedObjects = new List<SpawnObject>();
    40.                 List<SpawnObject> notSpawnedObjects = new List<SpawnObject>();
    41.  
    42.                 foreach (SpawnObject item in spawnObjectList)
    43.                 {
    44.                     Vector3 spawnPosition = GetPosition();
    45.                     if (item.objectPosition != spawnPosition)
    46.                     {
    47.                         Instantiate(objectForSpawn, spawnPosition, transform.rotation);
    48.                         spawnedObjects.Add(item);
    49.                     }
    50.                     else
    51.                     {
    52.                         notSpawnedObjects.Add(item);
    53.                     }
    54.                 }
    55.                 Debug.Log("Object spawned :" + spawnedObjects.Count);
    56.                 Debug.Log("Object notSpawned :" + notSpawnedObjects.Count);
    57.             }
    58.         }
    59.         Vector3 GetPosition()
    60.         {
    61.             Vector3 randmPosition;
    62.             randmPosition.x = Random.Range(1, 100);
    63.             randmPosition.y = Random.Range(1, 100);
    64.             randmPosition.z = Random.Range(1, 100);
    65.             return randmPosition;
    66.         }
    67.     }
    68.     public class SpawnObject
    69.     {
    70.         public Vector3 objectPosition;
    71.         public SpawnObject(Vector3 objectPosition)
    72.         {
    73.             this.objectPosition = objectPosition;
    74.         }
    75.  
    76.     }
    77. In case you want serialize this position for example for save you better use float[3] for position then vector.
    78. }
     

    Attached Files:

    Last edited: Apr 24, 2021
  9. Realspawn1

    Realspawn1

    Joined:
    Jun 8, 2015
    Posts:
    128
    I recreated the trick i mentioned earlier. Scripts can be found at my place if you can use them.

     
  10. MartinMa_

    MartinMa_

    Joined:
    Jan 3, 2021
    Posts:
    455
    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
     
    Last edited: Apr 25, 2021
  11. Realspawn1

    Realspawn1

    Joined:
    Jun 8, 2015
    Posts:
    128
    Good to hear i hope it helps you out. just follow my profile link.