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

Question Why is this for loop crashing Unity

Discussion in 'Scripting' started by thewonderingvagabond, May 1, 2023.

  1. thewonderingvagabond

    thewonderingvagabond

    Joined:
    Mar 11, 2023
    Posts:
    16
    Hi everyone,

    I have this for loop to instantiate objects (before checking whether the space is not already occupied). The array has only 2 items in it and works without the while part of the code. With the while part, unity keeps crashing on me. What did I overlook?

    Code (CSharp):
    1.         for (int i = 0; i <Fruits.Count; i++)
    2.         {
    3.             //Get a Random Position
    4.             Vector3 GeneratedPosition()
    5.             {
    6.             min_X = -16.8f;
    7.             max_X = 16.8f;
    8.             min_Y = -9f;
    9.             max_Y = 8f;
    10.             return new Vector3((int)Random.Range(min_X,max_X),(int)Random.Range(min_Y,max_Y),0f);
    11.             }
    12.            
    13.             //Check if there is not already an object and spawn fruit
    14.             while (!isObjectHere(GeneratedPosition()))
    15.             {
    16.                 Instantiate (Fruits[i], GeneratedPosition(), Quaternion.identity);
    17.                 spawnedFruits.Add(Fruits[i]);
    18.             }
    19.  
    20.  
    21.     static bool isObjectHere(Vector3 position)
    22. {
    23.     Collider[] intersecting = Physics.OverlapSphere(position, 0.01f);
    24.     if (intersecting.Length == 0)
    25.     {
    26.         return false;
    27.     }
    28.     else
    29.     {
    30.         return true;
    31.     }
     
  2. MartinMa_

    MartinMa_

    Joined:
    Jan 3, 2021
    Posts:
    455
    "program attempts to use more space than is available"

    I am missing reason why is your code in while loop why you just don't check isObjectThere and if it is not you spawn it
     
    Last edited: May 1, 2023
  3. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    1,869
    Nothing to do with Stack Overflow.

    The code won't compile as-is. You have a function declaration (GeneratedPosition) inside a for loop statement. If that function were elsewhere and you just copied it here wrong, you could use it.

    Your algorithm is trying to find an un-occupied area within a larger area. This approach can create an endless while loop, if all possible space is occupied by objects. Try to fit ten apples into a teapot. You need some sort of limit to the number of attempts you make on the second apple, or you will be there all day.

    There's also a logic error on line 14. You'd want to loop until there's nothing there (or you decide to give up), THEN place one.
     
    Last edited: May 1, 2023
  4. MartinMa_

    MartinMa_

    Joined:
    Jan 3, 2021
    Posts:
    455

    Yes you prob right. What you could do is that anytime your IsObjectThere return true you can index-- in your for loop.THen your for loop will iterate more then fruit count but it will not be endless and you will be sure that desired ammount of fruit will be spawned.
     
  5. thewonderingvagabond

    thewonderingvagabond

    Joined:
    Mar 11, 2023
    Posts:
    16
    I can't get my head around what I did.

    I pulled out the GeneratedPosition() and made it a separate function. But now I am thinking that it will generate a new Vector every time it is called, so checking it in the while loop does not do anything.

    As I have a fixed number of objects that all need to be spawned, I put in the while loop, as I do not want to code to not spawn one of the objects.
     
  6. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    1,869
    The "pick a spot until they're all placed" algorithm will need constraints along these lines:

    * make sure the area is big enough to always easily fit all your elements (like, an extra 50%), and/or
    * make sure to include a bail-out criteria when it seems to have trouble placing them

    Now, if you can meet those constraints, the algorithm works like this:

    * while there are items still to be placed,
    * do
    * pick a place​
    * repeat while that place we picked is occupied
    * place the next item​

    The "do" there is important, because you need to pick a place before you can place an item. If you are not sure about C# syntax for that, check this tutorial page for the difference between
    while (condition) { task }
    and
    do { task } while (condition);
     
  7. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,756
  8. thewonderingvagabond

    thewonderingvagabond

    Joined:
    Mar 11, 2023
    Posts:
    16
    The last hours I have been restarting my computer every time I wanted to check the code. Is there a better way to check whether it works?

    I did the following, but this also crashes Unity, and with it all other programs I had open. Very annoying.

    I did not know how to put a bailout in without it skipping placing any of the fruits, however, on the first level, the entire field is empty.

    Code (CSharp):
    1. for (int i = 0; i <Fruits.Count; i++)
    2.         {
    3.             do
    4.             {
    5.                 min_X = -15.8f;
    6.                 max_X = 15.8f;
    7.                 min_Y = -8f;
    8.                 max_Y = 8f;
    9.                 GeneratedPos = new Vector3((int)Random.Range(min_X,max_X),(int)Random.Range(min_Y,max_Y),0);
    10.             }
    11.             while (!isObjectHere(GeneratedPos));
    12.             Instantiate (Fruits[i], GeneratedPos, Quaternion.identity);
    13.             spawnedFruits.Add(Fruits[i]);
    14.            
    15.  
    16.         }
     
  9. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    1,869
    The while condition should be "isObjectHere()", drop the !. You want to pick again if the place you picked IS occupied already.

    For bailout, you would increment a counter every time you picked a spot, clear it when you were successful, and break out of the for-loop if that counter ever exceeded a certain count like 100 tries to find a free spot.
     
  10. thewonderingvagabond

    thewonderingvagabond

    Joined:
    Mar 11, 2023
    Posts:
    16
    Thanks a lot, it all seems to work and I understand it better!

    It seemed to be working all fine, but I just had two objects spawning on top of each other while playing.


    Code (CSharp):
    1.         for (int i = 0; i <Cards.Count; i++)
    2.         {
    3.             int counter = 0;
    4.             do
    5.             {
    6.                 min_X = -15.8f;
    7.                 max_X = 15.8f;
    8.                 min_Y = -8f;
    9.                 max_Y = 8f;
    10.                 GeneratedPos = new Vector3((int)Random.Range(min_X,max_X),(int)Random.Range(min_Y,max_Y),0);
    11.                 counter++;
    12.             }
    13.             while (isObjectHere(GeneratedPos) && counter<100);
    14.             Instantiate (Cards[i], GeneratedPos, Quaternion.identity);
    15.         }
     
  11. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,756
    Look at your logic. You blindly spawn line 14 whether the loop exits for whatever condition.

    I prefer being explicit with this stuff:

    - make a
    bool canSpawnHere = false;


    - try 100 times to find a spot

    - if you find one, set
    canSpawnHere = true;
    and
    break;


    - finally if you canSpawnHere then ... spawn here
     
  12. thewonderingvagabond

    thewonderingvagabond

    Joined:
    Mar 11, 2023
    Posts:
    16
    Thanks Kurt!

    After a long day of restarts, it should finally work :)