- Home /
How to randomly iterate through a list of objects without repeating?
Note: this is a classic general programming problem and nothing to do with Unity.
You simply SHUFFLE a list to achieve this.
(To shuffle in unity, just do something like this,
return ints.OrderBy(Rx => Random.value).ToArray();
it's a basic, you do it many times in any game, all over the place.)
I have a list with GameObjects as elements. How can I randomly select each one but never select the same one twice?
Are you running this code on update function? It's not giving you only a nomber, its giving you the random position. Just use that position to get the object from your list.
I wasn't running it in the update function but ins$$anonymous$$d, it is in a method that is called when the mouse clicks on a certain object. I see what you are saying about it returning the index number in the list. I should've realized that but my brain was too tired from spending hours on this project. Thanks for your thoughts.
Answer by steakpinball · Apr 30, 2015 at 02:06 PM
The other answers here provide solutions with indeterminate runtime. At best they will run fine, at worst they will take an infinite amount of time. A common practice for randomizing order is to shuffle the original order.
void ShuffleArray<T>(T[] array)
{
int n = array.Length;
for (int i = 0; i < n; i++) {
// Pick a new index higher than current for each item in the array
int r = i + Random.Range(0, n - i);
// Swap item into new spot
T t = array[r];
array[r] = array[i];
array[i] = t;
}
}
// Somewhere else
// objectList is a List<GameObject>
// We don't want to mess up the original list, copy it
var objectArray = objectList.ToArray();
ShuffleArray(objectArray); // Call this more to shuffle again
foreach (var o in objectArray) {
// This will be a random order of objectList
}
This shuffle algorithm is known as the Fisher-Yates Shuffle. It has a runtime of O(n), meaning the time it takes scales with the size of the list.
That's awesome, thanks! I love learning new algorithms and seeing the logic behind them. Thanks for sharing your smarts.
However $$anonymous$$ichael, Don't write your own shuffle - no more than you'd write your own List handling.
int r = i + Random.Range(-i, n - i); Is better. Provides an even distribution while Random.Range(0, n - i) is biased to larger numbers.int r = i + Random.Range(0, n - i);
Answer by Addyarb · May 03, 2015 at 08:17 PM
My solution would be something a little more simple/approachable than most here. This won't be as "Random" as the others mind you, but the player won't know it anyway either way, right?
int CurrentID;
public GameObject[] myArray;
GameObject mySelectedGameObject;
void Start(){
InvokeRepeating ("SelectRandom", 0, 1);
}
void SelectRandom(){
int lastID = CurrentID;
int randomInt = Random.Range (0, myArray.Length);
if (randomInt != lastID) {
mySelectedGameObject = myArray [randomInt];
CurrentID = randomInt;
} else {
if (lastID != myArray.Length-1) {
mySelectedGameObject = myArray [++lastID];
CurrentID = lastID + randomInt;
}
else{
mySelectedGameObject = myArray [--lastID];
CurrentID = lastID + randomInt;
}
}
}
Answer by CalxDesign · Apr 30, 2015 at 07:18 AM
I'd steer clear of Linq where possible, especially if what you're doing is performance critical. Try keeping a list of objects you've already selected in that sequence, then check that list for your next selected object. Something like this funciton should do the trick, but you may wish to add some more robust safety checks to avoid infinite loops etc - This is untested.
private List<GameObject> UniqueSelected(List<GameObject> gameObjects, int targetNumber)
{
List<GameObject> selected = new List<GameObject>();
while (selected.Count < targetNumber)
{
GameObject go = gameObjects[(int) Random.Value * gameObjects.Count];
if(!selected.Contains(go))
{
selected.Add();
}
}
return selected;
}
Thanks for the heads up about Linq. I'm only in CS1301 and we haven't learned anything about them. Also thanks for your input about how to randomize a list. I went ahead and used Linq after my professor helped show me how to make it work in my project.
You should always use Linq at all times. Never, ever consider performance -- except in the exceedingly rare cases where you have to.
Just TBC it's inconceivable this is something that would be happening every frame, or even often. It wouldn't matter, at all, how long it takes. Yo'd no more optimise this than you'd worry about using a raw array somewhere, rather than a "string".
Your answer
Follow this Question
Related Questions
A node in a childnode? 1 Answer
Reset Generic List To Empty(C#, Urgent!) 2 Answers
Instantiate random from a list gives error: "Reference not set to an instance" 1 Answer
Can't add GameObjects to ArrayList 1 Answer
Keep adding targets to a list 2 Answers