【Unity】Unity Attribute活用ガイド:コードに特別な動作を与えるメタデータマーカー

作成: 2026-02-05

C#のAttributeを使ってUnityのインスペクターをカスタマイズする方法を解説。SerializeField、Header、Range、RequireComponentなど実践的な使い方を紹介します。

概要

動作確認環境: Unity 2022.3 LTS / Unity 6

「インスペクターにprivate変数を表示したいけど、どうすればいいんだろう…」「変数の上に見出しを表示して、インスペクターを見やすくしたい…」

そこで役立つのが、Attribute(属性) です。Attributeは、C#のメタデータマーカーで、クラス、プロパティ、メソッドの宣言の上に配置することで、特別な動作を指示できます。

[HideInInspector]
public float strength;

インスペクター拡張用のAttribute

SerializeField:private変数をインスペクターに表示

[SerializeField]
private float speed = 5.0f;

カプセル化を維持しながら、デザイナーやプランナーが値を調整できるようになります。

field: SerializeField:プロパティをシリアライズ

C#のauto-propertyをインスペクターに表示する現代的な書き方です。

[field: SerializeField] public float Speed { get; private set; } = 5.0f;

外部からは読み取り専用、インスペクターからは編集可能という設計が簡潔に書けます。

HideInInspector:変数をインスペクターに表示しない

[HideInInspector]
public float currentHealth;

シリアライズ自体は行われるため、値は保存されます。

NonSerialized:シリアライズを無効化

[System.NonSerialized]
public float tempValue;
Attributeシリアライズインスペクター表示
HideInInspectorされるされない
NonSerializedされないされない

使い分け: 値を保存したくない一時的な変数にはNonSerialized、保存はしたいが編集はさせたくない場合はHideInInspectorを使います。

Header:見出しを表示

[Header("移動設定")]
[SerializeField] private float moveSpeed = 5.0f;
[SerializeField] private float jumpForce = 10.0f;

[Header("攻撃設定")]
[SerializeField] private float attackPower = 20.0f;

Tooltip:ツールチップを表示

[Tooltip("移動速度(単位:m/s)")]
[SerializeField] private float moveSpeed = 5.0f;

Range:スライダーを表示

[Range(0, 100)]
[SerializeField] private float volume = 50.0f;

注意: Rangeはインスペクター上での入力制限のみです。スクリプトから直接値を代入する場合は範囲外の値も設定できてしまいます。コードからも範囲を制限したい場合はMathf.Clampを併用してください。

Min:最小値を設定

[Min(0)]
[SerializeField] private float health = 100.0f;

Space:スペースを空ける

[SerializeField] private float moveSpeed = 5.0f;
[Space(20)]
[SerializeField] private float attackPower = 20.0f;

TextArea:複数行のテキスト入力

[TextArea(3, 10)]
[SerializeField] private string description;

メソッド用のAttribute

ContextMenu:インスペクターのコンテキストメニューにメソッドを追加

[ContextMenu("体力を全回復")]
private void ResetHealth()
{
    currentHealth = maxHealth;
    Debug.Log("体力を全回復しました");
}

インスペクターでコンポーネントを右クリックすると、メニューが表示されます。

MenuItem:Unityエディタのメニューにメソッドを追加

重要: [MenuItem]を使用するスクリプトはEditorフォルダ 内に配置する必要があります。UnityEditor名前空間はエディタ専用のため、Editorフォルダ外に配置するとビルドエラーになります。

// Editor/MyEditorTools.cs
using UnityEditor;
using UnityEngine;

public class MyEditorTools
{
    [MenuItem("Tools/すべてのオブジェクトをリセット")]
    private static void ResetAllObjects()
    {
        Debug.Log("すべてのオブジェクトをリセットしました");
    }
}

RuntimeInitializeOnLoadMethod:ゲーム実行時に初期化

using UnityEngine;

public class GameInitializer
{
    [RuntimeInitializeOnLoadMethod]
    private static void Initialize()
    {
        Debug.Log("ゲームが起動しました");
    }
}

MonoBehaviourを継承しない静的クラスでも使用できます。

クラス用のAttribute

RequireComponent:必須コンポーネントを指定

[RequireComponent(typeof(Rigidbody))]
public class PlayerController : MonoBehaviour
{
    private Rigidbody rb;

    void Start()
    {
        rb = GetComponent<Rigidbody>();
    }
}

PlayerControllerをアタッチすると、自動的にRigidbodyもアタッチされます。

DisallowMultipleComponent:同じコンポーネントを複数アタッチできないようにする

[DisallowMultipleComponent]
public class GameManager : MonoBehaviour
{
}

ExecuteAlways:エディタモードでも実行

[ExecuteAlways]
public class GridGenerator : MonoBehaviour
{
    void Update()
    {
        // エディタモードと再生モードで処理を分ける
        if (Application.isPlaying)
        {
            // 再生中の処理
        }
        else
        {
            // エディタでの編集中の処理(シーンビューで可視化など)
        }
    }
}

注意: [ExecuteAlways]を付けると、エディタモードでもUpdateが毎フレーム呼ばれます。重い処理を書くとエディタが遅くなるため、Application.isPlayingで分岐するか、処理を軽量に保ってください。

CreateAssetMenu:ScriptableObjectの作成メニューを追加

[CreateAssetMenu(fileName = "NewWeapon", menuName = "MyGame/Weapon")]
public class WeaponData : ScriptableObject
{
    public string weaponName;
    public int damage;
}

シリアライズ用のAttribute

Serializable:カスタムクラスをシリアライズ

MonoBehaviourやScriptableObjectではないカスタムクラスをインスペクターに表示するには、[Serializable]が必要です。

[System.Serializable]
public class EnemyData
{
    public string name;
    public int hp;
    public float speed;
}

public class EnemySpawner : MonoBehaviour
{
    [SerializeField] private EnemyData[] enemies;
}

SerializeReference:ポリモーフィズム対応のシリアライズ

基底クラスやインターフェースのフィールドに、派生クラスのインスタンスを保持できます。

public interface ISkill
{
    void Execute();
}

[System.Serializable]
public class FireSkill : ISkill
{
    public float damage;
    public void Execute() { /* 火属性攻撃 */ }
}

[System.Serializable]
public class IceSkill : ISkill
{
    public float slowDuration;
    public void Execute() { /* 氷属性攻撃 */ }
}

public class Player : MonoBehaviour
{
    [SerializeReference] private ISkill skill;  // FireSkillまたはIceSkillを保持可能
}

注意: SerializeReferenceを使う場合、インスペクターでの型選択にはカスタムエディタまたはOdinInspectorなどのアセットが必要です。

カスタムAttributeの作成

カスタムAttributeの定義

インスペクターに反映させるカスタムAttributeを作成するには、PropertyAttributeを継承します。

using UnityEngine;

[System.AttributeUsage(System.AttributeTargets.Field)]
public class ReadOnlyAttribute : PropertyAttribute
{
}

注意: System.AttributeではなくUnityEngine.PropertyAttributeを継承する必要があります。System.Attributeを継承した場合、PropertyDrawerと連携できません。

エディタ拡張と組み合わせる

PropertyDrawerは Editorフォルダ 内に配置する必要があります。

Assets/
├── Scripts/
│   └── ReadOnlyAttribute.cs    // Attributeの定義
└── Editor/
    └── ReadOnlyDrawer.cs       // PropertyDrawer(Editorフォルダ内)
// Editor/ReadOnlyDrawer.cs
using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(ReadOnlyAttribute))]
public class ReadOnlyDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        GUI.enabled = false;
        EditorGUI.PropertyField(position, property, label);
        GUI.enabled = true;
    }
}

重要: PropertyDrawerをEditorフォルダ外に配置するとビルドエラーになります。UnityEditor名前空間はエディタ専用のため、ビルドに含めることができません。

実践的な使用例

using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(CapsuleCollider))]
public class PlayerController : MonoBehaviour
{
    [Header("移動設定")]
    [Tooltip("移動速度(単位:m/s)")]
    [SerializeField] private float moveSpeed = 5.0f;

    [Tooltip("ジャンプ力")]
    [SerializeField] private float jumpForce = 10.0f;

    [Space(20)]

    [Header("攻撃設定")]
    [Range(10, 100)]
    [SerializeField] private float attackPower = 20.0f;

    [Min(0)]
    [SerializeField] private float attackRange = 2.0f;

    [Space(20)]

    [Header("ステータス")]
    [HideInInspector]
    public float currentHealth;

    [SerializeField] private float maxHealth = 100.0f;

    [ContextMenu("体力を全回復")]
    private void ResetHealth()
    {
        currentHealth = maxHealth;
    }
}

ベストプラクティス

  • SerializeFieldとprivateを組み合わせる - カプセル化を維持しながらインスペクターで調整
  • HeaderとSpaceで見やすくする - 変数をグループ化して可読性向上
  • Tooltipで説明を追加する - チーム開発で他のメンバーが理解しやすくなる
  • RangeとMinで適切な範囲を設定する - 誤った値の設定を防止
  • RequireComponentで必須コンポーネントを指定する - 依存関係を明確にする

まとめ

Attributeは、C#のメタデータマーカーで、コードに特別な動作を指示できます。

  • SerializeField - private変数をインスペクターに表示
  • Header、Space - インスペクターの可読性を向上
  • Range、Min - 値の範囲制限を設定
  • RequireComponent - 必須コンポーネントを自動アタッチ
  • ContextMenu - エディタでメソッドを実行可能に

ぜひ、Attributeを活用して、より読みやすく、保守しやすいコードを書いてください。

さらに学ぶために