Unity Freezes when breaking this While loop

Hey.

I’m fairly new to Unity and wanted to use a while loop to wait for the code in it to execute and then break. Now this works fine for the first 3 times this method is called.
On the last call it just freezes the whole unity editor.

Does anybody now how to fix this?

    public void getTestObjects()
    {
        int randomNumber = Random.Range(0, 4);
        int i = 1;

        while(i == 1 && usedObjects.Count <= 4)
        {
            Debug.Log("Test");
            if(!usedObjects.Contains(randomNumber)){
                if(activeQuestion){
                    hide1.SetActive(false);
                    hide2.SetActive(false);
                    hide3.SetActive(false);
                    hide4.SetActive(false);
                }
               
                activeQuestion = objects[randomNumber];
                usedObjects.Add(randomNumber);
               
                activeQuestion.SetActive(true);

                Debug.Log(randomNumber);

                i = 0;
            }
        }

You’d be much better off making a list of available numbers (instead of the collection of integers you call “usedObjects” :P) and selecting / removing one from that. Judging by the use of a loop, I’m guessing your intent was to try and keep generating a new number to try, which is A) not good at all and B) not properly implemented here, since you only generate the random number once. If you happen to get 0 in your first call then 0 in your second call, you’ll never generate a new number.
Again. I must stress this. Don’t do something like keep trying more random numbers to see what works. You can just start with a list of available numbers and select an index from that.
Also. Using “int i” to act as a condition for exiting here doesn’t make sense. The “break” keyword exists for a reason.

1 Like

Unity will happily go into an infinite loop if you let it, so that’s what you have, an infinite loop.

If you want something to happen over time, use coroutines.

In this case, I think your logic is just off and creating an endless loop.

Unity will lock up 100% of the time EVERY millisecond your scripting code is running.

Nothing will render, no Debug.Log() will come out, no GameObjects or transforms will appear to update, absolutely NOTHING will happen until your code either:

  • returns from whatever function it is running

  • yields from whatever coroutine it is running

As long as your code is looping, Unity isn’t going to do even a single frame of change. Nothing.

No exceptions.

1 Like

Your code has a logical error in it, that’s why while loops like that are dangerous if you get any condition or part of your algorithm wrong. I’m with @Spy-Master that it’s much easier to go the other way round. Start with a list / collection of all the numbers you want to choose from and simply pick a random number from that list and then remove it. Once you have all numbers used, the list would be empty since there are not numbers left over.

The issue in your code is, when you enter your method you roll a single random number between 0 and 3 (0, 1, 2, or 3)
Your if statement checks if you had used that number already. If you had used it already, you would skip your whole if body and just loop again. However you haven’t rolled a new random number since you do that outside the loop. So the next loop iteration you would still have the same number and of course get the same outcome. So you’re stuck in an infinite loop.

Just assume you would copy the random.Range line also to the end of your loop. So when you haven’t found an unused value, you would just roll a new value and test that again. I guess that was your idea. Though even if that part of the algorithm were corrected, you would still end up in an infinite loop when you call your method a 5th time. That’s because your guard condition of your while loop does check for usedObjects.Count <= 4). So the while loop will run as long as you have 4 or less numbers in your usedObjects list. Since there are only 4 possible distinct numbers possible, it means when you have used up all numbers, you’re still looping. So even rolling a new number each iteration which guarantees that you eventually reach every possible number, you will never get past the contains check. If the count is 4 it means all for possible numbers have been used and it’s impossible to find a new one that hasn’t been used yet.

Besides all those things that can lock up and make your game hang, it’s the most inefficient way to roll random numbers. With max 4 elements it’s not too bad as the chance for the last number is at 25%. Though each Contains call would have to iterate through all elements in the list every time. So for the last number you need to roll a new number on everage 4 times until you hit the last missing number by chance. Each time Contains would iterate through the 3 elements. Worst case could be way longer.

Just do this instead:

List<int> numberPool = new List<int>();

void Awake()
{
    for(int i = 0; i < 4; i++)
        numberPool(i);
}

public void getTestObjects()
{
    // no more numbers available, we used all of them, so just leave
    if (numberPool.Count == 0)
        return;
    int index = Random.Range(0, numberPool.Count);
    int randomNumber = numberPool[index];
    numberPool.RemoveAt(index);
    // at this point we have our unique randomNumber and the rest is essentially the same.
    Debug.Log(randomNumber);
    if(activeQuestion)
    {
        hide1.SetActive(false);
        hide2.SetActive(false);
        hide3.SetActive(false);
        hide4.SetActive(false);
    }
    activeQuestion = objects[randomNumber];
    activeQuestion.SetActive(true);
}
1 Like