スポンサーリンク
UnityUnityの使い方

Unityエディタをもっと便利に!カスタムアトリビュートの作り方と活用法

Unity

【Unity×C#】カスタム属性の作り方と応用完全ガイド!

1. はじめに

C#でプログラミングをしていると、「属性(Attribute)」という言葉をよく見かけますよね。たとえば、[SerializeField][Range] のように、スクリプトの変数やクラスにちょこんと書き添えられているあれです。

これらの属性は、ただの飾りではなく、コードに「追加情報(メタデータ)」を与える仕組みです。そして、この仕組みを自分で拡張できるのが「カスタム属性」。C#の世界ではもちろん、Unityエディタのインスペクターをもっと便利にするためにも大活躍します。

特にUnityには PropertyAttribute という仕組みがあり、これを使うことで変数の見え方や操作性を自由にカスタマイズできます。普段の開発では「インスペクターの表示をちょっと変えたいな…」と思う瞬間がありますよね。そんなときに役立つのが、このPropertyAttributeです。

本記事では、まずC#における属性の基礎をおさらいし、そこからカスタム属性の作り方、さらにUnityでの実践的な使い方(PropertyAttribute × PropertyDrawer)までをやさしく解説していきます。

「インスペクターをもっと自分好みにしたい!」という方にぴったりの内容ですので、ぜひ最後まで読んでみてくださいね。




2. 属性とカスタム属性の基礎

まずはC#における属性(Attribute)とは何かを整理しましょう。属性はクラスやメソッド、変数などに「追加情報」を与える仕組みです。これにより、コンパイラやフレームワーク、ツール(Unityなど)がその情報を参照して挙動を変えることができます。

2-1. 属性の定義と役割

代表的な例として、Unityでよく見る [SerializeField][Range] があります。 これらは「この変数をシリアライズしてインスペクターに表示してね」といった指示をUnityに与えるものです。 つまり属性は、コードに対するメタデータを簡単に埋め込む仕組みだと考えると分かりやすいです。

2-2. カスタム属性とは?

C#では、自分で新しい属性を作ることも可能です。これをカスタム属性と呼びます。 独自に作成したクラスを System.Attribute から継承することで、オリジナルの属性を定義できます。

たとえば「このクラスの作成者名を残したい」という目的で [Author("名前")] のような属性を作ることもできます。 命名規則としては、クラス名の末尾に Attribute を付けることが推奨されていますが、使用時には省略可能です。

2-3. 属性のメリットと注意点

  • メリット
    • コードに追加情報を埋め込める
    • コンパイラやUnityに特定の動作をさせられる
    • リフレクションと組み合わせれば、実行時に情報を取り出して活用できる
  • 注意点
    • 属性は「メタデータ」なので動的に変更はできない
    • リフレクションを多用するとパフォーマンス低下につながる
    • インスタンス化のタイミングはCLR(.NETの仕組み)が管理するため、制御できない

このように、カスタム属性を理解しておくと、コードの可読性や柔軟性が一気に向上します。 次は、実際にカスタム属性を作成する手順を見ていきましょう。




3. カスタム属性の作成手順

ここからは、実際にカスタム属性を作る流れを具体的に見ていきましょう。C#では System.Attribute を継承したクラスを作成することで、独自の属性を定義できます。

3-1. 属性クラスの作成

プロジェクトウィンドウを右クリックし、CreateC# Script を選び、新しいスクリプトを作成します。名前は「AuthorAttribute」のように、末尾に「Attribute」を付けるのが基本です。


using System;

public class AuthorAttribute : Attribute
{
    public string Name { get; }
    public double Version { get; set; }

    public AuthorAttribute(string name)
    {
        Name = name;
        Version = 1.0;
    }
}

この例では「作成者の名前」と「バージョン情報」を保持する属性を作っています。

3-2. 位置指定パラメータと名前付きパラメータ

  • 位置指定パラメータ:コンストラクタで受け取る値(例:[Author("Alice")]
  • 名前付きパラメータ:プロパティとして指定できる値(例:[Author("Alice", Version = 2.0)]

位置指定パラメータは必須、名前付きパラメータは任意で指定できます。これにより、柔軟にメタデータを管理できます。

3-3. AttributeUsageで利用範囲を制限

属性を適用できる場所(クラス、メソッド、フィールドなど)を制御するには AttributeUsage を使います。


[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, 
                AllowMultiple = true, Inherited = false)]
public class AuthorAttribute : Attribute
{
    // 省略
}
  • AttributeTargets:適用対象(クラス、メソッドなど)
  • AllowMultiple:同じ要素に複数回付けられるかどうか
  • Inherited:継承したクラスにも適用されるかどうか

これを設定しておくことで、「どの要素に使えるか」を明確に制御できます。

3-4. 属性を利用してみる

作成した属性は次のようにクラスに付与できます。


[Author("Alice", Version = 1.2)]
public class SampleClass
{
    // クラスの内容
}

これで「このクラスの作者はAliceで、バージョン1.2」という情報をメタデータとして残せるようになりました。

次のステップでは、作成した属性を実行時に読み取る方法(リフレクション)について解説していきます。




4. リフレクションで属性情報を取得

カスタム属性を定義しただけでは、まだその情報を活用できません。実際にプログラム実行中に属性情報を取り出すには、リフレクション(Reflection)を使います。

4-1. クラスに付与された属性を取得

例えば、前章で作成した AuthorAttributeSampleClass に付けたとしましょう。 以下のコードで、実行時にその情報を取得できます。


using System;
using System.Reflection;

[Author("Alice", Version = 1.2)]
public class SampleClass
{
}

public class Test
{
    public static void Main()
    {
        // 型情報を取得
        Type type = typeof(SampleClass);

        // カスタム属性を取得
        AuthorAttribute attr = 
            type.GetCustomAttribute<AuthorAttribute>();

        if (attr != null)
        {
            Console.WriteLine($"Author: {attr.Name}, Version: {attr.Version}");
        }
    }
}

出力結果は以下の通りです。


Author: Alice, Version: 1.2

4-2. メンバーに付与された属性を取得

属性はクラスだけでなく、メソッドやフィールドにも付与できます。その場合はリフレクションで対象メンバーを取得してから、属性を読み出します。


public class Player
{
    [Author("Bob", Version = 2.0)]
    public void Attack() { }
}

public class Test
{
    public static void Main()
    {
        MethodInfo method = typeof(Player).GetMethod("Attack");
        AuthorAttribute attr = 
            method.GetCustomAttribute<AuthorAttribute>();

        if (attr != null)
        {
            Console.WriteLine($"Method Author: {attr.Name}, Version: {attr.Version}");
        }
    }
}

4-3. 注意点

  • リフレクションは便利ですが、処理が重いためゲームのフレーム処理には不向きです。
  • 主にエディタ拡張初期設定で使うのがおすすめです。

このようにして、カスタム属性を実行時に読み取ることで、コードに埋め込んだメタデータを柔軟に活用できます。 次はいよいよ、UnityにおけるPropertyAttributeを使った実践的な応用に進みましょう。




5. Unityでの応用:PropertyAttributeとPropertyDrawer

ここからはUnity専用の拡張に入ります。Unityには PropertyAttributeUnityEngine)と、それをインスペクターに描画する PropertyDrawerUnityEditor)が用意されています。
これらを組み合わせると、フィールドごとの表示や操作性を自由にカスタマイズできます。

5-1. 仕組みの全体像

  • PropertyAttribute:フィールドに付ける「印(メタデータ)」。
    内部的な AttributeUsageAttributeTargets.Field / Inherited = true / AllowMultiple = false に相当
  • PropertyDrawer:上記の印が付いたフィールドをどう描くかを決める描画クラス(エディタ専用)。
  • 対応関係[CustomPropertyDrawer(typeof(YourAttribute))] で属性 ⇄ ドロワーをひもづける。

PropertyDrawerエディタ拡張専用です。ビルド後の実行には影響しません。

5-2. よく使うUnity標準の属性(抜粋)

  • [Range(min, max)]:数値フィールドをスライダーで入力
  • [Tooltip("説明")]:フィールドにツールチップを付与
  • [Header("見出し")]:インスペクターに見出しを追加

これらは PropertyAttribute を継承して実装されています。挙動を参考に、独自の見た目・制御を作れます。

5-3. 実装フロー(最短レシピ)

  1. 属性クラスを作る(PropertyAttribute継承)
  2. ドロワークラスを作る(PropertyDrawer継承)
      ┗ [CustomPropertyDrawer(typeof(属性クラス))] を付ける
  3. エディタ用スクリプトは Editor フォルダへ(もしくは #if UNITY_EDITOR で囲う)
  4. 属性をフィールドに付ける → インスペクター表示が変わる

5-4. サンプル:フィールドを読み取り専用で表示する [Disable]

① 属性クラスの作成

プロジェクトウィンドウを右クリック「Create」→「C# Script」を選んで、DisableAttribute を作成します。


using UnityEngine;

public class DisableAttribute : PropertyAttribute
{
    // 必要ならコンストラクタや引数を定義可能
    public readonly bool disabled;
    public DisableAttribute(bool disabled = true)
    {
        this.disabled = disabled;
    }
}

② ドロワークラスの作成

プロジェクト内に Editor フォルダを作成し、その中に DisableDrawer.cs を作ります。


#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(DisableAttribute))]
public class DisableDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        var attr = (DisableAttribute)attribute;
        bool prev = GUI.enabled;
        GUI.enabled = !attr.disabled; // trueなら無効化(グレーアウト)

        EditorGUI.PropertyField(position, property, label, true);

        GUI.enabled = prev;
    }

    // 可変高さのプロパティでも崩れないように
    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
        => EditorGUI.GetPropertyHeight(property, label, true);
}
#endif

③ 使い方(フィールドに付与)

プロジェクトウィンドウを右クリック「Create」→「C# Script」で SampleDisable を作成し、下記のように記述します。


using UnityEngine;

public class SampleDisable : MonoBehaviour
{
    [Disable] public string authorName = "Alice";
    [Disable] public int level = 5;

    [Disable(disabled: false)] public float editableValue = 1.0f; // falseなら編集可能
}

④ シーンで確認する手順

  1. ヒエラルキー(Hierarchy)ウィンドウで右クリック → 「3D Object」→「Cube」を選ぶ(何でもOK)。
  2. 作成した SampleDisable.csドラッグ&ドロップでキューブにアタッチ。
  3. インスペクターを見ると、[Disable] を付けたフィールドがグレーアウトで表示されます。

5-5. Tips:引数付きの属性で柔軟に

属性に引数を持たせると、[Disable(true)] / [Disable(false)] のように挙動を切り替えできます。ドロワー側では (DisableAttribute)attribute から値を取り出して分岐します。

5-6. よくあるハマりどころ

  • Editorフォルダを忘れるPropertyDrawer はエディタ専用。
    Editorフォルダ配下に置くか、#if UNITY_EDITOR で囲ってください。
  • シリアライズ対象のみ描画SerializedProperty に現れないフィールド(非シリアライズ)は描画できません。public または [SerializeField] を付けましょう。
  • 実行時ロジックは不可PropertyDrawer はインスペクター表示専用。ゲーム中の動作は変わりません。

ここまでで、「属性で印を付ける」→「ドロワーで見た目を決める」という基本パターンを押さえられました。




6. 実践例:Disable属性でフィールドをグレーアウト

前章で紹介した [Disable] 属性を、実際に Unity エディタ上で試してみましょう。ここでは「インスペクターに表示されるけれど編集はできない値」を作ることを目標にします。

6-1. 属性クラスの定義

まずは DisableAttribute を定義します。これは「読み取り専用にする印」として機能します。


using UnityEngine;

public class DisableAttribute : PropertyAttribute
{
    public readonly bool disabled;

    public DisableAttribute(bool disabled = true)
    {
        this.disabled = disabled;
    }
}

ここで disabledtrue にすればフィールドを無効化、false にすれば通常通り編集可能にできます。

6-2. PropertyDrawerの作成

次に DisableDrawer を作ります。必ず Editor フォルダの中に配置してください。


#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(DisableAttribute))]
public class DisableDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        var attr = (DisableAttribute)attribute;
        bool prevState = GUI.enabled;
        GUI.enabled = !attr.disabled;

        EditorGUI.PropertyField(position, property, label, true);

        GUI.enabled = prevState;
    }

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
        => EditorGUI.GetPropertyHeight(property, label, true);
}
#endif

このコードでは、GUI.enabled を切り替えることでフィールドの編集可否をコントロールしています。

6-3. 利用例

最後に MonoBehaviour を継承したクラスで [Disable] を使ってみます。


using UnityEngine;

public class PlayerInfo : MonoBehaviour
{
    [Disable] public string playerName = "Alice";
    [Disable] public int playerLevel = 10;

    [Disable(false)] public float editableValue = 5.0f;
}

6-4. 実行手順

  1. ヒエラルキー(Hierarchy)ウィンドウで右クリック → 「3D Object」→「Cube」を作成。
  2. 作成した Cube に PlayerInfo.cs をドラッグ&ドロップしてアタッチ。
  3. インスペクターを確認すると、[Disable] を付けたフィールドがグレーアウトされ、編集できない状態になっています。

6-5. この仕組みの活用ポイント

  • デバッグ時に「表示はしたいけど値を変えてほしくない」変数に便利。
  • 読み取り専用のログ表示や、エディタ上で確認用の変数として活用可能。
  • 複雑な挙動を伴うフィールドの誤操作防止にも役立ちます。

このように、属性 + PropertyDrawer を組み合わせることで、Unityのインスペクターを自在にカスタマイズできます。 次は、さらに効率的にインスペクターを拡張できるおすすめアセットを紹介します。


7. おすすめアセット紹介

ここまで学んだように、PropertyAttribute + PropertyDrawer を使えば自分でインスペクターをカスタマイズできます。 ただし実務レベルで複雑なエディタ拡張を行うのは大変ですよね。そんなときにおすすめなのが、すでに多くの開発者に使われているUnity Asset Storeのアセットです。

Odin Inspector and Serializer

Unityのインスペクターを劇的に便利にする定番アセット。 カスタム属性を自分で書かなくても、[Button][ShowIf][TableList] など多彩な機能が揃っています。 スクリプトが増えて管理が大変…という方に特におすすめです。

👉 アセットストアで見る

Editor Console Pro

Unityの標準コンソールを拡張してくれる便利ツール。 エラーやログをフィルタリング・検索・カラー表示

👉 アセットストアで見る

これらのアセットを活用すれば、カスタム属性を自作するステップをさらに効率化できます。 Unity初心者からプロまで、インスペクターを便利に使いこなしたい方におすすめです。


8. まとめ

今回は、C#の属性(Attribute)の基礎から、UnityのPropertyAttributeとPropertyDrawerを活用したインスペクター拡張までを解説しました。

  • 属性は「コードに追加情報を与える仕組み」で、標準の [SerializeField][Range] もその一例。
  • カスタム属性を作れば、開発者独自の情報をメタデータとしてコードに埋め込める。
  • リフレクションを使えば、実行時にその情報を読み出すことも可能。
  • UnityのPropertyAttributeとPropertyDrawerを組み合わせることで、インスペクターの表示を自在に変更できる。
  • Disable属性のように「表示するけど編集はさせない」といった使い方ができる。

属性は一見地味な機能ですが、うまく使うことでコードの可読性や拡張性が向上します。 また、インスペクター拡張に応用すれば、日々の開発効率を大幅にアップできます。

さらに効率を求めるなら、Odin InspectorEditor Console Pro のような人気アセットを導入するのもおすすめです。これらを使えば、自作コードと合わせて強力な開発環境を構築できます。


あわせて読みたい

今回のテーマと関連性が高い、Unity開発に役立つ記事をいくつかピックアップしました。 インスペクター拡張や属性の応用に興味がある方はぜひチェックしてみてください。

関連記事を読むことで、今回学んだ「属性 × インスペクター拡張」の知識をさらに広げることができます。


よくある質問(FAQ)

Q
カスタム属性は実行時にも使えますか?
A

はい、使えます。リフレクションを利用すれば、実行中にクラスやメソッド、フィールドに付与されたカスタム属性を読み取ることが可能です。
ただしリフレクションは処理が重いので、ゲームループのような毎フレーム実行する場面では避け、初期化処理やエディタ拡張に活用するのがおすすめです。

Q
PropertyDrawerはビルド後のゲームでも動作しますか?
A

いいえ、PropertyDrawer は Unityエディタ専用の仕組みです。そのため、実際のゲームビルド後には影響しません。
ゲーム中の動作を制御したい場合は通常のスクリプトロジックを実装し、PropertyDrawer はあくまで開発時の作業効率を上げるためのツールとして使うと良いでしょう。

Q
Odin Inspectorなどのアセットと併用できますか?
A

はい、可能です。Odin Inspector などのインスペクター拡張アセットは、独自の属性を多数提供しており、カスタム属性と共存できます。
自作の属性とOdinの機能を組み合わせれば、さらに柔軟で効率的なインスペクター環境を構築できます。

※当サイトはアフィリエイト広告を利用しています。リンクを経由して商品を購入された場合、当サイトに報酬が発生することがあります。

※本記事に記載しているAmazon商品情報(価格、在庫状況、割引、配送条件など)は、執筆時点のAmazon.co.jp上の情報に基づいています。
最新の価格・在庫・配送条件などの詳細は、Amazonの商品ページをご確認ください。

スポンサーリンク