[Unity] Sceneの役割や使い方をざっくりまとめた

More than 1 year has passed since last update.

Unity触る上で避けては通れないのでざっくり書いた

Sceneとは?

SceneをUnityEditor上で作る

  • Pojectビュー上の[create]ボタン (もしくは右クリック > Create) からSceneを選択
  • もしくは上部メニューより [Edit] => [NewScene] を選択で新規Sceneが読み込まれる
    • [Ctrl(Cmd) + n] がショートカット

SceneをUnityEditor上で読み込む

  • Project上での任意のSceneファイルをダブルクリック
    • 現在Hierarchy上で開かれているSceneは閉じられる(アンロードされる)ので注意
    • 複数のSceneを読み込んでいる場合、全て閉じられるので注意
  • 複数のSceneを同時に読み込みたい場合は任意のSceneファイルをHierarchy上にドラッグ&ドロップ

addscene.gif
(実際はCameraを破棄されないGameObjectとして複数混在させないほうが良いがDEMO用に...)

SceneをUnityEditor上でアンロードする

  • https://docs.unity3d.com/ja/current/Manual/MultiSceneEditing.html
  • Hierarchy上の任意のシーンを右クリック (もしくはScene名右のメニューを展開)
  • UnloadScene
    • シーンをアンロードされるが、Hierarchy内に表示されたままとなる
    • Editor上で一時的に特定のシーンを無効にしたいときに使用する
  • RemoveScene
    • シーンをアンロードし、Hierarchy上からも消す

unloadscene.gif

Sceneの保存形式をバイナリからテキストに変更する

  • デフォルトではバイナリファイルで保存される
  • この状態でgit等でバージョン管理した際にコンフリクトすると解消が困難
    • (テキストに切り替えても仕様ベースの問題で解消困難なケースは多々あるけど....)
  • 設定からテキスト(YAML形式)として保存するようにしておくことをオススメ

変更方法

  • [Edit] => [Project Settings] => [Editor] でEditorSettingsを表示
  • AssetSerializationForceText に変更

forcetext.png

BuildSettingsにSceneを定義する

  • https://docs.unity3d.com/jp/current/Manual/BuildSettings.html
  • 上部メニューより [File] => [BuildSettings...] を開く
  • 上部の Scenes in Build にSceneを登録しないと実行時に動的に呼び出せないので注意
    • SceneをEditor上で開いた状態で [Add Open Scenes] を選択する
    • Project上のSceneファイルをドラッグ&ドロップする

buildsetting.gif

実行中にScript上からSceneを操作する

SceneManagerについて

using UnityEngine.SceneManagement;

任意のSceneに切り替える

using UnityEngine;
using UnityEngine.SceneManagement;

public class SceneTest : MonoBehaviour
{
    public void MoveScene()
    {
        // 引数にシーン名を指定する
        // Build Settings で確認できる sceneBuildIndex を指定しても良い
        SceneManager.LoadScene("stage1"); 
    }
}

任意のSceneを追加で読み込む

using UnityEngine;
using UnityEngine.SceneManagement;

public class SceneTest : MonoBehaviour
{
    public void AddScene()
    {
        SceneManager.LoadScene("stage2", LoadSceneMode.Additive); 
    }
}

任意のSceneを非同期に読み込む

using UnityEngine;
using UnityEngine.SceneManagement;

public class SceneTest : MonoBehaviour
{
    public void AddSceneAsync()
    {
        SceneManager.LoadSceneAsync("stage3", LoadSceneMode.Additive); 
    }
}

任意のSceneをアンロードする

using UnityEngine;
using UnityEngine.SceneManagement;

public class SceneTest : MonoBehaviour
{
    public void RemoveScene()
    {
        // 引数にシーン名を指定する
        // Build Settings で確認できる sceneBuildIndex を指定しても良い
        SceneManager.UnloadScene("stage1"); 
    }
}

現在読み込まれているSceneの名称をリストで取得する

using UnityEngine.SceneManagement;
using System.Collections.Generic;

public static class SceneUtil
{
    public static List<string> GetActiveSceneNames()
    {
        int sceneCount = SceneManager.sceneCount;
        List<string> activeSceneNames = new List<string>();
        for (int i = 0; i < sceneCount; i++)
        {
            Scene scene = SceneManager.GetSceneAt(i);
            activeSceneNames.Add(scene.name);
        }
        return activeSceneNames;
    }
}
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections.Generic;

public class SceneTest : MonoBehaviour
{
    public void ShowActiveSceneNames()
    {
        foreach (string name in SceneUtil.GetActiveSceneNames())
        {
            Debug.Log(name);
        }
    }
}

現在のシーンをアンロードし、指定したScene群を読み込む

  • もっと綺麗に書ける気がするけどざっくり
using UnityEngine.SceneManagement;
using System.Collections.Generic;
using System.Linq;

public static class SceneUtil
{
    public static void MoveScenes(List<string> sceneNames)
    {
        List<string> activeSceneNames = GetActiveSceneNames();
        Dictionary<string, bool> activeSceneNameMap = activeSceneNames.ToDictionary(name => name, name => true);
        Dictionary<string, bool> sceneNameMap = sceneNames.ToDictionary(name => name, name => true);

        // シーンのロード
        // シーンが空にならないように先に行う
        // 既に読み込まれている場合、そのまま利用する
        foreach (string sceneName in sceneNames)
        {
             if (!activeSceneNameMap.ContainsKey(sceneName))
             {
                 SceneManager.LoadScene(sceneName, LoadSceneMode.Additive);
             }
        }

        // シーンのアンロード
        // 読み込むシーンに指定されている場合はアンロードしない
        foreach (string activeSceneName in activeSceneNames)
        {
             if (!sceneNameMap.ContainsKey(activeSceneName))
             {
                 SceneManager.UnloadScene(activeSceneName);
             }
        }
    }

    public static List<string> GetActiveSceneNames()
    {
        int sceneCount = SceneManager.sceneCount;
        List<string> activeSceneNames = new List<string>();
        for (int i = 0; i < sceneCount; i++)
        {
            Scene scene = SceneManager.GetSceneAt(i);
            activeSceneNames.Add(scene.name);
        }
        return activeSceneNames;
    }
}
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections.Generic;

public class SceneTest : MonoBehaviour
{
    public void MoveScenes()
    {
        List<string> sceneNames = new List<string>() { "stage1", "stage2", "stage3" };
        SceneUtil.MoveScenes(sceneNames);
    }
}

Sceneを跨いで使用するGameObjectを用意する

Editor拡張でSceneを取り扱う

例) 全てのシーンのTextComponentを指定したフォントに置き換える君