Retrieving the names of your scenes at runtime with Unity

Edit: this script is awesome, and if you combine it with a ScriptableObject you’re ready to go!

Retrieving the names of your scenes at runtime with Unity is a common problem. The API of Unity doesn’t provide an easy way do to so. With the quite recent SceneManager, I thought Unity had finally provided something to get information about your scenes you have added in the Build Settings. No way !

I faced this problem when implementing a little configuration GUI, where the user has the possibility to indicate the order the scenes will be played. Yes, I could have hard-coded this, but it’s not the elegant way in my opinion. Thus, I made some investigations to get once for all the names of my scenes.

Editor Build Settings

First of all, I needed the names of my scenes, and the only place I found them was inside the Editor Build Settings which is not documented by Unity . Oh gosh, it starts badly. This class provides a static property to get the information about the scenes available in your Build Settings (enabled AND disabled). Then, simply loop into the array and get the name of your scenes in the same order they have been added to the Build Settings !

EditorBuildSettingsScene[] scenes = EditorBuildSettings.scenes ;
for( int i = 0 ; i < scenes.Length ; ++i)
{
     if( scenes[i].enabled )
          Debug.Log( "Scene #" + i + " : " + scenes[i].path + " is enabled" );
     else
          Debug.LogWarning( "Scene #" + i + " : " + scenes[i].path + " is not enabled" );
}

Oh, you thought you would be able to copy-paste this code directly in your script ? I am sorry, it’s not that easy, I would not have written this post for only 6 lines of code ! 😉

As you may have noticed, the class is called EditorBuildSettingsScene, then, it belongs to the UnityEditor namespace, and, as you surely know, the classes in this namespace can’t be used in a standalone build.

In the editor, we can easily display this list using the MenuItem attribute like this :

using UnityEngine;
using UnityEditor;

public class GetScenesNamesFromEditor
{
    [MenuItem( "Scenes Names/Save Scenes Names" )]
    private static void GetScenesNames()
    {
        EditorBuildSettingsScene[] scenes = EditorBuildSettings.scenes;
        for ( int i = 0 ; i < scenes.Length ; ++i )
        {
            if( scenes[i].enabled )
                Debug.Log( "Scene #" + i + " : " + scenes[i].path + " is enabled" );
            else
               Debug.LogWarning( "Scene #" + i + " : " + scenes[i].path + " is not enabled" );
        }
    }
}

Now, you will tell me “That doesn’t solve the problem of retrieving the names of my scenes in my game, I can only call this function in my editor !” , yes, indeed ! Since we can’t use directly this function in our game, let’s use them indirectly ! How ? One word : Serialization !

Serializing a Scriptable Object

Let’s start by defining what is serialization  :

Serialization is the process of converting an object into a given format  in order to store this object (into a file, inside a database, …) or transmit it across a network connection link. Its main purpose is to save the state of an object in order to be able to recreate it when needed, eventually in another program context. The reverse process is called deserialization.

Source : MSDN & Wikipedia

You surely have guessed what I want to do here :

  1. Save the names of my scenes from the editor
  2. Retrieve them at runtime

There are so many ways to do this, how about using a database ? No, I am joking, here, around ten strings must be saved. I can use a XML, JSON, YAML file or even a simple text file with one scene name per line, but I wanted to take a look at Unity’s scriptable objects.

ScriptableObject is a class that allows you to store large quantities of shared data independent from script instances.

Source : Unity Manual

Most often, scriptable objects are used as assets which are only meant to store data. Nice, it’s our case here !So let’s define our data structure, containing only a simple array of strings. Feel free to add another array if you want to know whether the scenes are enabled or not.

using UnityEngine;

public class ScenesList : ScriptableObject
{
     public string[] scenesNames;
}

Now, we will be able to create a new ScriptableObject using the ScriptableObject.CreateInstance, the only way to instantiate such object (the new operator doesn’t work). Then, we will fill the array of the scriptable object and store it in a place we will be able to retrieve it later to deserialize it.

Scriptable object have been designed to be easily serialized and deserialized under the form of assets. Thus, we will use the static functions provided in the AssetDatabase class.

[MenuItem( "Scenes Names/Save Scenes Names" )]
 private static void SaveScenesNames()
 {
     EditorBuildSettingsScene[] scenes = EditorBuildSettings.scenes;

     // First, try to load the list if already exists
     ScenesList list = (ScenesList) AssetDatabase.LoadAssetAtPath("Assets/Resources/ScenesList.asset", typeof(ScenesList)) ;

     // If doesn't exist, create it !
     if( list == null )
     {
         list = ScriptableObject.CreateInstance<ScenesList>() ;
         AssetDatabase.CreateAsset( list, "Assets/Resources/ScenesList.asset" );
     }

     // Fill the array
     list.scenesNames = new string[scenes.Length];
     for ( int i = 0 ; i < scenes.Length ; ++i )
     {
         list.scenesNames[i] = scenes[i].path ;
     }

     // Writes all unsaved asset changes to disk
     AssetDatabase.SaveAssets();
 }

If you want to only save the enabled scenes, use a temporary List (in the System.Collections.Generic namespace) :

[MenuItem( "Scenes Names/Save Scenes Names" )]
 private static void SaveScenesNames()
 {
     EditorBuildSettingsScene[] scenes = EditorBuildSettings.scenes;
     List<string> scenesNames = new List<string>();
     // First, try to load the list if already exists
     ScenesList list = (ScenesList) AssetDatabase.LoadAssetAtPath("Assets/Resources/ScenesList.asset", typeof(ScenesList)) ;

     // If doesn't exist, create it !
     if( list == null )
     {
         list = ScriptableObject.CreateInstance<ScenesList>() ;
         AssetDatabase.CreateAsset( list, "Assets/Resources/ScenesList.asset" );
     }

     // Fill the array
     for ( int i = 0 ; i < scenes.Length ; ++i )
     {
         if( scenes[i].enabled )
             scenesNames.Add( scenes[i].path );
     }
     asset.scenesNames = scenesNames.ToArray();
     // Writes all unsaved asset changes to disk
     AssetDatabase.SaveAssets();
 }

Retrieving the names of the scenes

In the last script, I decided to store the list inside my Resources folder to easily retrieve it using a single line of code in any of my scripts :

ScenesList list = Resources.Load<ScenesList>( "ScenesList" );

However, you could have stored the asset in an other folder, then, declared a public ScenesList in the script needing the list of scenes’ names, and dragged & dropped the created asset in the inspector.

public class RetrieveScenesNames : MonoBehaviour
{
    public ScenesList list ;

    private void Start()
    {
        // Here I suppose you have saved the List in your Resources folder
        if( list == null )
            list = Resources.Load<ScenesList>( "ScenesList" );

        for( int i = 0 ; i < list.scenesNames.Length ; ++ i )
        {
            Debug.Log( list.scenesNames[i] );
        }
    }
}

And … voilà ! You have the names of your scenes at runtime ! Or the path of your scenes to be more precise. If you want only the name, I suggest you to use the magic of the Regular expressions (in the System.Text.RegularExpressions namespace) . Use it when you store the data or when you use it :

private void Start()
{
    Regex regex = new Regex( @"([^/]*/)*([\w\d\-]*)\.unity" ) ;
    for( int i = 0 ; i < list.scenesNames.Length ; ++i )
    {
        // Will replace the full path by the second capturing group
        // corresponding to the name of the scene
        Debug.Log( regex.Replace( list.scenesNames[i], "$2" ) );
    }
}

Warning

With the last script used to store into the scriptable object, keep in mind that the information won’t be dynamically updated if you make modifications of your scenes in the Build Settings or if you rename your scenes. You will have to call the SaveScenesNames using the Menu item.

If you don’t want to do this by hand, you could listen for the event EditorApplication.playmodeStateChanged and save the names of the scenes when you click on the Play Button of the editor. Take a look here for an example of how to use it.

6 thoughts on “Retrieving the names of your scenes at runtime with Unity”

  1. Hi!
    First, thanks for this post, i’m learning some new things here (in fact i can only learn new things as i am really not a programmer!)
    But as i am a newbie, there are some things i didn’t understood :p
    If you don’t mind i will try to expose my problem:
    I have copy your script (the part that was useful for me), named it BuildScenesList, and putted it in the Editor folder. And it works, i have my newly asset ScenesList in the Resources folder, great!

    using UnityEditor;
    using UnityEngine;

    public class ScenesList : ScriptableObject
    {
    public string[] scenesNames;

    [MenuItem(“Scenes Names/Save Scenes Names”)]
    private static void SaveScenesNames()
    {
    EditorBuildSettingsScene[] scenes = EditorBuildSettings.scenes;

    // First, try to load the list if already exists
    ScenesList list = (ScenesList)AssetDatabase.LoadAssetAtPath(“Assets/Resources/ScenesList.asset”, typeof(ScenesList));

    // If doesn’t exist, create it !
    if (list == null)
    {
    list = ScriptableObject.CreateInstance();
    AssetDatabase.CreateAsset(list, “Assets/Resources/ScenesList.asset”);
    }

    // Fill the array
    list.scenesNames = new string[scenes.Length];
    for (int i = 0; i < scenes.Length; ++i)
    {
    list.scenesNames[i] = scenes[i].path;
    }

    // Writes all unsaved asset changes to disk
    AssetDatabase.SaveAssets();
    }
    }

    But when using this next code in an other script…:
    ScenesList list = Resources.Load(“ScenesList”);
    for ( int i = 0 ; i < list.scenesNames.Length ; ++ i )
    {
    Debug.Log( list.scenesNames[i] );
    }
    …It tells me that "The type or namespace name "ScenesList" could not be found blablabla"
    So, as i don't know what to do i just recreated a Class in that same script….
    public class ScenesList : ScriptableObject
    {
    public string[] scenesNames;
    }
    …. Which makes disappears the error, but when pressing the play button, i get a nullReferenceException on the list.scenesNames.Length!

    I'm only sure of one thing: I don't know what i'm doing !!

    I you have time to explain me what i didn't get, it would be really nice. If not, thanks again anyway.

    And by the way, i'm only trying to use this script because i want to use the SceneManager.Scene.buildindex based on the NAME of the scene but there is this problem of the SceneManager only having the loaded scenes and not the buildsettings scenes… Perhaps i'm not even looking in the right direction!!

  2. My bad, i just had to put

    public class ScenesList : ScriptableObject
    {
    public string[] scenesNames;
    }

    in its how script, sorry!

  3. this is the closest i’ve come to a solution to search scenes within a game or at runtime to be accurate…

    i’ve an input field to search and need the scene to pop up as i type… then if i hit the corresponding name then load the scene that the name is attached to…

    it doesn’t seem to be too hard but it seems really hard!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.