8

Multiscene editing in Unity, blessed be it, permits the launching (via Editor Play mode) of the current scenes, in their current hierarchical state.

However, building and running the project doesn't recognise the current scene setup in the editor, and starts with whatever is set in the Build Settings.

Is there some way to make builds aware of the current editor state of Multi-scene editing hierarchy, and build and run that setup?

Confused
  • 6,048
  • 6
  • 34
  • 75

2 Answers2

8

1. Getting the editor scenes into the build settings

First for collecting the settings you can use an editor script using

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

enter image description here


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

enter image description here


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:

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
derHugo
  • 83,094
  • 9
  • 75
  • 115
  • 1
    Heading into Unity to try to grok. Be back with a headache. Amazing. This is an article. I don't think 200 pts are nearly enough. – Confused Feb 11 '19 at 17:09
  • @Confused Glad to help ;) – derHugo Feb 13 '19 at 11:07
  • I f*kn HATE fascism. And this place reeks of it. I was away for a bit, working on other parts of my game. And they've auto-awarded HALF the points to the OTHER answer. What a craptastic invention pre-empting behaviour is. – Confused Feb 14 '19 at 01:52
  • And that's DESPITE your answer already having more votes. Unbelievable. – Confused Feb 14 '19 at 01:54
  • I hadn't ticked your answer because I hadn't gotten around to trying it. And I can't read that amount of code and know it to work. Not that code smart. It all looks amazing. Still haven't tried it. But feel bad and angry that this fascist little ghetto has decided to award the other answer. Sorry. Words fail me on the rage response this kind of auto-autocratic crap sets off in me. – Confused Feb 14 '19 at 02:05
  • @Confused I don't know that it has to do with fascism. I don't care about the points but I actually myself learned somehting new while working on the answer ;) You could complain to the moderators but I don't think it is possible to claim back bounty points ^^ it seems that in doubt they are simply awarded to the first given answer and I have to admit that my answer came in like on the last day so ;) – derHugo Feb 14 '19 at 07:07
  • Yeah... nah. I don't think I'm going to get anywhere with the mods. They're the face of the problem, passing it off on the "democratic" means by which the rules supposedly came about. Rules, regulations and roles, no flex. They know what is best for us, and if we don't agree, the only way to shape change of the system is to meld with the system, but it's a democracy, so real change can't be created, only ever more rules, regulations and ever tighter enforcements. It's a microcosm, and a minute example, but it's the general flow of creeping, dystopian digital fascism. – Confused Feb 14 '19 at 11:26
4

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:

  1. Get the necessary scenes using SceneManager.GetSceneByName and filter out all the scenes that are already loaded.
  2. For the scenes that are not loaded yet, call LoadSceneAsync and attach some sort of the coroutine to check the loading progress.
  3. 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.

MarengoHue
  • 1,789
  • 13
  • 34
  • I tried a few of these sorts of approaches, but kept coming up against the issue of the Build Settings interfering with this. I have a specific build setting, that works for the game as a whole. However, when editing a set of nested scenes, and wanting to play as a build from there, I can't quite get it to work without making a mess of the existing build settings, which I don't want to lose. It feels like I have to maintain two separate projects. One to work on and in, and one as a finale and destination for the works that are completed in the more flexible one using your above techniques. – Confused Feb 05 '19 at 19:59
  • Shouldn't you just be able to replicate the exact scene configuration you need with the above approach incrementally (e.g. just load in the scenes that are missing at any given moment)? I might be misunderstanding your question if that is not going to work. Could you provide a sample project and detailed description on how it is supposed to be loaded in different cases (when starting from the editor and when building)? – MarengoHue Feb 06 '19 at 06:28
  • The game has a "correct" way to start, setup in the build settings, and the "home" scene. I want to retain that ability, AND be able to build from any other scene, as per its multi-scene setup. Having these two things available doesn't seem possible. – Confused Feb 06 '19 at 06:40
  • Imagine the current [command + B] builds as per the project's ultimate, complete state, starting from the "home" scene. Whereas [command + alt + B] builds as if the current scene and its multi-scene setup is the game, even if the scenes in the current scene haven't been added to the build settings. – Confused Feb 06 '19 at 06:42
  • Then you only need to load the scene that is containing all of your "gamestate" data (manager components). You could add an object containing the "RootSceneLoader" script which would see if some sort of main scene is loaded and load it otherwise. This way you still get the opportunity to build from any scene you want and include all the necessary data. If you just want to build the player with the current scene and some predefined scenes you might then want to look at the BuildPipeline. You could add a menu to build from current scene: https://docs.unity3d.com/Manual/BuildPlayerPipeline.html – MarengoHue Feb 06 '19 at 07:15
  • I've updated the answer to include that BuildPipeline info – MarengoHue Feb 06 '19 at 07:21
  • This is the first I've read about the build player pipeline. And I am kind of clueless about what it is and what it can do. So a STUPID question: does this mean it's possible to build and run a game (standalone) with scenes not included in the build settings dialog? ie survey the current Editor Hierarchy, insert its scenes into a build, and build it and run it, just as it is in the Editor, without altering the project's build settings? – Confused Feb 06 '19 at 10:27
  • It allows you to customize the way unity builds your player, including what you just mentioned. Take a look at the manual page - it has pretty much all the code you need already (apart from the code that would build that list of scenes to include which you can write yourself) – MarengoHue Feb 06 '19 at 10:47
  • I think this is where I'm confused: "It allows you to customize the way unity builds your player,..." I consider the player to be the playing of the game inside the editor. A build to be standalone version of the game. Is this standalone version what you're calling a 'player'? – Confused Feb 06 '19 at 11:31
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/187987/discussion-between-mokona-modoki-and-confused). – MarengoHue Feb 06 '19 at 11:33