0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Unity】Header属性の付いた変数をまとめて編集できる機能

Last updated at Posted at 2025-01-31

Unityに慣れてない人でも簡単にレベルデザインできる

🎮 はじめに

チームでゲームを開発していると、変数の値の調整はプログラマーだけでなく、デザイナーやプランナーも関わることが多いです。しかし、

  • Unityの使い方が分からない!
  • どのスクリプトにどの変数があるか分からない!
  • ヒエラルキー??インスペクター??ゲームオブジェクト??

といった理由で、エンジニア以外がレベルデザインに関与しにくい状況が発生しがちです。

そこで、シーン内の特定の変数をGUIから簡単に編集できるエディタ拡張をしました。本記事では、その作成過程を紹介します。

🎯 目指したこと

  • Unityに詳しくない人でもレベルデザインに関われる
  • スクリプトを編集しなくても、エディタ上でレベルデザインに関わる変数を調整できる
  • [Header]属性を活用し、レベルデザインに関係する変数だけを表示する

🛠 実装内容

1. [Header] 属性を活用する

Unityの[Header]属性を使うことで、特定のグループに分類された変数だけをリストアップし、エディタ上で編集できるようにします。

2. エディタウィンドウを作成する

UnityのEditorWindowを利用して、独自のエディタウィンドウを作成します。

3. シーン内のオブジェクトをスキャンし、対象の変数を探す

Unityのシーン内にあるオブジェクトをすべてスキャンし、[Header]付きの変数を検索します。

4. 変数を編集できるUIを作成する

リストアップした変数を、エディタウィンドウ内で直接編集できるようにします。

コード

using UnityEditor;
using UnityEngine;
using System.Reflection;
using System.Collections.Generic;

public class LevelDesignEditor : EditorWindow
{
    // シーン内の対象コンポーネント
    private List<Component> targetComponents = new List<Component>();

    // ヘッダー名ごとに関連する変数を管理
    private Dictionary<string, List<(Component, FieldInfo)>> headerFields = new Dictionary<string, List<(Component, FieldInfo)>>();

    [MenuItem("Tools/Scene Level Editor")]
    public static void ShowWindow()
    {
        GetWindow<LevelDesignEditor>("Scene Level Editor");
    }

    private void OnEnable()
    {
        FindAllHeaderFields();
    }

    // UI描画
    private void OnGUI()
    {
        if (GUILayout.Button("再スキャン"))
        {
            FindAllHeaderFields();
        }

        if (headerFields.Count == 0)
        {
            EditorGUILayout.HelpBox("シーン内に `[Header]` がついた変数が見つかりません。", MessageType.Warning);
            return;
        }

        // 各ヘッダーごとに変数を表示
        foreach (var header in headerFields)
        {
            EditorGUILayout.Space();
            EditorGUILayout.LabelField(header.Key, EditorStyles.boldLabel); // ヘッダー名

            foreach (var (component, field) in header.Value)
            {
                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.LabelField($"{component.gameObject.name} ({component.GetType().Name})", GUILayout.Width(200));

                object currentValue = field.GetValue(component);
                object newValue = DrawField(field, currentValue); // フィールドの描画

                // 値が変更された場合、Undoを記録し、変更を適用
                if (!Equals(currentValue, newValue))
                {
                    Undo.RecordObject(component, "Modify Level Design Variable");
                    field.SetValue(component, newValue);
                    EditorUtility.SetDirty(component);
                }

                EditorGUILayout.EndHorizontal();
            }
        }
    }

    // シーン内の全オブジェクトをスキャンし、[Header]付きの変数を収集
    private void FindAllHeaderFields()
    {
        headerFields.Clear();
        targetComponents.Clear();

        GameObject[] allObjects = GameObject.FindObjectsOfType<GameObject>();

        foreach (var obj in allObjects)
        {
            Component[] components = obj.GetComponents<Component>();
            foreach (var component in components)
            {
                if (component == null) continue;

                // クラス内のすべてのフィールドを取得
                FieldInfo[] fields = component.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                foreach (var field in fields)
                {
                    var headerAttribute = field.GetCustomAttribute<HeaderAttribute>();
                    var serializedFieldAttribute = field.GetCustomAttribute<SerializeField>();

                    // [Header]が付いており、public または [SerializeField] がついている変数を対象とする
                    if (headerAttribute != null && (field.IsPublic || serializedFieldAttribute != null))
                    {
                        if (!headerFields.ContainsKey(headerAttribute.header))
                        {
                            headerFields[headerAttribute.header] = new List<(Component, FieldInfo)>();
                        }
                        headerFields[headerAttribute.header].Add((component, field));
                    }
                }
            }
        }
    }

    // フィールドの型に応じた入力欄を描画
    private object DrawField(FieldInfo field, object value)
    {
        if (value is int intValue)
        {
            return EditorGUILayout.IntField(intValue);
        }
        if (value is float floatValue)
        {
            return EditorGUILayout.FloatField(floatValue);
        }
        if (value is bool boolValue)
        {
            return EditorGUILayout.Toggle(boolValue);
        }
        if (value is string stringValue)
        {
            return EditorGUILayout.TextField(stringValue);
        }
        return value; // 未対応の型はそのまま返す
    }
}

🎮 使い方

1. スクリプトを作成する

作成したLevelDesignEditor.csAssets/Editor フォルダに配置します。

2. [Header] を使ったスクリプトを準備する

レベルデザイン用の変数に[Header]を付与し、エディタで編集できるようにします。

例 : playerSettings.cs

using UnityEngine;

public class playerSettings : MonoBehaviour
{
    [Header("移動速度")]
    public float moveSpeed = 5.0f;

    [Header("攻撃力")]
    public int attackPower = 10;
    
    [Header("攻撃範囲")]   
    public float attackRange = 2.5f;

    [Header("防御力")]
    [SerializeField] private int defensePower = 5;
}

3. Unityエディタでツールを開く

Unityのメニューバーから Tools > Scene Level Editor を開きます。

4. シーン内の変数をスキャン

開いたエディタウィンドウで 「再スキャン」ボタン を押すと、シーン内の[Header]付きの変数が一覧表示されます。

5. パラメータを調整する

各変数の入力フィールドで数値を変更すると、リアルタイムでゲームオブジェクトの値が更新されます。

6. 変更を保存する

Unityの標準機能を利用して、変更が記録されます。
• 変更は Undo(Ctrl+Z) で元に戻せる
• シーンを保存すれば変更が保持される

✅ まとめ

このエディタ拡張を使うことで、

  • Unityに精通したプログラマー以外でも レベルデザイン用のパラメータを編集可能
  • 各ゲームオブジェクトのインスペクターを開かずに まとめて調整できる
  • [Header]を利用することで、整理されたUI で管理しやすい

といったメリットがあります。
チーム開発において、デザイナーやプランナーが手軽にレベルデザインできる環境を作れれば、チームメンバーも喜んでくれそう!ということでこの辺で失礼します。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?