テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

UnityのAttribute(属性)についてまとめてメモる。

Unityの属性について紹介します。

目次

属性(Attribute)とは

Unityを利用するC#は、クラス定義や変数定義に属性(Attribute)を付与する事で他の変数と区別したり、特別な挙動を設定することが出来ます。*1

 

例えば変数に[SerializeField]属性を追加すれば、privateな変数であってもシリアライズされシーン上に表示されるようになります。

[SerializeField]
 int count;
 

f:id:tsubaki_t1:20150103165049p:plain

シリアライズは、とりあえずは「シーンやプレハブに値が保存される」と認識しておけばOKです。シーンやプレハブとったメタデータに値が保存されるため、ゲームを再生せずともInspectorで値を設定する事が出来る訳です。

 

また、[RequireComponent]をクラスに追加すれば、記述したコンポーネント*2がアタッチされているオブジェクトに指定したコンポーネント*3を強制する事が出来ます。

[RequireComponent(typeof(Rigidbody))]
public class AttributeSample : MonoBehaviour {

}

属性を利用することで、命名規則以外で変数に特別な挙動を設定することが出来ます。Unityは属性を利用して、エディタやメソッド等に特別な挙動を設定しています。

属性の表記ルール

属性は変数やクラスの定義前に [***] のような形で記述します。複数の属性を設定したい場合、[***, ***] のようにカンマで区切り記述します。

例えばSerializeFieldかつRangeの属性を設定したい場合、以下のように記述します。

    [SerializeFieldRange(05)]
    int count

f:id:tsubaki_t1:20150103165851p:plain

属性は直後に定義した変数全てに適応されます。例えば一つの宣言で複数の変数を定義するような場合、そのどちらにも適応されます。

    [SerializeFieldRange(05)]
    int count3count4;

f:id:tsubaki_t1:20150103170135p:plain

同様に配列に対して適当した場合、内包する全ての変数に適応されます。

    [SerializeFieldRange(05)]
    int[] counts;

f:id:tsubaki_t1:20150103170258p:plain

Unity標準の属性

Inspectorを拡張する属性

フィールドに設定することでInspectorの挙動を拡張します。

  • SerializeField
    privateやprotectedの値をシリアライズします。
    シーンビューで編集したい場合に有効です。

       [SerializeField]
        int count;

  • TooltipAttribute

    マウスカーソルがフィールドの上にある場合に説明文を表示します。   

     

    [SerializeFieldTooltipAttribute("説明文")]
    int count5;

     

    f:id:tsubaki_t1:20150103170913p:plain

  • SpaceAttribute

    フィールドとフィールドの間にスペースを設定します。

     

    [SerializeFieldSpace(15)]
    int count6;

     

    f:id:tsubaki_t1:20150103171238p:plain

  • HeaderAttribute

    フィールドの頭にタイトルを設定します。リストにHeaderAttributeを追加すると全項目にタイトルが付与されるので注意が必要です

     

    [SerializeFieldHeaderAttribute ("Title")]
    int count7;

     

    f:id:tsubaki_t1:20150103171504p:plain

  • MultilineAttribute

    複数行の入力が可能なテキストフィールドを設定します。

     

        [SerializeFieldMultilineAttribute (2)]
        string message1;

     

    f:id:tsubaki_t1:20150103172116p:plain

  • TextAreaAttribute

    複数行の入力が可能なテキストフィールドを設定します。行数の最小値・最大値を設定することができます。

     

    [SerializeField, TextAreaAttribute(25)]
     string message2;

     

    f:id:tsubaki_t1:20150103172425p:plain

  • HideInInspector

    public等のシリアライズするフィールドをInspectorから隠します。エディタ拡張で参照関係やパラメータを構築した状態で、パラメータを隠したい場合等に便利です。

    Unityはシーンを構築する際にシリアライズした値を優先して使用するため、HideInInspectorで隠すとInspectorから編集出来ず、フィールド変数宣言時の初期化も無視される点に注意して下さい。

    純粋にシリアライズしたくない場合はNonSerializableを利用します。

     

        [HideInInspector]
        public int count8;

     

  • NonSerializable

    シリアライズしないように設定し、Inspectorから表示されなくします。 

     

      [System.NonSerialized]
        public int count9;

     

  • FormerlySerializedAs
  • 変数名を変更した際に破棄される情報を保持します。
    Unityはフィールドの情報をフィールド名で保持するため、フィールド名が変更されると値が破棄されます。その際、FormerlySerializedAsを指定しておくことで移行先の変数名にこの情報が引き継がれるようになります。

     public float first;

    f:id:tsubaki_t1:20150103191655p:plainまずは変数firstがあります。これを変数secondに変更しようと思います。その際、FormerlySerializedAs属性を追加し、旧名"first"を設定します。

    [FormerlySerializedAs("first")]
     public float second;

    f:id:tsubaki_t1:20150103191702p:plain

     これで変数名firstで設定した値は変数名secondに変更した後も破棄されず残ります。移行後はFormerlySerializedAsは削除して構いませんが、他のシーンで使っていた場合に色々面倒かもしれないので注意して下さい。

コンポーネントの動作に関連する属性

  • RequireComponentAttribute

    コンポーネントの動作に必要なコンポーネントがオブジェクトにアタッチされていなければ追加します。例えばRigidbody等。

    [RequireComponent(typeof(Rigidbody))]
    public class AttributeSample : MonoBehaviour {
    }

  • DisallowMultipleComponent

    同一オブジェクトに複数のコンポーネントを追加出来ないようにします。

     

    [DisallowMultipleComponent]
    public class AttributeSample : MonoBehaviour {
    }

     

    f:id:tsubaki_t1:20150103203508p:plain

  • ContextMenuAttribute

    コンポーネントコンテキストメニュー(右上のマーク)からメソッドを呼び出せるようにします。例えばセットアップをランタイムではなく事前に行う場合に便利です。

    [ContextMenu("Init")]
    void Init(){
    }

    f:id:tsubaki_t1:20150103182902p:plain

ゲームの動作に影響する属性

  • RPC

    RPCを行う時に使うやつです。NetworkViewで通信したりPhotonで通信する際に使います。この属性があるメソッドは、ネットワーク越しに呼ばれます。

     

    [RPC]
    void Damage(){
    }

     

  • ImageEffectTransformsToLDR

    HDRからLDRに変換するらしいです。使い方不明。

  • ImageEffectOpaque

    OnRenderImageに付与すると透明より前にレンダリングします。

     

    [ImageEffectOpaque]
    void OnRenderImage (RenderTexture sourceRenderTexture destination){
    }

     

  • DLLImport

    C++(アンマネージドコード)のメソッドをC#から呼び出せるようにします。

     

    [DllImport("DLL Name")]
     private static extern void MethodName();

     

エディタの動作に関連する属性

エディタの上からシーンに影響を与える事のできる属性です。

  • AddComponentMenu

    メニューバーのComponentやAddComponentボタンから指定する際のパスを設定します。これを行わない場合、Script/Namespace/コンポーネント名が設定されます。

     

    [AddComponentMenu("Sample/TestCode")]
    public class AttributeSample : MonoBehaviour{

    }

     

    f:id:tsubaki_t1:20150103174334p:plain

  • ExecuteInEditMode

    コンポーネントのUpdateやStartといったイベントを、ゲームを再生しない状態でも動作するようにします。ランタイムで動作する挙動を確認する際に便利です。

     

    [ExecuteInEditMode]
    public class AttributeSample : MonoBehaviour {}

     


    ExecuteInEditMode はホットスポットのような形で動作し、コードが変更になった際にシリアライズ出来る値はシリアライズコンパイル→再注入となり、出来ない値は破棄されま す。このため、Start等で初期化しているコードやstaticに依存するコードはシーンを再生する必要が出たりするので注意が必要です。
    例えば以下のコードはコード変更前は3を出力し、コード変更しコンパイルが走ると0を出力します。

     

    static int sCount = 0;
    void Start(){
        sCount = 3;
    }
    void Update(){
       Debug.Log("export:" + sCount);
    }

     

    f:id:tsubaki_t1:20150103184915p:plain

  •  SelectionBaseAttribute

    シーンビューで選択した際にこの属性を持つコンポーネントを選択するようにします。

     

    [SelectionBase]
    public class AttributeSample : MonoBehaviour {}

     

    例えばモデルがシーンビューにあった場合、シーンビューで選択するとモデルのRendererが選択されますが、親オブジェクトにSelectionBaseAttributeを付与したコンポーネントを付与すると、選択時に親オブジェクトが選択されます。f:id:tsubaki_t1:20150103185829g:plainf:id:tsubaki_t1:20150103185848g:plain

    上がGameObjectオブジェクトにSelectionBaseを付与した際の動作、下が付与していない場合の動作です。付与していない物はSphereを取得しますが、付与しているとGameObjectオブジェクトを取得します。

  • CreateAssetMenu

メニューバーにScriptableObjectのアセットを生成するメニューを追加します。

[CreateAssetMenu()] 
public class Data  : ScriptableObjec{}

f:id:tsubaki_t1:20151108193758j:plain

 

エディタ関連はまた後日まとめます多分。

属性を自作する

Inspectorで描画を行う属性や、通常の属性は自作することが出来ます。

この記事が参考になります。

 Unity3D - 自分だけのPropertyDrawerを作ろう! - Qiita

 

結構な量のサンプルがココにあるので、自作する前に見ておくと良さそうです。anchan828/property-drawer-collection · GitHub

 

また、単純に属性を使用する場合もそこそこ役立つケースが有ります。

例えばエディタでゲーム再生中に変更した値を停止しても消さない その2 では属性を持った変数を識別し、値を一時保存する事でシーン終了時に破棄されるパラメータを保持するのに使ったり、シーンのビルド時にプラットフォーム毎に値を変更するのに使用したりといった活用が出来ます。

 

関連資料

PanzerSoftのブログ Unityのスクリプトで利用する属性(アトリビュート)

An Overview of Unity Attributes

Extending Unity Container to Support DefaultValue Attribute - CodeProject

Unity3D - 自分だけのPropertyDrawerを作ろう! - Qiita

HeaderとSpaceを使ってInspectorを整理する - テラシュールブログ

NativePluginsにC#デリゲートを登録する - テラシュールブログ

エディタでゲーム再生中に変更した値を停止しても消さない その2 - テラシュールブログ

属性 (C# によるプログラミング入門)

 

サンプル

anchan828/property-drawer-collection · GitHub

 

*1:エレメーラ(属性力)とは関係ありません。

*2:AttributeSample

*3:今回はRigidbody