Multi-scene launching of build, as is, from Unity, how?
1. Getting the editor scenes into the build settings
First for collecting the settings you can use an editor script using
- EditorSceneManager.GetSceneManagerSetup to receive the current setup of scenes in the editor
- I assume you want only
loaded
Scenes so make a list of only scenes with isLoaded = true - EditorBuildSettings.scenes to add those scenes to the build settings
1.a. Update on MenuItem click
I made it an extra button in the menu since you might not want to have it always automatically.
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.SceneManagement;
public static class UpdateBuildSettigns
{
[MenuItem("Example/UpdateBuildSettings")]
public static void UpdateSettings()
{
// get current editor setup
SceneSetup[] editorScenes = EditorSceneManager.GetSceneManagerSetup();
// filter list e.g. get only scenes with isActive true
var activeEditorScenes = editorScenes.Where(scene => scene.isLoaded);
// set those scenes as the buildsettings
List<EditorBuildSettingsScene> editorBuildSettingsScenes = new List<EditorBuildSettingsScene>();
foreach (var sceneAsset in activeEditorScenes)
{
string scenePath = sceneAsset.path;
// ignore unsaved scenes
if (!string.IsNullOrEmpty(scenePath)) continue;
editorBuildSettingsScenes.Add(new EditorBuildSettingsScene(scenePath, true));
}
// Set the Build Settings window Scene list
EditorBuildSettings.scenes = editorBuildSettingsScenes.ToArray();
}
}
Updating on menu button
1.b. Update automaticly on (un)loading scenes
If you want it happening automatically you could also add the call as callback to EditorSceneManager.sceneOpened and EditorSceneManager.sceneClosed using InitializeOnLoad and a static constructor to get the callbacks added after recompile or opening the UnityEditor like
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
[InitializeOnLoad]
public static class UpdateBuildSettigns
{
// ofcourse you still can also call it via menu item
[MenuItem("Example/UpdateBuildSettings")]
public static void UpdateSettings()
{
//...
}
static UpdateBuildSettigns()
{
// it is always save to remove callbacks even if they are not there
// makes sure they are always only added once
//
// this is a static constructor so actually there should be no
// callbacks yet ... but .. you never know ;)
EditorSceneManager.sceneOpened -= OnSceneLoaded;
EditorSceneManager.sceneClosed -= OnSceneUnloaded;
EditorSceneManager.sceneOpened += OnSceneLoaded;
EditorSceneManager.sceneClosed += OnSceneUnloaded;
}
private static void OnSceneUnloaded(Scene current)
{
UpdateSettings();
}
private static void OnSceneLoaded(Scene current, OpenSceneMode mode)
{
UpdateSettings();
}
}
Using automatic update
1.c. Enable/Disable automatic updates
If you want more control you can also add extra menu entries for enabling and disabling the automatic updates like
// flag to check if auto-updates are currently enabled
private static bool isEnabled;
// disable the "EnableAutoUpdate" button if already enabled
[MenuItem("Example/EnableAutoUpdate", true)]
private static bool CanEnable()
{
return !isEnabled;
}
// disable the "DisableAutoUpdate" button if already disabled
[MenuItem("Example/DisableAutoUpdate", true)]
private static bool CanDisable()
{
return isEnabled;
}
// add callbacks
[MenuItem("Example/EnableAutoUpdate")]
private static void EnableAutoUpdate()
{
// it is always save to remove callbacks even if they are not there
// makes sure they are always only added once
EditorSceneManager.sceneOpened -= OnSceneLoaded;
EditorSceneManager.sceneClosed -= OnSceneUnloaded;
EditorSceneManager.sceneOpened += OnSceneLoaded;
EditorSceneManager.sceneClosed += OnSceneUnloaded;
isEnabled = true;
}
// remove callbacks
[MenuItem("Example/DisableAutoUpdate")]
private static void DisableAutoUpdate()
{
EditorSceneManager.sceneOpened -= OnSceneLoaded;
EditorSceneManager.sceneClosed -= OnSceneUnloaded;
isEnabled = false;
}
Note since this uses the UnityEditor
namespace you should either place this script in an Editor
folder or use proper pre-processors like
#if UNITY_EDITOR
// above code here
#endif
2. Loading all scenes from the build settings
Than later when running the app in the first scene there should be a script responsible for loading all those scenes. Something like e.g.
// making it a component to make sure it is inside of one scene
public class SceneLoader : MonoBehaviour
{
private void Start()
{
var thisScene = SceneManager.GetActiveScene();
// load all scenes
for(int i = 0; i < SceneManager.sceneCountInBuildSettings; i++)
{
// skip if is current scene since we don't want it twice
if(thisScene.buildIndex == i) continue;
// Skip if scene is already loaded
if(SceneManager.GetSceneByBuildIndex(i).IsValid()) continue;
SceneManager.LoadScene(i, LoadSceneMode.Additive);
// or depending on your usecase
SceneManager.LoadSceneAsync(i, LoadSceneMode.Additive);
}
}
}
refs:
- SceneManager.sceneCountInBuildSettings
- Scene.buildIndex
- SceneManager.GetSceneByBuildIndex
- SceneManager.LoadScene
- SceneManager.LoadSceneAsync
What I would do is attach some sort of a script to the launching scene in unity that would then trigger the loading of the rest of the required scenes after the game has started. That would require some fiddling to start properly (e.g detect the fact that the scenes are not already loaded before trying to load them).
I might extend the answer with a code snippet to achieve the result if you need it.
For now you could take a look at the docs here: https://docs.unity3d.com/ScriptReference/SceneManagement.SceneManager.GetSceneByName.html https://docs.unity3d.com/ScriptReference/SceneManagement.SceneManager.LoadSceneAsync.html
The basic idea would be:
- Get the necessary scenes using SceneManager.GetSceneByName and filter out all the scenes that are already loaded.
- For the scenes that are not loaded yet, call LoadSceneAsync and attach some sort of the coroutine to check the loading progress.
- When all of the scenes are loaded, run a callback so that rest of the game knows that the scenes are loaded and it is good to run the rest of necessary actions which rely on those scenes being loaded.
If you want to preserve the current hierarchy (a set of scenes that are opened in the editor) when building, then it might be achievable with BuildPipeline: https://docs.unity3d.com/Manual/BuildPlayerPipeline.html
There is a way to make a build with a programmatically-accessible list of scenes:
// Get filename.
string path = EditorUtility.SaveFolderPanel("Choose Location of Built Game", "", "");
string[] levels = new string[] {"Assets/Scene1.unity", "Assets/Scene2.unity"}; // You'd have to assemble this list yourself.
// Build player.
BuildPipeline.BuildPlayer(levels, path + "/BuiltGame.exe", BuildTarget.StandaloneWindows, BuildOptions.None);
(which you can determine based on the currently loaded scenes when running your build). This wouldn't be a standard (Cmd + b) build though, but pretty close.