- Home /
How to randomize a scene changer without replaying the current scene?
So my idea is to create a scene changer that randomizes which scene will be played whenever a level is completed. However I want to make sure that the next scene being randomized is not the current scene.
Here is what I did in my "wait" method (sceneName is a variable containing the name of the current scene):
Enumerator Wait()
{
if (sceneName == "A")
{
yield return new WaitForSeconds(2.0f);
SceneManager.LoadScene("Game", LoadSceneMode.Single);
}
else if(sceneName == "Ran1"){
yield return new WaitForSeconds(2.0f);
string[] zones = new string[2] {"Ran2", "Ran3" };
int random = Random.Range(0, 2);
Application.LoadLevel(zones[random]);
}
else if (sceneName == "Ran2")
{
yield return new WaitForSeconds(2.0f);
string[] zones = new string[2] { "Ran1", "Ran3" };
int random = Random.Range(0, 2);
Application.LoadLevel(zones[random]);
}
else if (sceneName == "Ran3")
{
yield return new WaitForSeconds(2.0f);
string[] zones = new string[2] { "Ran1", "Ran2" };
int random = Random.Range(0, 2);
Application.LoadLevel(zones[random]);
}
else
{
yield return new WaitForSeconds(2.0f);
string[] zones = new string[3] { "Ran1", "Ran2", "Ran3" };
int random = Random.Range(0, 3);
Application.LoadLevel(zones[random]);
}
}
It technically works, but it doesn't seem to be very dynamic.
How can I revise this to not use a bunch of if statements?
Answer by phouse2019 · Jun 23, 2018 at 11:28 PM
You only need one if statement for this. Define a list containing the names of all your levels you want to be randomly selected and then remove the one equaling the current scene name.
for instance,
List<string> allScenes = new List<string>(){"scene1", "scene2", etc};
IEnumeratorLoadRandomLevel(string currentLevelName)
{
if (scenename == "A")
{
yield return new WaitForSeconds(2f);
Application.LoadScene("Game");
}
else
{
allScenes.Remove(currentLevelName);
yield return new WaitForSeconds(2f);
Application.LoadScene(allScenes[Random.Range(0, allScenes.Count)]);
}
}
You should re-add the scene name after loading the scene, otherwise the scene will never be picked again in future calls.
Or make a copy of the list and remove the current scene name from the list copy.
Alternatively, if they want to preserve the scene in the collection without removing it, it's fairly straightforward to filter the item based on the last scene and return a new random one from the resulting list:
string sceneName = "scene1";
int scenesCount = (scenes.Count() - 1);
sceneName = scenes.Where(scene => !scene.Equals(sceneName)).ElementAt($$anonymous$$yRandom.Range(0, scenesCount));
Your code does work, however it also uses LINQ. It's preferable to avoid LINQ for performance reasons.
I would agree with you if it were using something like using ToList() to create a new list; however, this code is neither removing items from a list, nor generating a new list, but ins$$anonymous$$d enumerating over the existing list. This incurs no more overhead than a foreach() loop which I see used in many Unity samples.
When using LINQ for simple searching, filtering and perfor$$anonymous$$g operations on a subset of objects, it can be quite expressive and no less performant than the foreach() loop.
I ran a benchmark using my approach with a collection of scene names of "scene 1" through "scene 10", iterating 1,000,000 times to generate a new random scene which was not the same as the scene before it. The loop completed in 204 milliseconds and the only memory used was that of the initial list of 10 items.
If I needed to use the approach where I would remove the item from the list (which does incur its own cost) and then have to create a new list all over again once exhausted for the purposes of repeating more than the 10 levels, the cost would have been drastically higher.
One may choose whichever approach with which they feel comfortable; however, I am fairly confident in my ability to write LINQ-to-Objects queries that perform well without some of the overhead that gives it such a bad stigma and I will continue to write my code using LINQ where I see there is benefit to do so.
I still stand by my answer as a solution to the problem if they want to preserve the scene list ins$$anonymous$$d of removing the elements.