Search Unity

How to tell if a scene was loaded successfully (or exists in the project)

Discussion in 'Scripting' started by jlanisdev, Jun 24, 2017.

  1. jlanisdev

    jlanisdev

    Joined:
    Jan 18, 2016
    Posts:
    76
    I would like to:

    (1) Determine if an unloaded scene exists in the project, or
    (2) Determine if a scene was loaded unsuccessfully.

    I don't know how to do (1), but I can technically have a workaround solution for (2) by implementing a timer and checking the scene loaded callback. However, this approach is less than ideal for obvious reasons (scene load times can vary), and I'm honestly surprise Unity doesn't have any built in way to do these two simple things.
     
    Last edited: Jun 25, 2017
  2. TaleOf4Gamers

    TaleOf4Gamers

    Joined:
    Nov 15, 2013
    Posts:
    825
    Use GetSceneByName, if it exists it will return a scene.
    You will see it? Add a method to the sceneLoaded event using
    Example:
    Code (CSharp):
    1. SceneManager.sceneLoaded += OnLevelLoad;
    I just made something super simple which uses callbacks. I can send it to you if you would like. Then you could run a method when a scene is loaded. Its not hard to do this by yourself.
     
  3. jlanisdev

    jlanisdev

    Joined:
    Jan 18, 2016
    Posts:
    76
    GetSceneByName only works on loaded scenes. I want to know if an unloaded scene exists within the project.

    I actually meant to say "determine if a scene was loaded unsuccessfully". My bad.
     
  4. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Not sure what you mean by an unloaded scene existing in the project. If it was there, and subsequently unloaded, it had to be in the project, no?
    Also, not sure about loading unsuccessfully. If it didn't load, something is broken.. I don't get it.
     
  5. TaleOf4Gamers

    TaleOf4Gamers

    Joined:
    Nov 15, 2013
    Posts:
    825
    Wrong, I was literally using it 5 minutes ago. It just has to be in the build settings like normal.
    I will check what I have and edit this post if I find anything.

    EDIT:
    I found this code which I made.
    Feel free to test it and let me know how it goes.
    Code (CSharp):
    1.         public static IEnumerator LoadNamedSceneAsync(string sceneName, Action<float> OnProgress, Action OnFailure, Action OnComplete)
    2. {
    3.     if (SceneManager.GetSceneByName(sceneName) == null)
    4.     {
    5.         OnFailure();
    6.         yield break;
    7.     }
    8.  
    9.             AsyncOperation operation = SceneManager.LoadSceneAsync(sceneName);
    10.             operation.allowSceneActivation = true;
    11.  
    12.             while ((operation.progress * 0.9f) < 1f)
    13.             {
    14.                 OnProgress(operation.progress * 0.9f);
    15.                 yield return null;
    16.             }
    17.  
    18.             OnComplete();
    19.         }
    Small edit:
    Just did a quick test and the OnComplete does not work right now. probably not a big deal for you.

    MAJOR EDIT:
    heres the fixed code:
    it had a few problems but now works just as it should.
    Code (CSharp):
    1. public static IEnumerator LoadNamedSceneAsync(string sceneName, Action<float> OnProgress, Action OnFailure, Action OnComplete)
    2.         {
    3.             if (SceneManager.GetSceneByName(sceneName) == null)
    4.             {
    5.                 OnFailure();
    6.                 yield break;
    7.             }
    8.  
    9.             AsyncOperation operation = SceneManager.LoadSceneAsync(sceneName);
    10.  
    11.             while ((operation.progress / 0.9f) < 1f)
    12.             {
    13.                 OnProgress(operation.progress / 0.9f);
    14.                 yield return null;
    15.             }
    16.  
    17.             OnComplete();
    18.             SceneManager.SetActiveScene(SceneManager.GetSceneByName(sceneName));
    19.         }
     
    Last edited: Jun 25, 2017
  6. jlanisdev

    jlanisdev

    Joined:
    Jan 18, 2016
    Posts:
    76
    I'm sorry, but this is simply incorrect. I've tested this on the latest Unity version (5.6.2f1) and GetSceneByName only works on loaded scenes (and yes, I have all of the scenes included in build settings). You can verify this by logging the scene info that gets returned (i.e, build index and/or name) and by checking IsValid() returning false.

    Also, GetSceneByName never returns null, hence OnFailure() in your code example will never get called. You have to use the IsValid() function instead to check whether or not the returned scene is valid. In fact, you should see this as a warning within your IDE ("unreachable code detected").
     
    Last edited: Jun 25, 2017
  7. jlanisdev

    jlanisdev

    Joined:
    Jan 18, 2016
    Posts:
    76
    Here is some context: Each level in my game is a separate scene, and I have many different levels (i.e, "Level1, Level2...Level25, etc). So for example, before I attempt to load scene "Level37", I want to make sure that Level37 is actually there. Now maybe as a developer, I should know whether or not I have Level37 in my project. But regardless, there should be a mechanism to determine if a scene exists before attempting to load it, which will make it easier to prevent/track down potential problems in the future.
     
    Last edited: Jun 25, 2017
  8. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Fair enough, I never thought of it that way. I can see how that could be useful maybe. As you said, though, you should know what scenes are available, as you're making it :)
     
  9. TaleOf4Gamers

    TaleOf4Gamers

    Joined:
    Nov 15, 2013
    Posts:
    825
    Yup, simple research now im not busy revealed it to be incorrect. It also doesnt mention it in the docs which is ridiculous.

    Also confirmed now I have time to research. Although I did not get an unreachable code warning so... interesting.

    I assume it would throw an exception if the scene doesnt exist, so that should be something to work off. But agreed, there should probably be a method.
     
  10. jlanisdev

    jlanisdev

    Joined:
    Jan 18, 2016
    Posts:
    76
    No exceptions are thrown unfortunately.
    I agree. I think it is ridiculous that the docs aren't more clear about this. There's also one more thing that's interesting - when LoadScene fails, this is the log error:
    It mentions the AssetBundle needing to be loaded, but no mention about the scene itself having to be loaded? It's quite peculiar.
     
    TaleOf4Gamers likes this.
  11. jlanisdev

    jlanisdev

    Joined:
    Jan 18, 2016
    Posts:
    76
    Well, until the Unity devs get their act together, here is my workaround hack: o_O

    Code (CSharp):
    1. Application.logMessageReceived += HandleLog;
    2.  
    3. void HandleLog (string log, string stackTrace, LogType type) {
    4.    if (type == LogType.Error) {
    5.        if (log.Contains("couldn't be loaded because it has not been added to the build settings"))
    6.            SceneLoadFailed();
    7.         }
    8. }
     
  12. ldb

    ldb

    Joined:
    Apr 30, 2013
    Posts:
    40
    If you can use scene path instead of scene name (which you probably should use anyway), then SceneUtility.GetBuildIndexByScenePath works a treat...

    Code (csharp):
    1.  
    2. if (SceneUtility.GetBuildIndexByScenePath("path/to/your/Scene") == -1)
    3. {
    4.     // Scene does not exist in build settings...
    5. }
    6.  
     
  13. Aaron_Nelder

    Aaron_Nelder

    Joined:
    May 31, 2016
    Posts:
    1
    A quick and easy workaround I'm using right now is the try and get the path of the scene I want to load and check to see if that path is and empty string.
    Code (CSharp):
    1.  
    2. IEnumerator LoadNewScene(string sceneName)
    3.     {
    4.         Scene scene = SceneManager.GetSceneByName(sceneName);
    5.         string path = SceneUtility.GetScenePathByBuildIndex(scene.buildIndex);
    6.         if (path == String.Empty)
    7.         {
    8.             Debug.LogError("Scene not found: " + sceneName + ". Is it it the build index?");
    9.             yield break;
    10.         }
    11.  
    12.         loadingOperation = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
    13.  
    14.         while (!loadingOperation.isDone)
    15.         {
    16.             Debug.Log(currentScene + " is loading " + loadingOperation.progress + "%");
    17.             yield return null;
    18.         }
    19.  
    20.         Debug.Log(currentScene + " is loading " + loadingOperation.progress + "%");
    21.         currentScene = SceneManager.GetSceneByName(sceneName);
    22.         OnSceneLoaded?.Invoke(currentScene);
    23.     }