【Unity】アニメーションクリップでuGUIのマスクの形を変更させる

はじめに

はじめまして、ソーシャルゲーム事業部のUnityエンジニアのクアンです。
この記事はカヤックUnityアドベントカレンダー2018の13日目の記事になります。

6日目の記事ではメッシュで枠を描画していましたが、この記事ではカットイン演出に使うマスクのメッシュ形状をアニメーションさせてみようと思います。

DynamicMaskとは

ランタイムで頂点からマスク形状を描画するためのコンポネートです。
この記事では四角形のマスクしか対応していません。

DynamicMaskを試しましょう。
下のような入れ子構造で、シーンに配置します。

f:id:quan-nguyen:20181206121217p:plain

さらに、「mask」にuGUIのマスクコンポネートと「DynamicMask」コンポネートを追加。

f:id:quan-nguyen:20181206121350p:plain

上のスクリーンショットのVert1(左下), Vert2(左上), Vert3(右上), Vert4(右下)はそれぞれ4つの頂点です。

頂点の座標を入力してコンテキストメニューで「SetVerticesDirty」を選択したら画像は描画されます。

f:id:quan-nguyen:20181206121431p:plain

実装

基本的にはUnityEngine.UI.Graphicクラスを継承してOnPopulateMesh関数をオーバーライドするところから始まります。
ではOnPopulateMesh関数の引数VertexHelperインスタンスに対して処理を書いていきましょう。

public class DynamicMask : Graphic
{
    // 各頂点の情報
    public Vector2 vert1;
    public Vector2 vert2;
    public Vector2 vert3;
    public Vector2 vert4;

    protected override void OnPopulateMesh(VertexHelper toFill)
    {
        base.OnPopulateMesh(toFill);

        toFill.Clear();

        // 頂点情報を渡す
        toFill.AddVert(vert1, color, new Vector2(0f, 0f));
        toFill.AddVert(vert2, color, new Vector2(0f, 1f));
        toFill.AddVert(vert3, color, new Vector2(1f, 1f));
        toFill.AddVert(vert4, color, new Vector2(1f, 0f));

        // 頂点の順番
        toFill.AddTriangle(0, 1, 2);
        toFill.AddTriangle(2, 3, 0);
    }

#if UNITY_EDITOR
    [ContextMenu("SetVerticesDirty")]
    private void MenuSetVerticesDirty()
    {
        SetVerticesDirty();
    }
#endif
}

UIコンポーネントを拡張したから、Inspectorで頂点のプロパティーを表示するためにEditorクラスを追加します

[CustomEditor(typeof(DynamicMask))]
public class DynamicMaskEditor : GraphicEditor
{
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();

        var mask = (DynamicMask)target;
        mask.vert1 = EditorGUILayout.Vector2Field("Vert 1", mask.vert1);
        mask.vert2 = EditorGUILayout.Vector2Field("Vert 2", mask.vert2);
        mask.vert3 = EditorGUILayout.Vector2Field("Vert 3", mask.vert3);
        mask.vert4 = EditorGUILayout.Vector2Field("Vert 4", mask.vert4);
    }
}

アニメーションによって頂点の情報が変更されたときに画像を更新するため、OnDidApplyAnimationPropertiesをオーバーライドします。

// 頂点の情報がアニメーションによる変更が行われたときのコールバック
protected override void OnDidApplyAnimationProperties()
{
    base.OnDidApplyAnimationProperties();

    SetVerticesDirty();
}

次にマスクをアニメーションさせてみましょう。

画像とマスクを配置する

下のような入れ子構造で、シーンに画像とマスクを配置します。

f:id:quan-nguyen:20181206122222p:plain

f:id:quan-nguyen:20181206121531p:plain

上記のような「mask」にuGUIのマスクコンポネートと「DynamicMask」コンポネートを追加。

こうすることで、「mask」の子要素の表示が、「mask」のImage範囲のみに限定されます。

マスクをアニメーションで動かす

「DynamicMask」の頂点にキーフレームを追加。

f:id:quan-nguyen:20181206121552p:plain

実行すると、こんなかんじでマスクがアニメーションします。

f:id:quan-nguyen:20181206121613g:plain

おわりに

ゲームで必殺技とかのカットインによくマスクアニメーションを使用するのでご紹介しました。
読者の方のお役に立てば大変うれしいです。

明日は中島さんによる Androidで情報をローカル保存するときの話 になります。 よろしければお読みになってください。