유니티 Scene을 Additive로 관리하자!

프로젝트를 하다보면 하나의 Scene에 모든것을 관리하려니 리소스 관리가 힘들어지는 경우가 종종 생깁니다.

 

이럴 때 하나의 Scene을 여러개의 Scene으로 나누어서 관리한다면 좀 더 관리하기가 수월해집니다.

 

예를 들어, 하나의 Scene에서는 핵심 Core System만 다루고, 다른 Scene에서는 비쥬얼을 담당하는 Design System만 다룬다면 개발자와 디자이너가 작업하기에도 편하고, 협업 과정에서 충돌가능성이 적어질 수 있습니다.

 

저는 com.unity.multiplayer.samples.coop의 Boss Room에 있는 코드를 가져와서 사용하고 있으며, 코드를 공유드리고자 합니다.

 

GitHub - Unity-Technologies/com.unity.multiplayer.samples.coop: A small-scale cooperative game sample built on the new, Unity ne

A small-scale cooperative game sample built on the new, Unity networking framework to teach developers about creating a similar multiplayer game. - GitHub - Unity-Technologies/com.unity.multiplayer...

github.com

 


예시

(예시) 위와 같이 씬을 여러개 사용할 수 있다 / (우측사진의 경우) 2개만 세팅해둔 모습

 

왼쪽 사진처럼 현재 사용하고자 하는 Scene들을 배치해둔 다음 Save Scene Setup to Config버튼을 누르면 자동으로 Child Scene에 등록이 됩니다.

 

반대로 Reset 버튼을 누르면 저장되지 않은 추가 된 Scene들이 사라지게 됩니다.

 

 


코드

using System;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.SceneManagement;
#endif
using UnityEngine;
using UnityEngine.SceneManagement;

/// <summary>
/// Allows setting a scene as a root scene and setting its child scenes. To use this, drag this component on any object in a scene to make that scene a root scene. In the background, ChildSceneLoader will automatically manage this.
/// </summary>
public class EditorChildSceneLoader : MonoBehaviour
{
#if UNITY_EDITOR
    [SerializeField]
    public List<SceneAsset> ChildScenesToLoadConfig;

    void Update()
    {
        // DO NOT DELETE keep this so we can enable/disable this script... (used in ChildSceneLoader)
    }

    public void SaveSceneSetup()
    {
        if (ChildScenesToLoadConfig == null)
        {
            ChildScenesToLoadConfig = new List<SceneAsset>();
        }
        else
        {
            ChildScenesToLoadConfig.Clear();
        }
        foreach (var sceneSetup in EditorSceneManager.GetSceneManagerSetup())
        {
            ChildScenesToLoadConfig.Add(AssetDatabase.LoadAssetAtPath<SceneAsset>(sceneSetup.path));
        }
    }

    public void ResetSceneSetupToConfig()
    {
        var sceneAssetsToLoad = ChildScenesToLoadConfig;

        List<SceneSetup> sceneSetupToLoad = new List<SceneSetup>();
        foreach (var sceneAsset in sceneAssetsToLoad)
        {
            sceneSetupToLoad.Add(new SceneSetup() { path = AssetDatabase.GetAssetPath(sceneAsset), isActive = false, isLoaded = true });
        }

        sceneSetupToLoad[0].isActive = true;
        EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
        EditorSceneManager.RestoreSceneManagerSetup(sceneSetupToLoad.ToArray());
    }
#endif
}

#if UNITY_EDITOR


[InitializeOnLoad]
public class ChildSceneLoader
{
    static ChildSceneLoader()
    {
        EditorSceneManager.sceneOpened += OnSceneLoaded;
    }

    static void OnSceneLoaded(Scene _, OpenSceneMode mode)
    {
        if (mode != OpenSceneMode.Single || BuildPipeline.isBuildingPlayer) return; // try to load child scenes only for root scenes or if not building

        var scenesToLoadObjects = GameObject.FindObjectsOfType<EditorChildSceneLoader>();
        if (scenesToLoadObjects.Length > 1)
        {
            throw new Exception("Should only have one root scene at once loaded");
        }

        if (scenesToLoadObjects.Length == 0 || !scenesToLoadObjects[0].enabled) // only when we have a config and when that config is enabled
        {
            return;
        }

        scenesToLoadObjects[0].ResetSceneSetupToConfig();

        Debug.Log("Setup done for root scene and child scenes");
    }
}
#endif

 

#if UNITY_EDITOR

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(EditorChildSceneLoader))]
public class ChildSceneLoaderInspectorGUI : Editor
{
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();

        var currentInspectorObject = (EditorChildSceneLoader)target;

        if (GUILayout.Button("Save scene setup to config"))
        {
            currentInspectorObject.SaveSceneSetup();
        }

        if (GUILayout.Button("Reset scene setup from config..."))
        {
            currentInspectorObject.ResetSceneSetupToConfig();
        }
    }
}

#endif

 

Editor 스크립트는 Editor 폴더에 생성하셔야 합니다!