Common approach
Shuffle your array. A simple Fisher–Yates shuffle will do. The result is that you will have your array of GameObject
in a random order.
When picking a GameObject
to activate, just pick the next one. To do that, you keep an int index
field that tells you where you are so far, it starts at zero... then, on the event, you pick the GameOBject
at that index
, and increment the index
.
The result is that they are activated in a random order (because, remember, you did shuffle them) and there is no chance of duplicates.
It also very easy to tell when you activated all of the objects (index == Q.Length
).
To give you an intuition of this method, think of your questions as a deck of cards, you start by shuffling it, and then you deal the cards knowing they are already shuffled. You know that you have dealed all the cards when you reach the end of the deck. There are no duplicates because each card is only once in the deck.
I would advice to implement the Fisher–Yates algorithm yourself, it is only four lines of C#, so do not be intimidated by it.
Alternative approach
Let us say that you do not want the order to be selected before hand (even if the user does not know the order). In that case, this is what you do:
Have a field List<int> L
and initialize it with the numbers from 0
to Q.Length - 1
inclusive (this is a simple for loop, or a linq query if you want to be fancy - don't be fancy).
When picking GameObject
to activate, select an int index
at random from 0
to L.Count - 1
inclusive (you do something similar to this in your code) and activate Q[L[index]]
(that is the element of Q
which index is the element of L
which index is index
). Afterwards, remove it from the list (L.RemoveAt(index)
).
As you can see, you are selecting the items at random from a list that contains all items yet to be selected. By removing the one selected each time, you make sure that there are no duplicates.
It is very easy to tell when you are done (L.Count == 0
).
To give you an intuition of this method, imagine each question is a person, and each person has a number, then you make papers with the numbers of each person and throw them into a bag, and take them one by one - at random - you know that you have taken all when the bag is empty. There are no duplicates because you do not add the same number in the bag twice.
Have a read about Random.Range
, notice that it returns a float
, also notice that both min
and max
are inclusive (they are possible results).
Notes
The alternative method is more memory intensive than the common one because it requires to create a new list.
The only advantage that the alternative method has is that it makes it easier to implement a save and load feature such that the questions that are yet to be revealed appear in a different order on every load. This is still possible with the common approach, however it requires a careful re-shuffle (re-shuffle only the questions yet to be revealed).
Notice also that logic of both methods is similar in abstract terms. The main difference is that common approach is eager to randomize, while the alternative is lazy.