[Unity] Attributeいろいろ

## はじめに
Attributeとは、
```
[SerializeField,Range(0,1)] float rate=0f
```
のように、変数やクラスの前にある```[]```内の定義のことです。

Unityでよく使うor便利なAttributeをまとめてみました。

実際に試すのが一番わかりやすいので、下記のスクリプトを空のGameObjectにアタッチし、子の階層に```Create>3DObject>Cube```してみてください
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/162f122b-bb93-8676-1c7b-cc3a31fb1fa9.png)

```
using System;
using UnityEngine;
using Unity.Cinemachine; // [MinMaxRangeSlider()]

#if UNITY_EDITOR
using UnityEditor;
#endif

namespace Elix.Test {
    //Classにヘルプを追加します
    [HelpURL("https://docs.unity3d.com/2022.3/Documentation/ScriptReference/HelpURLAttribute.html")]
    //GameObjectに同じClassを追加できないようにします
    [DisallowMultipleComponent]
    //Sceneで子Objectを選択したときにInspectorに表示されるようにします
    [SelectionBase]
    //Script Execution Orderをスクリプトから設定します
    [DefaultExecutionOrder(-10)]
    //[ExecuteInEditMode]は将来廃止される予定
    //[ExecuteAlways]
    //Classの下に警告を出します（廃止された時などに使用）
    [Obsolete("このクラスは廃止されます")]
    public class AttributeSamples : MonoBehaviour
    {
        //enum表示時に名前でソートします
        [InspectorOrder()]
        public enum MyEnum
        {
            Auto = 0,
            [InspectorName("2だけどOne")]
            OneBut2 = 2,
            [InspectorName("4だけどTwo")]
            TwoBut4 = 4,
            [InspectorName("1だけどThree")]
            ThreeBut1 = 1,
        }

        [Tooltip("変数:右クリックメニューにアイテムを追加します")]
        [ContextMenuItem("黄色に変更", "changeColorToYellow")]
        //変数:ColorのAlpha,hdr変更を可能にします
        [ColorUsage(false, true)]
        // private変数をInspectorに表示します
        [SerializeField]
        Color _myCol = Color.red;

        [Tooltip("変数:Gradientをhdrにします")]
        [GradientUsage(true)]
        public Gradient hdrGradient;

        [SerializeField, Tooltip("実行時変数:値を確定後に変更します")]
        [Delayed]
        //Inspectorに変数のタイトルを表示します
        [Header("transform.x")]
        float _myPosX = 0f;

        [SerializeField, Tooltip("変数:最小値と最大値を設定します")]
        [Range(0, 2)] float _myRate = 0f;

        [Tooltip("enum:enumに名前を付けます")]
        public MyEnum myEnum = MyEnum.Auto;

        [Tooltip("変数:stringの表示行数を変更します")]
        [Multiline(3)]
        public string m_myPoem = "朝焼夕焼食べるものがない\n種田山頭火";

        [Header("using Unity.Cinemachine で使用できるようになるもの")]
        [MinMaxRangeSlider(-10, 10)] public Vector2 m_myMinMax = Vector2.zero;

        [Tooltip("スクリプト:ScriptのInspectorにメニューを追加します")]
        [ContextMenu("緑に変更しxをゼロに")]
        void changeColAndX()
        {
            changeColor(Color.green);
            _myPosX = 0;
        }

        void changeColorToYellow() { changeColor(Color.yellow); }

        void changeColor(Color _col)
        {
            _myCol = _col;
            GradientColorKey[] colKeys = hdrGradient.colorKeys;
            colKeys[0].color = _col;
            hdrGradient.SetKeys(colKeys, hdrGradient.alphaKeys);
        }


        // Start is called before the first frame update
        void Start()
        {
            Debug.Log(m_myPoem);
            GradientColorKey[] colKeys = new GradientColorKey[2];
            colKeys[0].color = Color.red;
            colKeys[0].time = 0.0f;
            colKeys[1].color = Color.blue;
            colKeys[1].time = 1.0f;
            hdrGradient.SetKeys(colKeys, hdrGradient.alphaKeys);
        }

        // Update is called once per frame
        void Update()
        {
            transform.position = new Vector3(_myPosX, 0, 0);
            if (transform.childCount > 0)
            {
                _myRate= (_myRate+Time.deltaTime) % 2f;
                Color col = hdrGradient.Evaluate(Mathf.PingPong(_myRate,1f));
                transform.GetChild(0).GetComponent<Renderer>().sharedMaterial.SetColor("_Color", col);
                transform.GetChild(0).transform.rotation = Quaternion.AngleAxis(_myRate*360f,Vector3.one);
            }

#if UNITY_EDITOR
            // EditorでPlay中はログを出力しません
            if (!EditorApplication.isPlaying)
            {
                Debug.Log("X:" + _myPosX.ToString());
            }
#endif
        }
    }
}
```

以下は簡単な説明です。

## Classに追加するAttribute
##### [HelpURL("url")]
Classにヘルプを追加します
スクリプトの後ろの？マークをクリックするとurlが開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/706b9add-4068-df9e-dbbf-d76e5df40f70.png)

##### [DisallowMultipleComponent]
GameObjectに同じコンポーネントを追加できないようにします
既に```AttributeSamples```がアタッチされているGameObjectに対して再度```AttributeSamples```をアタッチしようとすると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/cdb107ca-6881-1edf-2ad4-79564d7ff3b3.png)
のように警告が表示されます。

##### [DefaultExecutionOrder(-10)]
Script Execution Orderをスクリプトから設定します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/46be3d0c-a8dc-5ba3-14e6-82bcee33a8d0.png)

##### [SelectionBase]
Sceneで子Objectを選択した時もInspectorに表示されるようにします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/28859faf-cc7b-c7ed-9880-d65d459bc950.png)
Sceneで子ObjectのCubeを選択しましたが、InspectorにはAttributeSamplesが表示されています。

##### [ExecuteAlways]
プロパティやSceneが変更された時のみ、エディター非Play中もUpdate()が実行されます
以前は```[ExecuteInEditMode]```というAttributeがありましたが、こちらは将来廃止されるようです。

##### [Obsolete("message")]
Classの下に警告を出します。また、スクリプト名の後ろに```(Deprecated)```と表示されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/24b1b7ae-0a1e-49ac-de2f-64a744573bda.png)

## enumに追加するAttribute
##### [InspectorName("name")]
Inspectorで表示される名前を変更します
```
[InspectorName("2だけどOne")]
OneBut2 = 2,
```
##### [InspectorOrder()]
Inspectorで表示時に名前でソートします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/f90e86ee-42ba-4d49-3d39-31e2444e42b1.png)

## 変数に追加するAttribute
##### [Tooltip("message")]
変数をマウスオーバーしたときにツールチップを表示します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/2f433298-c741-2fca-e2e0-fd41b228c7b9.png)

##### [ContextMenuItem("message", "function")]
変数を右クリックしたときのメニューを追加します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/ac2e3016-708d-eb84-2e5d-bef8dcc1c71f.png)

##### [ColorUsage(bool, bool)]
ColorのAlpha,hdr変更を可能にします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/66795100-5fd1-895f-c1f8-614583d62d34.png)

##### [GradientUsage(bool)]
Gradientをhdrにします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/6f3509e8-a94f-0876-e8ed-c7d605d45274.png)

##### [Delayed]
値を確定後に変更します
通常、Inspectorで変更した変数は即座に反映されますが、```[Delayed]```を付けた変数はリターンを押す、フォーカスを外すなど値を変更確定した後に更新されます。

##### [Range(min, max)]
Inspector上にスライダーを表示します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/fa878ba3-ec4f-2b4d-a2fb-892ccebb9800.png)

##### [Multiline(num)]
String変数の表示行数を変更します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/5c4d6fe4-f040-0900-471e-d717da4ff662.png)

##### [FormerlySerializedAs("previousName")]
Inspectorの値を保持したまま変数名を変更します
通常、例えば```float fval```を```float rateVal```のように変更すると、fvalに設定されていた値は消えてしまいますが、
```
 [SerializeField, FormerlySerializedAs("fval")] float rateVal;
```
のように変更前の変数名を指定することで、```rateVal```に値を引き継ぐことができます。
例えば変数```fval```が```10.f```であった場合、変更後の変数```rateVal```も```10.f```になります。
また、Unityでは```fval```と```m_fVal```はInspector上で同じ```fval```と表示されますが、
```FormerlySerializedAs```を指定しないとfvalに設定されていた値は消えてしまいます。

## 関数に追加するAttribute
##### [ContextMenu("message")]
スクリプトを右クリックしたときに表示されるリストに項目を追加し、選択時に実行します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/eeb83770-6ef7-136b-0f55-357ead4602e5.png)

## using Unity.Cinemachine で使用できるようになるもの ##
##### [MinMaxRangeSlider(-10, 10)]
```using Unity.Cinemachine;``` を加えることで、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/633db5df-1921-edc4-aed4-ccf81c46a668.png)
最小値・最大値を設定できるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/7e323dc9-5cfc-35f9-647c-2c233d2b3bce.png)

## まとめ
Unity2022あたりから、地味に地味に使いやすくなってきたEditorですが、その恩恵を受けてAttributeもいろいろ追加されているようです。
さらに詳しく知りたい方は
[こちら](https://docs.unity3d.com/ja/2020.3/ScriptReference/AddComponentMenu.html)からはじまるAttributesを確認してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/8fb3f47e-facf-bd39-a5d3-60b5400f4dec.png)
Attributes全体へのURLってないんですよね。。。