Unityでゲーム開発を進めると、多数のタグやレイヤーを設定する必要があることはよくあります。特に中規模以上のプロジェクトでは、タグ・レイヤーの管理が煩雑になりがちです。この記事では、エディタ拡張を活用してタグとレイヤーを一括で登録する方法を紹介します。
この記事を読むと、以下のことができるようになります:
Unity エディタ拡張(Editor Extensions)は、Unity の開発環境自体をカスタマイズし、プロジェクト固有の機能を追加するための仕組みです。エディタ拡張を使うことで、次のメリットが得られます:
エディタ拡張スクリプトは通常、Editor という特別なフォルダ内に配置され、これらのスクリプトはビルドされたゲームには含まれません。つまり、開発効率を向上させるための機能を、最終製品のサイズやパフォーマンスに影響を与えることなく実装できます。
以下は、タグとレイヤーを一括で登録するためのエディタ拡張スクリプトです。このスクリプトをプロジェクトに追加すると、Unity メニューから簡単にタグとレイヤーを設定できるようになります。
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
public class TagLayerSetupWizard : EditorWindow
{
    [MenuItem("Tools/Setup Tags and Layers")]
    public static void ShowWindow()
    {
        EditorWindow.GetWindow(typeof(TagLayerSetupWizard));
    }
    void OnGUI()
    {
        GUILayout.Label("タグとレイヤーの一括セットアップ", EditorStyles.boldLabel);
        if (GUILayout.Button("タグとレイヤーを設定"))
        {
            SetupTagsAndLayers();
        }
    }
    void SetupTagsAndLayers()
    {
        // カスタムタグのリスト
        string[] customTags = new string[]
        {
            "Ball", "Flipper", "Plunger", "Wall", "Target",
            "FireTarget", "IceTarget", "LightningTarget", "EarthTarget", "WindTarget",
            "Bumper", "Ramp", "Gate", "Dragon", "Collectible",
            "Trigger", "DeadZone", "MultiballSpawner", "SpecialZone", "BonusArea"
        };
        // カスタムレイヤーのリスト
        string[] customLayers = new string[]
        {
            "Ball", "PinballElements", "Flippers", "Plunger", "Walls",
            "Targets", "DragonElements", "Triggers", "SpecialEffects", "Background",
            "InteractiveElements", "NonColliding", "PlayerOnly", "CameraIgnore", "PostProcess",
            "MiniGame", "DragonVisuals", "ElementalEffects", "Physics2D", "Physics3D",
            "DynamicObjects", "StaticObjects", "Debugging", "SensorOnly", "NonInteractive",
            "CinematicElements", "HiddenInGame"
        };
        // タグの追加
        SerializedObject tagManager = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset")[0]);
        SerializedProperty tagsProp = tagManager.FindProperty("tags");
        foreach (string tag in customTags)
        {
            bool found = false;
            for (int i = 0; i < tagsProp.arraySize; i++)
            {
                SerializedProperty t = tagsProp.GetArrayElementAtIndex(i);
                if (t.stringValue.Equals(tag)) { found = true; break; }
            }
            if (!found)
            {
                tagsProp.arraySize++;
                SerializedProperty newTag = tagsProp.GetArrayElementAtIndex(tagsProp.arraySize - 1);
                newTag.stringValue = tag;
            }
        }
        // レイヤーの追加
        SerializedProperty layersProp = tagManager.FindProperty("layers");
        for (int i = 0; i < customLayers.Length; i++)
        {
            // レイヤー8-31のみカスタム可能
            int layerIndex = 8 + i;
            if (layerIndex >= 32) break; // Unity は最大32レイヤーまで
            SerializedProperty layerProp = layersProp.GetArrayElementAtIndex(layerIndex);
            if (string.IsNullOrEmpty(layerProp.stringValue))
            {
                layerProp.stringValue = customLayers[i];
            }
        }
        tagManager.ApplyModifiedProperties();
        Debug.Log("タグとレイヤーを設定しました");
    }
}
実際にこのツールを使うための手順を見ていきましょう。
Editorフォルダの作成: プロジェクト内に Editor フォルダを作成します。このフォルダはAssets直下でも、任意のサブフォルダ内でも構いません。
Assets/Editor/
または
Assets/_Project/Scripts/Editor/
スクリプトの作成: 上記のスクリプトを TagLayerSetupWizard.cs という名前で Editor フォルダ内に保存します。
スクリプトの実行: Unity上部のメニューバーに新しく Tools メニューが追加され、その中に Setup Tags and Layers という項目が表示されます。このメニューをクリックすると、ウィンドウが開きます。
タグとレイヤーの設定: ウィンドウ内の「タグとレイヤーを設定」ボタンをクリックすると、コード内で定義したタグとレイヤーが自動的に追加されます。
このスクリプトがどのように動作するのか、主要な部分を解説します:
[MenuItem("Tools/Setup Tags and Layers")]
public static void ShowWindow()
{
    EditorWindow.GetWindow(typeof(TagLayerSetupWizard));
}
MenuItem 属性を使用して、Unityメニューバーに新しい項目を追加しています。ここでは Tools メニュー内に Setup Tags and Layers という項目を作成しています。
void OnGUI()
{
    GUILayout.Label("タグとレイヤーの一括セットアップ", EditorStyles.boldLabel);
    if (GUILayout.Button("タグとレイヤーを設定"))
    {
        SetupTagsAndLayers();
    }
}
OnGUI メソッドはエディタウィンドウの描画を担当します。ここではシンプルなラベルとボタンを配置しています。
SerializedObject tagManager = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset")[0]);
Unity のタグとレイヤーは ProjectSettings/TagManager.asset ファイルに保存されています。このファイルを SerializedObject として読み込み、プログラムから操作できるようにしています。
SerializedProperty tagsProp = tagManager.FindProperty("tags");
// 中略
tagsProp.arraySize++;
SerializedProperty newTag = tagsProp.GetArrayElementAtIndex(tagsProp.arraySize - 1);
newTag.stringValue = tag;
タグマネージャーの "tags" プロパティにアクセスし、配列のサイズを増やして新しいタグを追加しています。ただし、既存のタグと重複しないように事前にチェックを行います。
SerializedProperty layersProp = tagManager.FindProperty("layers");
// 中略
SerializedProperty layerProp = layersProp.GetArrayElementAtIndex(layerIndex);
if (string.IsNullOrEmpty(layerProp.stringValue))
{
    layerProp.stringValue = customLayers[i];
}
レイヤーも同様にアクセスしますが、Unityでは8〜31の範囲のみカスタムレイヤーとして設定可能です。また、既に設定されているレイヤーは上書きしないように条件分岐を設けています。
tagManager.ApplyModifiedProperties();
最後に、変更内容を確定して保存します。
タグとレイヤーを設定した後、多くの場合はレイヤー間の物理衝突マトリックスも設定する必要があります。これも同様にエディタ拡張で自動化可能です。
以下は、物理衝突マトリックスを設定するメソッドの例です:
void SetupPhysicsLayerCollisions()
{
    // Ball レイヤーは特定のレイヤーとのみ衝突
    int ballLayer = LayerMask.NameToLayer("Ball");
    int pinballElementsLayer = LayerMask.NameToLayer("PinballElements");
    int flippersLayer = LayerMask.NameToLayer("Flippers");
    int wallsLayer = LayerMask.NameToLayer("Walls");
    // 全てのレイヤー間の衝突をオフにする(デフォルトは衝突する)
    for (int i = 0; i < 32; i++)
    {
        for (int j = 0; j < 32; j++)
        {
            // 必要に応じてレイヤー間の衝突を制御
            // Physics.IgnoreLayerCollision(i, j, true); // 衝突を無視
        }
    }
    // 特定のレイヤー間の衝突を有効にする
    Physics.IgnoreLayerCollision(ballLayer, pinballElementsLayer, false); // 衝突を有効
    Physics.IgnoreLayerCollision(ballLayer, flippersLayer, false);
    Physics.IgnoreLayerCollision(ballLayer, wallsLayer, false);
    Debug.Log("物理衝突マトリックスを設定しました");
}
このメソッドを SetupTagsAndLayers メソッドの最後に呼び出すことで、タグとレイヤーの設定と同時に物理衝突マトリックスも設定できます。
物理衝突マトリックスを設定する際のポイント:
デフォルト設定を検討: すべてのレイヤーが互いに衝突するのがデフォルトですが、パフォーマンスを考慮して必要な衝突のみを有効にする「ホワイトリスト方式」も検討しましょう。
論理的なグループ化: 関連するオブジェクトは同じレイヤーに配置し、グループ単位で衝突制御を考えましょう。
頻繁に変更するレイヤーには注意: 実行時に頻繁に変更するレイヤーの衝突設定は慎重に行いましょう。
このエディタ拡張は、さらに以下のように発展させることができます:
タグやレイヤーをコード内のハードコーディングではなく、エディタウィンドウのGUIから動的に追加・削除できるようにします:
private List<string> customTags = new List<string>();
private string newTagName = "";
void OnGUI()
{
    GUILayout.Label("タグ設定", EditorStyles.boldLabel);
    // 新規タグの入力欄
    newTagName = EditorGUILayout.TextField("新規タグ名:", newTagName);
    if (GUILayout.Button("タグを追加") && !string.IsNullOrEmpty(newTagName))
    {
        customTags.Add(newTagName);
        newTagName = "";
    }
    // タグリストの表示
    EditorGUILayout.LabelField("登録予定のタグ:");
    for (int i = 0; i < customTags.Count; i++)
    {
        EditorGUILayout.BeginHorizontal();
        customTags[i] = EditorGUILayout.TextField(customTags[i]);
        if (GUILayout.Button("削除", GUILayout.Width(60)))
        {
            customTags.RemoveAt(i);
            break;
        }
        EditorGUILayout.EndHorizontal();
    }
    // 実行ボタン
    if (GUILayout.Button("タグを登録"))
    {
        SetupTagsAndLayers();
    }
}
プロジェクトごとに異なるタグ・レイヤー設定をプリセットとして保存し、必要に応じて読み込めるようにします:
[System.Serializable]
public class TagLayerPreset
{
    public string presetName;
    public List<string> tags = new List<string>();
    public List<string> layers = new List<string>();
}
private List<TagLayerPreset> presets = new List<TagLayerPreset>();
private int selectedPresetIndex = 0;
// プリセットの保存と読み込み機能
void SavePreset(string presetName)
{
    TagLayerPreset preset = new TagLayerPreset();
    preset.presetName = presetName;
    preset.tags = new List<string>(customTags);
    preset.layers = new List<string>(customLayers);
    presets.Add(preset);
    // プリセットをJSON形式で保存
    string json = JsonUtility.ToJson(new PresetContainer { presets = presets });
    EditorPrefs.SetString("TagLayerPresets", json);
}
void LoadPresets()
{
    string json = EditorPrefs.GetString("TagLayerPresets", "");
    if (!string.IsNullOrEmpty(json))
    {
        PresetContainer container = JsonUtility.FromJson<PresetContainer>(json);
        presets = container.presets;
    }
}
[System.Serializable]
private class PresetContainer
{
    public List<TagLayerPreset> presets = new List<TagLayerPreset>();
}
複数のプロジェクト間でタグとレイヤーの設定を共有できるようにします:
// プリセットのエクスポート
void ExportPreset(TagLayerPreset preset)
{
    string path = EditorUtility.SaveFilePanel(
        "プリセットをエクスポート",
        "",
        preset.presetName + ".json",
        "json");
    if (!string.IsNullOrEmpty(path))
    {
        string json = JsonUtility.ToJson(preset);
        System.IO.File.WriteAllText(path, json);
        Debug.Log("プリセットをエクスポートしました: " + path);
    }
}
// プリセットのインポート
void ImportPreset()
{
    string path = EditorUtility.OpenFilePanel(
        "プリセットをインポート",
        "",
        "json");
    if (!string.IsNullOrEmpty(path))
    {
        string json = System.IO.File.ReadAllText(path);
        TagLayerPreset preset = JsonUtility.FromJson<TagLayerPreset>(json);
        presets.Add(preset);
        Debug.Log("プリセットをインポートしました: " + preset.presetName);
    }
}
タグとレイヤーの一括登録ツールを実装する際のチェックリスト:
Editor フォルダが正しく作成されているUnity エディタ拡張を活用することで、タグとレイヤーの設定という単調な作業を自動化し、プロジェクト設定の一貫性を保てるようになります。さらに、この記事で紹介した手法は、タグとレイヤー以外のプロジェクト設定にも応用可能です。
エディタ拡張は以下の場面で特に効果を発揮します:
Unity のエディタ拡張機能は非常に強力で、自分たちのワークフローに合わせたカスタマイズが可能です。ぜひ、自分のプロジェクトに合わせたエディタ拡張ツールを開発してみてください。
この記事は、Unity 2020.3以上を基準に作成されています。新しいバージョンでは設定方法が異なる場合があります。