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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Load random level without repeating this!

Discussion in 'Scripting' started by ThisIsSparta, Mar 11, 2015.

  1. ThisIsSparta

    ThisIsSparta

    Joined:
    May 4, 2014
    Posts:
    142
    Hi all :)

    There is a way to use this

    Code (CSharp):
    1. Application.LoadLevel(Random.Range(1, 4)); // loads a random level between 1 and 3
    and say not to load the one is playing now? like load between 1 and 10 except 5....thx
     
  2. Razorwings18

    Razorwings18

    Joined:
    May 8, 2014
    Posts:
    69
    What I'd do is:

    Code (CSharp):
    1. levelToLoad = Random.Range(1, 10); // returns some int 1 to 9
    2. if (levelToLoad >= 5){
    3. ++levelToLoad;
    4. }
    5. Application.LoadLevel(levelToLoad); // loads a random level between 1 and 10 except for 5
    6.  
     
  3. ThisIsSparta

    ThisIsSparta

    Joined:
    May 4, 2014
    Posts:
    142
    what ++levelToLoad exactly does?
     
  4. Razorwings18

    Razorwings18

    Joined:
    May 8, 2014
    Posts:
    69
    It just adds 1 to the variable. It's exactly the same as writing:
    Code (CSharp):
    1. levelToLoad = levelToLoad + 1;
    or
    Code (CSharp):
    1. levelToLoad += 1;
    They all do the exact same thing.

    If you're curious, you could check the following link to see all operations that you can do with variables within c#:
    https://msdn.microsoft.com/en-us/library/6a71f45d.aspx
     
  5. larku

    larku

    Joined:
    Mar 14, 2013
    Posts:
    1,422
    ++levelToLoad;

    is shorthand for:

    levelToLoad = levelToLoad + 1;

    A better solution is:

    Code (csharp):
    1. int nextLevel = Random.Range(1, 4);
    2.  
    3. while(nextLevel == Application.loadedLevel)
    4. {
    5.     nextLevel = Random.Range(1, 4);
    6. }
    7.  
    8. Application.LoadLevel(nextLevel);
     
  6. Razorwings18

    Razorwings18

    Joined:
    May 8, 2014
    Posts:
    69
    While the use of Application.loadedLevel IS wiser, your solution is definitely not better.
    It potentially wastes significant resources if the Random returns the loaded level number for several iterations. It also performs two randomization operations instead of one.
    And if you're a really, really, REALLY unlucky guy, you could hang the program indefinitely. :p

    Not much of a concern in this particular case, but for bigger projects on low-resource environments (i.e. mobile), you don't want to be wasting processor time. And it's always good to learn proper resource management from the go.
     
  7. larku

    larku

    Joined:
    Mar 14, 2013
    Posts:
    1,422
    Look, this is certainly not worth arguing over. But how expensive you do think Random is going to be? "significant resources" most certainly not, ever detectable by a human? not even likely on an old 4mhz 8088 cpu.

    Really, really, REALLY as in never ever, EVER going to happen.

    In this instance they are loading a level (in comparison a significantly more expensive operation) this is not the right place to 'optimise' unnecessarily - my solution will never (even on the worst mobile) give any detectable difference in performance or resource use. The objective is to choose a random level and incrementing to the next level is not very random. Admittedly my solution is not significantly more random - but I didn't want to get too complex with the solution (this is not the solution I'd use for a well distributed random level selection). Remember this is no being called every frame in an Update() or similar. Early optimisation is evil and typically creates more complex code with no real benefit. Optimise the parts that actually have an effect on performance, keep the rest as readable, simple and maintainable as possible.

    My solution expresses clearly what it is doing and requires no comments which aids maintainability.

    Everyone's view of better is different and depends on the objective, perhaps I should not have used that term, perhaps I should have said 'alternative solution' Your solution will use less CPU but remember a 2GHz CPU will do 2 billion cycles a second.
     
  8. ThisIsSparta

    ThisIsSparta

    Joined:
    May 4, 2014
    Posts:
    142
    What if I have 10 scenes (from 0 to 9) and I want to load randomly without repeating this scenes?

    example: hit play, load and complete randomly the 10 scenes and when 10 scenes are over randomize that again and replay all in a different order...I hope this is clear :/
     
  9. edwin_s

    edwin_s

    Joined:
    Mar 10, 2015
    Posts:
    19
    Randomise your list first, then simply iterate over that.
     
  10. ThisIsSparta

    ThisIsSparta

    Joined:
    May 4, 2014
    Posts:
    142
    the range for 10 scenes is from 0,10?
     
  11. larku

    larku

    Joined:
    Mar 14, 2013
    Posts:
    1,422
    ThisIsSparta, like ESchrauwen said, this is IMO the 'correct' way to get a good even distribution:

    Something along the lines of (untested, so could be bugs):

    class to hold extension methods:

    Code (csharp):
    1. public static class Extensions {
    2.  
    3.     //
    4.     // Extension method to shuffle a list.
    5.     //
    6.     public static void Shuffle<T>(this IList<T> list)  
    7.     {  
    8.        Random rng = new Random();  
    9.        int n = list.Count;  
    10.        while (n > 1) {  
    11.            n--;  
    12.            int k = rng.Next(n + 1);  
    13.            T value = list[k];  
    14.            list[k] = list[n];  
    15.            list[n] = value;  
    16.        }  
    17.    }
    18. }
    then:

    Code (csharp):
    1. //
    2. // Levels 1..10, 0 is assumed main menu (?)
    3. //
    4. List<int> levelsList = new List<int>()    {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    5. int currentLevelIndex = 0;
    6.  
    7. void Start()
    8. {
    9.     levelsList.Shuffle();
    10. }
    11.  
    12. int getNextLevel()
    13. {
    14.     int nextLevel = levelsList[currentLevelIndex];
    15.     ++currentLevelIndex;
    16.     if(currentLevelIndex >= levelsList.Count) {
    17.         //
    18.         // Ok, reached the end of the list, let's start again.
    19.         //
    20.         currentLevelIndex = 0;
    21.     }
    22. }
    23.  
    If you want to keep loading random levels for ever after all 10 are done then change getNextLevel() to:

    Code (csharp):
    1. int getNextLevel()
    2. {
    3.     int nextLevel = levelsList[currentLevelIndex];
    4.     ++currentLevelIndex;
    5.     if(currentLevelIndex >= levelsList.Count) {
    6.         //
    7.         // Ok, reached the end of the list, let's start again.
    8.         //
    9.         currentLevelIndex = 0;
    10.         levelsList.Shuffle();
    11.  
    12.         //
    13.         // Make sure that the next level in the list is not the same as the current nextLevel.
    14.         //
    15.         while(levelsList[0] == nextLevel)
    16.         {
    17.             levelsList.Shuffle();
    18.         }
    19.     }
    20. }
    Or if you're super concerned about the millionth of a second wasted shuffling the list again (or possibly more times)

    Code (csharp):
    1. int getNextLevel()
    2. {
    3.     int nextLevel = levelsList[currentLevelIndex];
    4.     ++currentLevelIndex;
    5.     if(currentLevelIndex >= levelsList.Count) {
    6.         //
    7.         // Ok, reached the end of the list, let's start again.
    8.         //
    9.         currentLevelIndex = 0;
    10.         levelsList.Shuffle();
    11.  
    12.         //
    13.         // Make sure that the next level in the list is not the same as the current nextLevel.
    14.         //
    15.         if(levelsList[0] == nextLevel)
    16.         {
    17.             //
    18.             // Skip the first element as it's the same as the level we're about to load
    19.             //
    20.             ++currentLevelIndex;
    21.         }
    22.     }
    23. }
    I think that gives you a good idea of how this can be done..

    Now this code needs to either be static somewhere (to keep state between scene loads) or perhaps a MonoBehaviour that follows the unity singleton pattern and added to each scene.

    All untested so may not work at all :)