Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

How to randomly instantiate cubes that don't overlap?

Discussion in 'Scripting' started by RayDawg, Aug 19, 2014.

  1. Fraconte

    Fraconte

    Joined:
    Dec 6, 2013
    Posts:
    327
    I was able to recreate your problem with primitives of different sizes and shapes. While I had aeroplanes and cars and some cubes all mixing well togheter when I tryed those primitive they overlapped.
    In my case it only happens if there is no rigidbody. With a rigidbody all works fine even if you set it to kinematic.

    This is my last effort. I used a collider to set the area of spawn... just remember to set it's Layer to Ignore Raycast in the Editor.
    Code (CSharp):
    1. sing UnityEngine;
    2. using System.Collections.Generic;
    3.  
    4. /// <summary>
    5. /// Random spawn.
    6. /// Use a collider to define the spawn area
    7. /// Set the layer to Ignore Raycast
    8. /// </summary>
    9. [RequireComponent(typeof(Collider))]
    10. public class RandomSpawn : MonoBehaviour
    11. {
    12.     public List<GameObject>  clones;
    13.  
    14.     void Update()
    15.     {
    16.         Bounds area = collider.bounds;
    17.         int clone = Random.Range (0, clones.Count);
    18.         Vector3 randomPosition = new Vector3 (Random.Range (area.min.x, area.max.x), area.min.y, Random.Range (area.min.z, area.max.z));
    19.         print (randomPosition);
    20.         if (!CheckOverlap (clones[clone], ref randomPosition))
    21.         {
    22.             Instantiate (clones[clone], randomPosition, Quaternion.identity);//transform.rotation);
    23.         }
    24.         print (randomPosition);
    25.     }
    26.  
    27.     // groundPosition.y is initially set to ground level but get changed to the desired center
    28.     // so you can use it to Instantiate
    29.     bool CheckOverlap (GameObject go, ref Vector3 groundPosition)
    30.     {
    31.         Bounds maxBounds = RecursiveColliderBB (go);
    32.         float radius = Mathf.Max (maxBounds.extents.x, maxBounds.extents.z);
    33.         Vector3 chkPos = groundPosition;
    34.         chkPos.y += radius;
    35.         groundPosition.y += maxBounds.extents.y;
    36.         return Physics.CheckSphere (chkPos, radius);
    37.     }
    38.  
    39.     static public Bounds RecursiveColliderBB (GameObject go)
    40.     {
    41.         Collider[] colls = go.GetComponentsInChildren<Collider>();
    42.        
    43.         if (colls.Length>0)
    44.         {
    45.             Bounds b = colls[0].collider.bounds;
    46.             for (int i=1; i<colls.Length; i++)
    47.             {
    48.                 b.Encapsulate(colls[i].collider.bounds);
    49.             }
    50.             return b;
    51.         }
    52.         else
    53.             return new Bounds();
    54.     }
    55.  
    56. }
    57.  
    Edit: Changed to use Collider
     
    Last edited: Sep 2, 2014
  2. RayDawg

    RayDawg

    Joined:
    Mar 19, 2013
    Posts:
    108
    Thanks for at least trying to put in some time to help me. As for the code you provided, It ended up instantiating a whole load of objects non-stop.
     
  3. Fraconte

    Fraconte

    Joined:
    Dec 6, 2013
    Posts:
    327
    I bet you set them invisible... If so you can set them visible or just change renderer.bounds to mesh.bounds in the last function. Try harder... I am sure it works! :)

    Edit: Just in case... you have to set the layer only for the collider where you have this script but not on the objects you want to instantiate.
     
  4. RayDawg

    RayDawg

    Joined:
    Mar 19, 2013
    Posts:
    108
    They're all visible, and even setting all the renderer.bounds to mesh.bounds in the last function (RecursiveMesh.BB()) still causes the problem to persist. Yes, the spawn object does have a box collider and its layer is set to ignore raycast.
     
  5. Fraconte

    Fraconte

    Joined:
    Dec 6, 2013
    Posts:
    327
    If you set the layer to ignore raycast it ignore raycast so you always spawn... You have to set the layer only for the collider you use to define the area of spawn. The objects you want to spawn can be in all layers BUT ignore raycast.
    RandomSpawn.JPG
     
  6. Fraconte

    Fraconte

    Joined:
    Dec 6, 2013
    Posts:
    327
    The objects you want to spawn all have rigidbody?
     
  7. RayDawg

    RayDawg

    Joined:
    Mar 19, 2013
    Posts:
    108
    Yes, their layers are also set to default with x,y,z position and rotations frozen so they don't go flying and spinning wildly. The iskinematic and gravity is unchecked.
     
  8. Fraconte

    Fraconte

    Joined:
    Dec 6, 2013
    Posts:
    327
    I forgot to tell you that the bottom of the area collider is where the collision check is made and where the objects spawn. So if you put it too high and objects have gravity they leave always a free area, Just lower the collider near the ground or set all rigidbodies to kinematic or use no gravity.
     
  9. RayDawg

    RayDawg

    Joined:
    Mar 19, 2013
    Posts:
    108
    I give up. It seems my only choice is to brute force the solution since even fraconte's code doesn't help.
     
  10. _met44

    _met44

    Joined:
    Jun 1, 2013
    Posts:
    633
    It's always a good pick to free your mind from the issue you're stuck on for too long.

    At some point you'll figure it out don't worry. Right now you're probably in a tunel vision and you forgot that one magical checkbox somewhere...
     
  11. Fraconte

    Fraconte

    Joined:
    Dec 6, 2013
    Posts:
    327
    Another issue I have found was about rotated meshes. So I solved using colliders.bounds instead of mesh.bounds:
    Just substitute RecursiveMeshBB with this:
    Code (CSharp):
    1.     static public Bounds RecursiveColliderBB(GameObject go)
    2.     {
    3.         Collider[] colls = go.GetComponentsInChildren<Collider>();
    4.      
    5.         if (colls.Length>0)
    6.         {
    7.             Bounds b = colls[0].collider.bounds;
    8.             for (int i=1; i<colls.Length; i++)
    9.             {
    10.                 b.Encapsulate(colls[i].collider.bounds);
    11.             }
    12.             return b;
    13.         }
    14.         else
    15.             return new Bounds();
    16.     }
    17.  
    Edit: I changed the code in the post above too.
     
    Last edited: Sep 2, 2014
  12. Blue Bean Apps

    Blue Bean Apps

    Joined:
    Aug 10, 2014
    Posts:
    261
    _met44 is right. I've come to find that one small mistake can put you back a month or two. I've experienced this when I spent 5 weeks on a problem to find that it was all because of a semicolon in the code...
    Anyways, I have found a way to instantiate the objects, but does anyone know if it's possible to have two different colliders on a single object, but have different events occur depending on which one you collide with? I'm having a very similar problem to what RayDawg has, but mine is for 2D. I'm trying to set some distance between the objects that randomly instantiate, but without all this complicated foreign code. My method involves using two colliders (ie. 1 polygon collider 2D and 1 box collider 2D). I have the box collider's size set to the size I want and all I have to do is set up an OnCollisionEnter2D event just for a collision with the box collider. If I can do this, then RayDawg and I may be able to skip all this code, which would be nice! :) Any thoughts on weather or not having separate events for separate colliders is possible and/or would work in our case?

    -- Chris
     
  13. Blue Bean Apps

    Blue Bean Apps

    Joined:
    Aug 10, 2014
    Posts:
    261
    Here's a screenshot of how the colliders work in my game: Screenshot.png
    Notice in the scene view (bottom left window), the green boxes around the hooks are the Box Collider 2D. Each hook has one that are all the same size. However, all the hooks also have a Polygon Collider 2D. If you look in the inspector (far right window), you will notice that the Box Collider 2D are Trigger. I did that to prevent the fish from colliding with the boxes, but I want the fish to be able to collide with the Polygon Collider 2D. I want the hooks to collide with the Box Collider 2D and when they do, try a different location to spawn. Is this more or less what you are trying to do except you're trying to do it in 3D, RayDawg?

    Edit: There is a script that handles the collisions attached to each hook, even the instantiated ones. I don't know why they're not showing up in the inspector, but they are there.

    Edit 2: I forgot to mention that if you notice the two hooks that are closest to each other, you will see that their Box Colliders are touching, which is not what I want. I want the Box Colliders to be the space between each hook. By the way, the hooks that aren't touching other Box Colliders is not on purpose, they spawn randomly, so any and/or all hooks could be in contact with another Box Collider, it's not just these two.

    PS: All the objects you see highlighted in the hierarchy (furthest left grey window) are all the Hooks (a total of 12, only 10 clones for testing purposes). All the hooks at the top of the hierarchy are the clones.

    -- Chris
     
    Last edited: Sep 2, 2014
  14. Fraconte

    Fraconte

    Joined:
    Dec 6, 2013
    Posts:
    327
  15. Fraconte

    Fraconte

    Joined:
    Dec 6, 2013
    Posts:
    327
    You can use OnTriggerEnter for the trigger.
     
  16. Fraconte

    Fraconte

    Joined:
    Dec 6, 2013
    Posts:
    327
    This is a very basic 2d (I tested in 3d but it could works). Just adapt radius and position as needed.
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class RandomSpawn3 : MonoBehaviour
    5. {
    6.     public GameObject clone;
    7.  
    8.     void Update()
    9.     {
    10.             StartCoroutine (RandomInstantiate2D (clone));
    11.     }
    12.  
    13.     IEnumerator RandomInstantiate2D (GameObject go)
    14.     {
    15.         float radius = 2f;
    16.         Vector2 randomPosition;
    17.        
    18.         do
    19.         {
    20.             randomPosition = new Vector2 (Random.Range (10, 20), Random.Range (10, 20));
    21.             if (!Physics2D.OverlapCircle (randomPosition, radius))
    22.                 break;
    23.             yield return null;
    24.         }
    25.         while (true);
    26.         Instantiate (go, randomPosition, Quaternion.identity);
    27.     }
    28. }
    29.  
     
  17. Blue Bean Apps

    Blue Bean Apps

    Joined:
    Aug 10, 2014
    Posts:
    261
    WHOOOO!!!! I FINALLY GOT IT!!! I'm so happy!! :D:D:D:D:D Here's what I did:
    1. Made two Hook objects, one of which was a prefab.
    2. Made one of the two Hooks I just created instantiate the prefab (the other hook).
    3. Added Polygon Collider 2D and Box Collider 2D to every Hook.
    4. Made the Box Collider's size large enough for the amount of space I want between the Hooks.
    5. Wrote and attached the scripts to the Hooks.
    The scripts look as follows:
    Code (JavaScript):
    1. var prefab : Transform;
    2.  
    3. function Start ()
    4. {
    5.         var newPositionX : float = Random.Range (-5.5, 10.5);
    6.         transform.position.x = newPositionX;
    7.         var newPositionY : float = Random.Range (-2.5, 4.5);
    8.         transform.position.y = newPositionY;
    9.      
    10.         for (var i = 0; i < 10; i++)
    11.         {
    12.                 Instantiate (prefab, Vector2 (Random.Range (-5.5, 10.5), Random.Range (-2.5, 4.5)), Quaternion.identity);
    13.         }
    14.      
    15.         yield WaitForSeconds (1);
    16.         collider2D.isTrigger = true;
    17. }
    18.  
    19.  
    20. function Update ()
    21. {
    22.      
    23. }
    24.  
    25. var boxCollider2DSelf : Collider2D;
    26. var boxCollider2DOther : Collider2D;
    27.  
    28. function OnCollisionEnter2D (col : Collider2D)
    29. {
    30.         Debug.Log ("Hooks collided!");
    31.      
    32.         // If collision with objects tagged "Obstacle," repeat Start function.
    33.         if (col.gameObject.CompareTag ("Obstacle"))
    34.         {
    35.                 Start ();
    36.         }
    37.      
    38.         if (col == boxCollider2DSelf)
    39.         {
    40.                 Start ();
    41.         }
    42.      
    43.         if (col == boxCollider2DOther)
    44.         {
    45.                 Start ();
    46.         }
    47. }
    NOTE: This code is for the object you will use to instantiate another object. Just remove the instantiation part of the code for this to work with your clones. Furthermore, I only did yield WaitForSeconds (1); collider2D.isTrigger = true; in lines 15 and 16 to enable the Box Collider's trigger, which allows the character to ignore that Box Collider, but the character will still be aware of the Polygon Collider. Without making the Box Collider a trigger, the character just hits the Box Collider instead of the Polygon Collider, so that's why I did that.

    Obviously, if you want to use this in 3D, simply change the OnCollisionEnter2D (col : Collider2D) function to OnCollisionEnter (col : Collider) and change the colliders to 3D colliders. I hope this helps you, RayDawg.

    Here's my end result:

    How it looks in-game:
    Screenshot.png

    This is the same part of the game, just zoomed in so you can see that although they look like they're touching, they really aren't:
    Screenshot2.png
    -- Chris
     
  18. RayDawg

    RayDawg

    Joined:
    Mar 19, 2013
    Posts:
    108
    I'm going to try and use a bit of 2D to see what I can get.
     
  19. RayDawg

    RayDawg

    Joined:
    Mar 19, 2013
    Posts:
    108
    So I adapted this code to my code with some slight alterations and it works to a degree. The one thing that eludes me is getting my method that instantiates objects to run again if an overlapping circle is detected because I don't use the update function at all.
     
  20. Fraconte

    Fraconte

    Joined:
    Dec 6, 2013
    Posts:
    327
    If you use a Coroutine like I did you can just StartCoroutine for every object you want to instantiate even outside of Update.
    Can you post a minimal complete script that reflect exactly your code logic?
     
  21. RayDawg

    RayDawg

    Joined:
    Mar 19, 2013
    Posts:
    108
    So here are parts of it as minimal as I can make it. This particular iteration of the code leads to an infinite loop and I haven't had much time to test and figure it out just yet. I was using a normal while loop instead of do while and it worked ok.

    Code (CSharp):
    1.  
    2. public int objectAmount;
    3.  
    4.     void Start ()
    5.     {  
    6.         RollSize(objectAmount);    // Randomly picks the size of the objects and stores them in an array
    7.         checkObjectSize();     // Checks object size and calls instantiateItems()
    8.     }
    9.  
    10.     void checkObjectSize()
    11.     {
    12.         for(int i = 0; i < objectAmount; i++)
    13.         {
    14.             int RolledItem = SpawnTable[i]; // Spawn table is an array that contains the random sizes that were rolled in another method not listed here
    15.  
    16.             if (RolledItem <= SmallMax)
    17.             {
    18.                 InstantiateItems(SmallTreasure, i); // Call the instantiateItems method and pass the appropriate array along with the current iteration number
    19.             }
    20.  
    21.             if(RolledItem <= MedMax && RolledItem >= MedMin)
    22.             {
    23.                 InstantiateItems(MedTreasure, i);
    24.             }
    25.          
    26.             if(RolledItem <= LargeMax && RolledItem >= LargeMin)
    27.             {
    28.                 InstantiateItems(LargeTreasure, i);
    29.             }
    30.          
    31.         }
    32.     }
    33.  
    34. // What you're about to see here are parts of the original code with the slight alterations based on fraconte's code
    35. void InstantiateItems(GameObject[] ItemArray, int Increment)
    36.     {
    37.         do
    38.         {
    39.             // Select a random treasure from the appropriate treasure array
    40.             TreasureSelect = Random.Range(0, ItemArray.Length);
    41.  
    42.             // Store a random x and y position using info from the GridArray
    43.             StoredCell = GridArray[Random.Range(0, GridWidth), Random.Range(0, GridHeight)].transform.position;
    44.  
    45.             // sphere radius are stored in the square objects, other objects are rectangles
    46.             // newX and newY are the object's possible spawn position stored as floats
    47.             if (Physics2D.OverlapCircle(new Vector2(newX, newY), ItemArray[TreasureSelect].GetComponent<Treasure>().sphereRadius) == true)
    48.             {
    49.  
    50.                 break;
    51.             }
    52.         } while (true);
    53.  
    54.         Instantiate(ItemArray[TreasureSelect], new Vector3(newX, newY, OffSetZ), ItemArray[TreasureSelect].transform.rotation);
    55.  
    56.         ItemArray[TreasureSelect].transform.position = new Vector3(newX, newY, OffSetZ);
    57.     }
    58.  
    59.  
     
  22. RayDawg

    RayDawg

    Joined:
    Mar 19, 2013
    Posts:
    108
    Nevermind: Success.

    On to step 2: Figure out how to account for rectangles, since I'm only using objects with uniform length and width.

    Edit: New problem, 3d raycasts don't work with 2D colliders.
     
    Last edited: Sep 3, 2014
  23. Blue Bean Apps

    Blue Bean Apps

    Joined:
    Aug 10, 2014
    Posts:
    261
    Have you tried all the 2D colliders? If so, then probably 3D raycasts will only work on 3D colliders, so you'd have to change the colliders to their 3D counterparts.

    -- Chris
     
  24. RayDawg

    RayDawg

    Joined:
    Mar 19, 2013
    Posts:
    108
    I figured it out and problem is now solved :D
     
  25. Blue Bean Apps

    Blue Bean Apps

    Joined:
    Aug 10, 2014
    Posts:
    261
    At long last! Good for you! Could you post the code you used or explain what you did?

    -- Chris
     
  26. nateschmold

    nateschmold

    Joined:
    May 26, 2015
    Posts:
    14
  27. Scabbage

    Scabbage

    Joined:
    Dec 11, 2014
    Posts:
    268