４頂点の色を指定できるRawImageの派生コンポーネント　RawImage4Color

https://x.com/ScreenPocket/status/1738966606246822196?s=20

UIを組み立てていると、
グラデーションでぼかしたり、色付けを行いたい事が偶にあります。
嘘です。**よくあります。**

という事で、UnityのUGUIの**RawImageを派生させて、4頂点の色を変更する事が可能なRawImage4Colorを作成** してみましょう。

パッと考えるとRawImageの４頂点に自由な色を流し込めば達成できそうなので、
その通りにコーディングしてみます。

本ブログはのコードは
 * Unity 2023.2.3f1

で作成されました。

# コード

RawImage4Colorのコードは下記です。
後半にエディタ拡張用のコードも含まれています。

```cs:RawImage4Color.cs
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.UI;
#endif
using UnityEngine;
using UnityEngine.UI;

namespace ScreenPocket
{
    /// <summary>
    /// 4すみの色を指定できるRawImage
    /// </summary>
    public class RawImage4Color : RawImage
    {
        /// <summary>
        /// 左上頂点の色
        /// </summary>
        [SerializeField]
        private Color _leftTopColor = Color.white;
        public Color leftTopColor
        {
            get => _leftTopColor;
            set => _leftTopColor = value;
        }

        /// <summary>
        /// 右上頂点の色
        /// </summary>
        [SerializeField]
        private Color _rightTopColor = Color.white;
        public Color rightTopColor
        {
            get => _rightTopColor;
            set => _rightTopColor = value;
        }

        /// <summary>
        /// 左下頂点の色
        /// </summary>
        [SerializeField]
        private Color _leftBottomColor = Color.white;
        public Color leftBottomColor
        {
            get => _leftBottomColor;
            set => _leftBottomColor = value;
        }
        
        /// <summary>
        /// 右下頂点の色
        /// </summary>
        [SerializeField]
        private Color _rightBottomColor = Color.white;
        public Color rightBottomColor
        {
            get => _rightBottomColor;
            set => _rightBottomColor = value;
        }
        
        /// <summary>
        /// メッシュ生成
        /// 大本のメッシュ作成が終わった後で色だけ流し込みます
        /// </summary>
        /// <param name="vh"></param>
        protected override void OnPopulateMesh(VertexHelper vh)
        {
            base.OnPopulateMesh(vh);

            SetVertexColor(vh, _leftBottomColor, 0);
            SetVertexColor(vh, _leftTopColor, 1);
            SetVertexColor(vh, _rightTopColor, 2);
            SetVertexColor(vh, _rightBottomColor, 3);
        }

        /// <summary>
        /// 指定した頂点に色を流し込む
        /// </summary>
        /// <param name="vh">VertexHelper</param>
        /// <param name="color">指定したい色</param>
        /// <param name="index">頂点のIndex</param>
        private void SetVertexColor(VertexHelper vh, Color color, int index)
        {
            UIVertex vertex = new ();
            
            vh.PopulateUIVertex(ref vertex, index);
            vertex.color = color * vertex.color;
            vh.SetUIVertex(vertex, index);
        }
    }// end class RawImage4Color
    
#if UNITY_EDITOR
    /// <summary>
    /// RawImageEditorを派生して色を指定できるように追加
    /// </summary>
    [CustomEditor(typeof(RawImage4Color))]
    public class RawImage4ColorEditor : RawImageEditor
    {
        private SerializedProperty _leftTopColor;
        private SerializedProperty _rightTopColor;
        private SerializedProperty _leftBottomColor;
        private SerializedProperty _rightBottomColor;

        protected override void OnEnable()
        {
            base.OnEnable();
            
            _leftTopColor     = serializedObject.FindProperty("_leftTopColor");
            _rightTopColor    = serializedObject.FindProperty("_rightTopColor");
            _leftBottomColor  = serializedObject.FindProperty("_leftBottomColor");
            _rightBottomColor = serializedObject.FindProperty("_rightBottomColor");
        }

        public override void OnInspectorGUI()
        {
            //大本のエディタ拡張を表示
            base.OnInspectorGUI();

            EditorGUI.BeginChangeCheck();
            EditorGUI.indentLevel++;
            EditorGUILayout.PropertyField(_leftTopColor);
            EditorGUILayout.PropertyField(_rightTopColor);
            EditorGUILayout.PropertyField(_leftBottomColor);
            EditorGUILayout.PropertyField(_rightBottomColor);
            EditorGUI.indentLevel--;
            if (EditorGUI.EndChangeCheck())
            {
                serializedObject.ApplyModifiedProperties();
            }
        }
    }//end class RawImage4ColorEditor
#endif
}

```

# コードの解説
と言っても、ほぼ解説するべき事は無いのですが、3点だけ記載しておきます。

## OnPopulateMesh()について

```cs
        protected override void OnPopulateMesh(VertexHelper vh)
        {
            base.OnPopulateMesh(vh);

            SetVertexColor(vh, _leftBottomColor, 0);
            SetVertexColor(vh, _leftTopColor, 1);
            SetVertexColor(vh, _rightTopColor, 2);
            SetVertexColor(vh, _rightBottomColor, 3);
        }
```
見たまんまですね。
派生元のbase.OnPopulateMesh()で面が張られるので、その後で各頂点4つに色を流し込んでいます。

## SetVertexColor()について

```cs
        private void SetVertexColor(VertexHelper vh, Color color, int index)
        {
            UIVertex vertex = new ();
            
            vh.PopulateUIVertex(ref vertex, index);
            vertex.color = color * vertex.color;
            vh.SetUIVertex(vertex, index);
        }
```
コレも見た通りですね。
vh.PopulateUIVertex()で頂点情報を取り出し、
色を指定カラーで**乗算合成**した後で、
vh.SetUIVertex()で頂点情報を設定し直しています。

とりあえず乗算合成しましたが、加算合成やスクリーン合成を行っても良いかもしれませんね。
それでもう一つブログを書くかも？

後は、最適化の視点で言うならば**vertexを都度newするのではなくstatic化してしまっても良い** のではないかな？と思います。

## エディタ拡張について

これも色編集フィールドを基底メソッドの後で追加しているだけですが、1点だけ記載すると
```cs
            serializedObject.ApplyModifiedProperties();
```
については、
```cs
            EditorGUI.BeginChangeCheck();
            ：
            ：
            ：
            if (EditorGUI.EndChangeCheck())
            {
                serializedObject.ApplyModifiedProperties();
            }
```
の形にしないと、**大本のRawImageの色編集が上手くいきません** でした。
多分 base.OnInspectorGUI() の中で　serializedObject.ApplyModifiedProperties()　を呼んでいるからかな？

# 完成！

例えば**右側だけぼかし***たり
![スクリーンショット 2023-12-25 014955.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61266/eb879ab3-bb1d-c567-4e4b-6439c3d39f61.png)

大本のColorを使って全体をまとめて半透明にしたり
![スクリーンショット 2023-12-25 015051.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61266/8f733a82-a74c-8dcb-7860-2cacf9e4c5f2.png)

4頂点の色は大本のColor、Texture、Materialと連動できるので、色々表現の上で使えそうな予感ですね。

# おわりに

という事で、4頂点の色を指定できるRawImage「RawImage4Color」でした。
乗算合成以外を指定できるカスタマイズは冬休みに時間が有ればブログを書くかもしれません。
シンプルなUIでも、ちょっとグラデーションを入れるだけでもクオリティが上がって見えますので、是非ご活用ください。