Ben Rubin
Ben Rubin

Reputation: 7341

Can I programatically load scenes in the Unity editor?

I'm using the A* pathfinding algorithm for my 2D game (from my understanding, Unity Nav Meshes don't work in 2D). I would like to be able to pre-calculate navigation grids for all of my scenes, and save them in resource files that can be loaded whenever the player enters a new scene. Rather than having to remember to click "calculate" for every scene -- and remember to recalculate all of my scenes if I make a change to my navigation grids -- I want to be able to programatically have the Unity Editor iterate though each scene and calculate the grids.

Is there a way to create a command in the Unity editor that will iteratively open each scene in the editor and run a method on a MonoBehaviour that's in the scene? Alternatively, is there another way to accomplish what I'm trying to do?

Upvotes: 4

Views: 6665

Answers (1)

derHugo
derHugo

Reputation: 90779

Yes you can!

In editmode you can't use SceneManager but have to use the EditorSceneManager.


First of all you need the scenes you want to iterate.

Could be e.g. a public static field with a list of SceneAsset in the Inspector where you simply reference the scenes

public static List<SceneAsset> Scenes = new List<SceneAsset>();

or you could get them by script e.g. for only use the scenes added to the build settings using EditorBuildSettings.scenes

List<EditorBuildSettingsScene> Scenes = EditorBuildSettings.scenes;

For both you can get a list of the scene paths e.g. using LinQ Select (this is basically a kind of shortcut for a foreach loop) and AssetDatabase.GetAssetPath like

List<string> scenePaths = Scenes.Select(scene => AssetDatabase.GetAssetPath(scene)).ToList();

for the EditorBuildSettingsScene from EditorBuildSettings.scenes you can also simply use

List<string> scenePaths = Scenes.Select(scene => scene.path).ToList();

Now you can iterate over them all and do your stuff by using EditorSceneManager.OpenScene, EditorSceneManager.SaveScene and EditorSceneManager.CloseScene (and if you need it AssetDatabase.SaveAssets)

foreach(string scenePath in scenePaths)
{
    // Open a scene e.g. in single mode
    var currentScene = EditorSceneManager.OpenScene(scenePath);

    /* Do your calculation for currentScene */

    // Don't know if it makes changes to your scenes .. if not you can probably skip this
    EditorSceneManager.SaveScene(currentScene);

    // Finally Close and remove the scene
    EditorSceneManager.CloseScene(currentScene, true);
}

// you might be doing changes to an asset you want to save when done
AssetDatabase.SaveAssets();

Before starting you should probably ask to save the current open scene(s) using EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo

if(EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo())
{
    // Saved => the foreach loop here
}
else
{
    // aborted => do nothing
}

Than in order to finally start that method the simplest would be to add a [MenuItem]

public static class Calculation
{
    [MenuItem("YourMenu/RunCalculation")]
    public static void RunCalculation()
    {
        // all the before mentioned snippets here
        // depending how exactly you want to do it
    }
}

This will add a new menu YourMenu with one entry RunCalculation to the top menubar of the Unity Editor.

Note:
Since this uses a lot of types (EditorSceneManager etc) that only exist in the UnityEditor namespace you should either place the whole script in an Editor folder (so it is ignored in the final build) or use pre-processors like

#if UNITY_EDITOR
    // ... code here
#endif

so in the build the code is also ignored.


Note that I'm assuming so far you also did your calculation in editmode. The thing is if this calculation relies somewhere aon any Start or Awake method you have to call it manually from that editorscript before running the calculation.

Upvotes: 11

Related Questions