Unityでシーン選択と遷移を簡単に！– シーン管理のカスタム方法

Unityプロジェクトでのシーン管理は、シーンの遷移や再読み込みなど多くの場所で必要になります。今回は、SceneFieldというカスタムクラスを使って、シーン選択を簡単に行えるようにする方法を解説します。また、シーン遷移を管理するための便利なユーティリティスクリプトや、シーン遷移を扱うためのカスタムスクリプトも紹介します。

## GitHub

https://github.com/yayoi-exe/unity-scene-management-utility.git

## シーン選択のカスタムクラス: SceneField

SceneFieldは、シーン名をインスペクターで選択できるようにするカスタムクラスです。シーン名を文字列として扱いながらも、プルダウン形式でインスペクターから簡単に選択できるのがポイントです。

``` csharp
using UnityEngine;

[System.Serializable]
public class SceneField
{
    [SerializeField] private string sceneName;

    public string SceneName => sceneName;

    public static implicit operator string(SceneField sceneField)
    {
        return sceneField.SceneName;
    }
}
```

このクラスでは、sceneNameというプライベートな変数にシーン名を格納します。SceneNameプロパティでその値を取得できるようにしています。また、implicit operator stringを定義することで、このクラスを文字列のように扱うことも可能です。これにより、シーン名を簡単に取得できるようになります。

## インスペクターでのシーン選択: SceneFieldPropertyDrawer

SceneField クラスを使ってインスペクターにてプルダウン形式でシーンを選択するためには、Unityエディタ向けのカスタムプロパティドロワーを定義する必要があります。これが SceneFieldPropertyDrawer です。

``` csharp
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(SceneField))]
public class SceneFieldPropertyDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // シーン名プロパティの取得
        SerializedProperty sceneNameProperty = property.FindPropertyRelative("sceneName");

        // ビルド設定に含まれるシーン名の配列を取得
        string[] scenes = GetSceneNames();

        // 現在のシーン名を取得
        string currentSceneName = sceneNameProperty.stringValue;

        // 現在のシーン名のインデックスを取得
        int currentIndex = -1;
        for (int i = 0; i < scenes.Length; i++)
        {
            if (scenes[i] == currentSceneName)
            {
                currentIndex = i;
                break;
            }
        }

        // プルダウンメニューを表示
        int selectedIndex = EditorGUI.Popup(position, label.text, currentIndex, scenes);

        // 選択されたシーン名を更新
        if (selectedIndex >= 0 && selectedIndex < scenes.Length)
        {
            sceneNameProperty.stringValue = scenes[selectedIndex];
        }
    }

    private string[] GetSceneNames()
    {
        var sceneList = new System.Collections.Generic.List<string>();
        foreach (var scene in EditorBuildSettings.scenes)
        {
            if (!scene.enabled) continue;
            string scenePath = scene.path;
            string sceneName = System.IO.Path.GetFileNameWithoutExtension(scenePath);
            sceneList.Add(sceneName);
        }
        return sceneList.ToArray();
    }
}
#endif
```

・シーン名プロパティの取得
FindPropertyRelative("sceneName") を使って、SceneFieldクラス内のsceneNameプロパティにアクセスしています。

・ビルド設定に含まれるシーン名の取得
GetSceneNames()メソッドで、プロジェクトのビルド設定に含まれているすべてのシーン名を取得しています。これは、シーンパスから名前を抽出してリスト化し、配列として返しています。

・プルダウンメニューの表示
EditorGUI.Popup() を使って、インスペクターにプルダウン形式のシーン選択メニューを表示しています。現在のシーン名に応じて、選択中のインデックスを更新し、選択されたシーン名をプロパティに反映します。

## シーン遷移の管理: SceneTransitionUtility

次に、シーンの読み込みやリロードなどの操作を行うための SceneTransitionUtility クラスを紹介します。このユーティリティクラスを使うことで、シーン遷移を簡単に実装できます。

``` csharp
using UnityEngine.SceneManagement;

public static class SceneTransitionUtility
{
    // 指定したシーンを同期的に読み込む
    public static void LoadScene(string sceneName)
    {
        if (SceneExists(sceneName))
        {
            SceneManager.LoadScene(sceneName);
        }
        else
        {
            UnityEngine.Debug.LogError($"シーン '{sceneName}' は存在しません");
        }
    }

    // 現在のシーンをリロードする
    public static void ReloadScene()
    {
        SceneManager.LoadScene(SceneManager.GetActiveScene().name);
    }

    // シーンが存在するか確認する
    private static bool SceneExists(string sceneName)
    {
        for (int i = 0; i < SceneManager.sceneCountInBuildSettings; i++)
        {
            string path = SceneUtility.GetScenePathByBuildIndex(i);
            string name = System.IO.Path.GetFileNameWithoutExtension(path);
            if (name == sceneName)
            {
                return true;
            }
        }
        return false;
    }
}
```

・シーンのロード
LoadScene() メソッドを使って、指定されたシーン名が存在するか確認し、存在する場合はシーンをロードします。

・シーンのリロード
ReloadScene() メソッドでは、現在のシーン名を取得してリロードします。シーンのデバッグやテスト時に使えます。

・シーンの存在確認
SceneExists() メソッドで、ビルド設定に含まれるシーン名と一致するかどうかを確認します。

## 実際にシーン遷移を実行する: CustomSceneManager

最後に、SceneField クラスと SceneTransitionUtility を統合して実際にシーンを遷移させる CustomSceneManager スクリプトを紹介します。

``` csharp
using UnityEngine;

public class CustomSceneManager : MonoBehaviour
{
    [SerializeField] private SceneField sceneToLoad;

    // シーンを同期的に読み込むメソッド
    public void LoadScene()
    {
        SceneTransitionUtility.LoadScene(sceneToLoad.SceneName);
    }

    // 現在のシーンをリロードする
    public void ReloadScene()
    {
        SceneTransitionUtility.ReloadScene();
    }
}
```

・sceneToLoad には SceneField を使用して、インスペクターで簡単にシーンを選択できるようにしています。
・LoadScene() メソッドでは、選択されたシーン名を使ってシーンを読み込みます。
・ReloadScene() メソッドで、現在のシーンを再読み込みします。

## 使い方

1. オブジェクトにアタッチ
Hierarchyビューで空のゲームオブジェクトを作成し、名前を「SceneManager」などに変更します。このオブジェクトに、先ほど作成した CustomSceneManager スクリプトをアタッチします。

2. シーンの指定
インスペクターに SceneToLoad というフィールドが表示されるので、ここにビルド設定に含まれているシーンを指定します。SceneField が有効になっているため、プルダウンメニューからシーンを簡単に選択できます。

プロジェクトに応じてカスタマイズしてください。