こんにちは、エンジニアのオオバです。

記事にある一定のページビューがあります。
継続的なニーズがあるのだと思われます。。

ところで前回書いた動的メッシュ記事は
Meshクラスを用いたワールド座標に表示されるメッシュです。

今回はuGUI上に動的なメッシュを表示させてみます。
まずは三角形を表示させてみましょう。

本記事は、動的にメッシュを生成するシリーズの一貫です。

とりあえずソースから。

DynamicMeshWithUGUI.cs

using UnityEngine;  
using System.Collections;  
using UnityEngine.UI;  

public class DynamicMeshWithUGUI : Graphic  
{
    protected override void OnPopulateMesh(VertexHelper vh)  
    {
        // 頂点の順番  
        vh.AddTriangle(0,1,2);  

        // UIVertex:各頂点の情報  
        var v0 = new UIVertex();  
        v0.position = new Vector3(-100f, -100f);  
        var v1 = new UIVertex();  
        v1.position = new Vector3(0, 100f);  
        var v2 = new UIVertex();  
        v2.position = new Vector3(100f, -100f);  

        // 頂点情報を渡す  
        vh.AddVert(v0);  
        vh.AddVert(v1);  
        vh.AddVert(v2);  
    }
}

uGUIで三角形を動的に描画する場合、Graphicクラスを継承してOnPopulateMesh関数をオーバーライドするところから始まります。

ではOnPopulateMesh関数の引数VertexHelperインスタンスに対して処理を書いていきましょう。

以前の記事と同様Meshに必要な最低限の情報は変わりません。

今回は配列ではなくそれ専用の関数が用意されています。

👉DOTweenの教科書を読んでUnityアニメーションをプログラミングしてみよう!

頂点座標

uGUIの場合は、UIVertexという構造体が定義されており、頂点の数分生成します。今回は三角形を描画するため、v0〜v2を生成。

前回同様Vector3型の座標をUIVertexクラスのpositionプロパティに渡します。
※今回の三角形は1辺200pixelです。

var v0 = new UIVertex();  
v0.position = new Vector3(-100f, -100f);  

生成した頂点情報UIVertexをVertexHelperにAddVert関数で渡します。

vh.AddVert(v0);  
vh.AddVert(v1);  
vh.AddVert(v2);  

頂点を結ぶ順番

vh.AddTriangle(0,1,2);  

頂点を結ぶ順番はVertextHelperクラスのAddTriangleで設定します。

早速描画

【Unity】生成したメッシュをUIで使う方法_0

何も描画されていない様に見えますが、シーンビューをWireframeにして見てみましょう。

【Unity】生成したメッシュをUIで使う方法_1

三角形が生成されていることが確認できます。

なぜ透明なのか

これは、Graphicオブジェクトにデフォルトで設定されるビルトインシェーダーUI-Default.shaderの中に答えがあります。
※ちなみにビルトインシェーダーソースは公式からダウンロード可能です

Unity - Download Archive

下記はUnityビルトインシェーダーのUI-Default.shaderの全ソースです。

Shader "UI/Default"  
{
    Properties  
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}  
        _Color ("Tint", Color) = (1,1,1,1)  

        _StencilComp ("Stencil Comparison", Float) = 8  
        _Stencil ("Stencil ID", Float) = 0  
        _StencilOp ("Stencil Operation", Float) = 0  
        _StencilWriteMask ("Stencil Write Mask", Float) = 255  
        _StencilReadMask ("Stencil Read Mask", Float) = 255  

        _ColorMask ("Color Mask", Float) = 15  

        [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0  
    }

    SubShader  
    {
        Tags  
        {
            "Queue"="Transparent"  
            "IgnoreProjector"="True"  
            "RenderType"="Transparent"  
            "PreviewType"="Plane"  
            "CanUseSpriteAtlas"="True"  
        }

        Stencil  
        {
            Ref [_Stencil]  
            Comp [_StencilComp]  
            Pass [_StencilOp]  
            ReadMask [_StencilReadMask]  
            WriteMask [_StencilWriteMask]  
        }

        Cull Off  
        Lighting Off  
        ZWrite Off  
        ZTest [unity_GUIZTestMode]  
        Blend SrcAlpha OneMinusSrcAlpha  
        ColorMask [_ColorMask]  

        Pass  
        {
        CGPROGRAM  
            #pragma vertex vert  
            #pragma fragment frag  

            #include "UnityCG.cginc"  
            #include "UnityUI.cginc"  

            #pragma multi_compile __ UNITY_UI_ALPHACLIP  

            struct appdata_t  
            {
                float4 vertex   : POSITION;  
                float4 color    : COLOR;  
                float2 texcoord : TEXCOORD0;  
            };  

            struct v2f  
            {
                float4 vertex   : SV_POSITION;  
                fixed4 color    : COLOR;  
                half2 texcoord  : TEXCOORD0;  
                float4 worldPosition : TEXCOORD1;  
            };  

            fixed4 _Color;  
            fixed4 _TextureSampleAdd;  
            float4 _ClipRect;  

            v2f vert(appdata_t IN)  
            {
                v2f OUT;  
                OUT.worldPosition = IN.vertex;  
                OUT.vertex = mul(UNITY_MATRIX_MVP, OUT.worldPosition);  

                OUT.texcoord = IN.texcoord;  

                #ifdef UNITY_HALF_TEXEL_OFFSET  
                OUT.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1);  
                #endif  

                OUT.color = IN.color * _Color;  
                return OUT;  
            }

            sampler2D _MainTex;  

            fixed4 frag(v2f IN) : SV_Target  
            {
                half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;  

                color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);  

                #ifdef UNITY_UI_ALPHACLIP  
                clip (color.a - 0.001);  
                #endif  

                return color;  
            }
        ENDCG  
        }
    }
}

ポイントは88行目の頂点シェーダー処理。
v2f vert(appdata_t IN)vertメソッドの部分です。

OUT.color = IN.color * _Color;  

IN.colorとは頂点に設定されているカラー情報です。
ここが(0, 0, 0, 0)になっているため透明で描画されている理由です。

頂点カラーを設定する

DynamicMeshWithUGUI.csに修正を加えいます。
各頂点に色を設定してみます。

DynamicMeshWithUGUI.cs全文

using UnityEngine;  
using System.Collections;  
using UnityEngine.UI;  

public class DynamicMeshWithUGUI : Graphic  
{
    protected override void OnPopulateMesh(VertexHelper vh)  
    {
        // 頂点の順番  
        vh.AddTriangle(0,1,2);  

        // UIVertex:各頂点の情報  
        var v0 = new UIVertex();  
        v0.position = new Vector3(-100f, -100f);  
        // 修正箇所 : 色情報追加  
        v0.color = new Color32(255, 0, 255, 255);  
        var v1 = new UIVertex();  
        v1.position = new Vector3(0, 100f);  
        // 修正箇所 : 色情報追加  
        v1.color = new Color32(255, 255, 255, 255);  
        var v2 = new UIVertex();  
        v2.position = new Vector3(100f, -100f);  
        // 修正箇所 : 色情報追加  
        v2.color = new Color32(255, 255, 0, 255);  

        // 頂点情報を渡す  
        vh.AddVert(v0);  
        vh.AddVert(v1);  
        vh.AddVert(v2);  
    }
}

【Unity】生成したメッシュをUIで使う方法_2

着色状態で描画されました!

まとめ

uGUIで動的にメッシュを生成してみました。
通常のメッシュ生成と違い、
uGUIにはUIVertexが用意されていて、
手数少なく実装できました。

次回以降このメッシュにテクスチャを貼ってみたり、
三角形以外の形を描画してみようと思います。

オススメ記事
検証環境