えんじにあ雑記!

開発していて学んだことをまとめていきます!

【Editor拡張】Scene名一覧を持つクラスの自動生成

f:id:flat-M_M:20200703121923j:plain

Unityで開発している時に、シーン名を定数文字列として持つクラスを作成することがあります。簡単な作業ですが、自動化できればありがたいなと思いEditor拡張を作りました。

BuildSettingsからScene一覧を取得する

まずは、BuildSettingsからビルドに登録されているシーン名一覧を取得します。

取得するコードは下記のようになります。

List<string> sceneNames = EditorBuildSettings.scenes
    .Where(scene => scene.enabled)
    .Select(scene => Path.GetFileNameWithoutExtension(scene.path))
    .ToList();

シーン名一覧からクラスを生成する

シーン名一覧が取得できたので、それらを元にクラスの実装となる文字列を生成します。

namespaceあたりは適時自分の好みに変更すればいいかと思います。

private static string GenerateClassContent(List<string> sceneNames)
{
    StringBuilder content = new StringBuilder();
    content.Append("namespace Config {\n");
    content.Append("    public class SceneNames {");
    foreach (var sceneName in sceneNames)
    {
        content.Append($"\n        public const string {sceneName} = \"{sceneName}\";");
    }
    content.Append("\n    }");
    content.Append("\n}");
    return content.ToString();
}

ファイルに書き込む

StreamWriterインスタンスを生成して、先ほど生成したクラスのコンテンツを書き込みます。

Refreshを呼び出すことで、Editor上でリロードが走り自動生成されたファイルが作成されたことがわかります。

using (var sr = new StreamWriter(ScriptsFolderPath + "/Scripts/Config/SceneNames.cs"))
{
    sr.Write(GenerateClassContent(sceneNames));
}
AssetDatabase.Refresh();

スクリプト内容

最後に、私が実装したスクリプトを一例として載せておきます。

要点は上記で解説した通りですが、自分のお好みに編集して使いやすいようにEditor拡張を実装してみてください!

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEngine;

public class CreateSceneNames : EditorWindow
{
    private static string ScriptsFolderPath => Application.dataPath + "/CustomAssets";
    
    [MenuItem("Editor/Generate Scene Names Class")]
    private static void GenerateSceneNamesClass()
    {
        Debug.Log("GenerateSceneNamesClass is executing...");
        List<string> sceneNames = EditorBuildSettings.scenes
            .Where(scene => scene.enabled)
            .Select(scene => Path.GetFileNameWithoutExtension(scene.path))
            .ToList();
        StringBuilder log = new StringBuilder();
        foreach (var scene in sceneNames)
        {
            log.Append($"{scene}\n");
        }
        Debug.Log($"Available scene names are\n{log}");
        
        CreateSceneNamesClass(sceneNames);
    }

    private static void CreateSceneNamesClass(List<string> sceneNames)
    {
        if (Directory.Exists(ScriptsFolderPath + "/Scripts") == false)
        {
            Debug.Log("Scripts directory not found. Create New...");
            Directory.CreateDirectory(ScriptsFolderPath + "/Scripts");
        }

        if (Directory.Exists(ScriptsFolderPath + "/Scripts/Config") == false)
        {
            Debug.Log("Scripts/Config directory not found. Create new...");
            Directory.CreateDirectory(ScriptsFolderPath + "/Scripts/Config");
        }

        if (File.Exists(ScriptsFolderPath + "/Scripts/Config/SceneNames.cs") == false)
        {
            Debug.Log("Scripts/Config/SceneNames.cs not found. Create new...");
            using (var sr = File.Create(ScriptsFolderPath + "/Scripts/Config/SceneNames.cs"))
            {
            }
        }
        using (var sr = new StreamWriter(ScriptsFolderPath + "/Scripts/Config/SceneNames.cs"))
        {
            sr.Write(GenerateClassContent(sceneNames));
        }
        AssetDatabase.Refresh();
    }

    private static string GenerateClassContent(List<string> sceneNames)
    {
        StringBuilder content = new StringBuilder();
        content.Append("namespace Config {\n");
        content.Append("    public class SceneNames {");
        foreach (var sceneName in sceneNames)
        {
            content.Append($"\n        public const string {sceneName} = \"{sceneName}\";");
        }
        content.Append("\n    }");
        content.Append("\n}");
        return content.ToString();
    }
}