LoginSignup
30
17

More than 5 years have passed since last update.

[Unity]TextShaderAnimationまとめ

Last updated at Posted at 2017-06-04

概要

Unityのテキストをシェーダーを使ってアニメーションさせる方法のまとめ
demo.gif

こんな感じでテキストをアニメーションさせるスクリプトを書いてみました。
https://github.com/katsuma99/TextShaderAnimation

移動アニメーション

demo1.gif

TextMoveAnimation.shader
Shader "Custom/TextMoveAnimation" {
    Properties
    {
        [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
        _Color("Color", Color) = (1,1,1,1)
        _Power("Power", Float) = 1
        _NormalTime("NormalizationTime", Float) = 0
        _TextCount("TextCount", Int) = 1
        [MaterialToggle] PixelSnap("Pixel snap", Float) = 1
    }

    SubShader
    {
        Cull Back
        Lighting Off
        AlphaToMask On

        Pass
        {
            CGPROGRAM

#pragma target 3.5
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile PIXELSNAP_ON
#include "UnityCG.cginc"

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

            struct v2f
            {
                float4 pos   : SV_POSITION;
                fixed4 color : COLOR;
                half2 texcoord  : TEXCOORD0;
            };

            fixed4 _Color;
            float _Power;
            int _TextCount;
            float _NormalTime;

            float4 TransportInterval(float4 pos, uint vertexId) {

                uint geometoryId = vertexId / 4;
                uint textId = ((_TextCount - 1) - geometoryId);
                pos.x -= textId * 10 * _Power * _NormalTime;

                return pos;
            }

            v2f vert(appdata_t IN)
            {
                v2f OUT;
                float4 pos = IN.vertex;
                pos = TransportInterval(pos, IN.vertexId);

                OUT.pos = UnityObjectToClipPos(pos);
                OUT.texcoord = IN.texcoord;
                OUT.color = _Color;

                return OUT;
            }

            sampler2D _MainTex;
            fixed4 frag(v2f IN) : SV_Target
            {
                fixed4 c = tex2D(_MainTex, IN.texcoord);
                c.rgb = IN.color;
                c.rgb *= c.a;

                return c;
            }
            ENDCG
        }
    }
    Fallback "GUI/Text Shader"
}

解説

        _Color("Color", Color) = (1,1,1,1)
        _Power("Power", Float) = 1
        _NormalTime("NormalizationTime", Float) = 0
        _TextCount("TextCount", Int) = 1

_Color : テキストの色を指定
_Power : アニメーションの影響度合
_NormalTime : アニメーションの時間(0 noPower 1 maxPower)
 1 -> 0に変化するように設定すると広がって元の状態に戻るアニメーションになる
_TextCount : 文字数を指定

            float4 TransportInterval(float4 pos, uint vertexId) {

                uint geometoryId = vertexId / 4;
                uint textId = ((_TextCount - 1) - geometoryId);
                pos.x -= textId * 10 * _Power * _NormalTime;

                return pos;
            }

vertexId : 描画するポリゴンの頂点番号
 SV_VertexIDのセマンティクスで取得する
geometoryId : ポリゴン番号
textId : テキスト番号(右から数えたポリゴン番号)
ポリゴン番号.PNG

回転アニメーション

demo2.gif

TextRotateAnimation.shader
Shader "Custom/TextRotateAnimation" {
    Properties
    {
        [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
        _Color("Color", Color) = (1,1,1,1)
        _Power("Power", Float) = 1
        _NormalTime("NormalizationTime", Float) = 0
        _TextCount("TextCount", Int) = 1
        [MaterialToggle] PixelSnap("Pixel snap", Float) = 1
    }

    SubShader
    {
        Cull Back
        Lighting Off
        AlphaToMask On

        Pass
        {
            CGPROGRAM

#pragma target 4.0
#pragma vertex vert
#pragma geometry geo
#pragma fragment frag
#pragma multi_compile PIXELSNAP_ON
#include "UnityCG.cginc"

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

            struct geomIn
            {
                float4 pos   : SV_POSITION;
                fixed4 color : COLOR;
                half2 texcoord  : TEXCOORD0;
            };

            struct v2f
            {
                float4 pos   : SV_POSITION;
                fixed4 color : COLOR;
                half2 texcoord  : TEXCOORD0;
            };

            fixed4 _Color;
            float _Power;
            int _TextCount;
            float _NormalTime;

            v2f vert(appdata_t IN)
            {
                v2f OUT;
                OUT.pos = UnityObjectToClipPos(IN.vertex);
                OUT.texcoord = IN.texcoord;
                OUT.color = _Color;
                return OUT;
            }

            float4 CalculateCenter(float s, float t, float4 pos1, float4 pos2, float4 pos3) {
                float4 pos = pos1 + (pos2 - pos1) * s + (pos3 - pos1) * t; // 3点の中間位置を求める
                return pos;
            }

            float4 Rotate(float4 pos, uint vertexId) {

                //画面の中心から回転する
                float Deg2Rad = 0.0174532924;
                int id = vertexId % 8;

                //最大角度(場所によって変える)
                float rotate = _NormalTime * _Power * 50;
                if (id == 0 || id == 1)
                    rotate *= 0.3 + abs(_CosTime.z) * 0.8;
                else if (id == 2 || id == 3)
                    rotate *= 0.2 + abs(_CosTime.z) * 0.1;
                else if (id == 6 || id == 7)
                    rotate *= 0.3 + abs(_SinTime.w) * 0.2;
                float rad = rotate * Deg2Rad;

                //角度算出
                float4 newPos = pos;
                newPos.x = pos.x * cos(rad) - pos.y * sin(rad);
                newPos.y = pos.x * sin(rad) + pos.y * cos(rad);
                return newPos;
            }

            [maxvertexcount(3)]
            void geo(triangle geomIn In[3], uint primitiveId : SV_PrimitiveID, inout TriangleStream<v2f> TriStream) {

                //回転
                float4 center = (In[0].pos - In[2].pos) * 0.5 + In[2].pos;

                In[0].pos -= center;
                In[1].pos -= center;
                In[2].pos -= center;

                In[0].pos = Rotate(In[0].pos, primitiveId);
                In[1].pos = Rotate(In[1].pos, primitiveId);
                In[2].pos = Rotate(In[2].pos, primitiveId);

                In[0].pos += center;
                In[1].pos += center;
                In[2].pos += center;

                TriStream.Append(In[0]);
                TriStream.Append(In[1]);
                TriStream.Append(In[2]);

                // 連続したトライアングルを終了
                TriStream.RestartStrip();
            }

            sampler2D _MainTex;

            fixed4 frag(v2f IN) : SV_Target
            {
                fixed4 c = tex2D(_MainTex, IN.texcoord);
                c.rgb = IN.color;
                c.rgb *= c.a;
                return c;
            }
            ENDCG
        }
    }
    Fallback "GUI/Text Shader"
}

解説

            [maxvertexcount(3)]
            void geo(triangle geomIn In[3], uint primitiveId : SV_PrimitiveID, inout TriangleStream<v2f> TriStream) {

                //回転
                float4 center = (In[0].pos - In[2].pos) * 0.5 + In[2].pos;

                //省略

                TriStream.Append(In[0]);
                TriStream.Append(In[1]);
                TriStream.Append(In[2]);

                // 連続したトライアングルを終了
                TriStream.RestartStrip();
            }

[maxvertexcount(3)] : プリミティブの頂点数を指定
 テキストは三角形ポリゴン2枚で1文字を描画するプレートを作っている
triangle geomIn In[3] : ジオメトリ頂点を取得
 指定したプリミティブの頂点数以上は取得できない
primitiveId : プリミティブの番号
center : 回転させるための中心を計算
 ジオメトリ頂点番号0と2の線分の中点
TriStream.Append(In[0]) : 描画する頂点を指定
 指定した順番に頂点を繋いで面を設定する
 プリミティブ番号.PNG

スケールアニメーション

demo3.gif

TextScaleAnimation.shader
Shader "Custom/TextScaleAnimation" {
    Properties
    {
        [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
        _Color("Color", Color) = (1,1,1,1)
        _Power("Power", Float) = 1.5
        _NormalTime("NormalizationTime", Float) = 0
        _TextCount("TextCount", Int) = 1
        [MaterialToggle] PixelSnap("Pixel snap", Float) = 1
    }

    SubShader
    {

        Cull Back
        Lighting Off
        AlphaToMask On

        Pass
        {
            CGPROGRAM

#pragma target 4.0
#pragma vertex vert
#pragma geometry geo
#pragma fragment frag
#pragma multi_compile PIXELSNAP_ON
#include "UnityCG.cginc"

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

            struct geomIn
            {
                float4 pos   : SV_POSITION;
                fixed4 color : COLOR;
                half2 texcoord  : TEXCOORD0;
            };

            struct v2f
            {
                float4 pos   : SV_POSITION;
                fixed4 color : COLOR;
                half2 texcoord  : TEXCOORD0;
            };

            fixed4 _Color;
            float _Power;
            int _TextCount;
            float _NormalTime;

            v2f vert(appdata_t IN)
            {
                v2f OUT;
                float4 pos = IN.vertex; // Scale(IN.vertex, IN.vertexId);
                OUT.pos = UnityObjectToClipPos(pos);
                OUT.texcoord = IN.texcoord;
                OUT.color = _Color;
                return OUT;
            }

            [maxvertexcount(3)]
            void geo(triangle geomIn In[3], uint primitiveId : SV_PrimitiveID, inout TriangleStream<v2f> TriStream) {

                //拡大
                float4 center = (In[0].pos - In[2].pos) * 0.5 + In[2].pos;
                float power = _NormalTime * _Power;
                int id = primitiveId % 10;
                if (id == 2 || id == 3)
                    power *= 0.1 + abs(_CosTime.w) * 0.3;
                else if (id == 4 || id == 5)
                    power *= 0.8 - abs(_SinTime.y) * 0.5;
                else if (id == 6 || id == 7)
                    power *= 0.2 + abs(_SinTime.y) * 0.4;
                else if (id == 8 || id == 9)
                    power *= 0.7 - abs(_CosTime.w) * 0.6;

                In[0].pos = (In[0].pos - center) * (power + 1) + center;
                In[1].pos = (In[1].pos - center) * (power + 1) + center;
                In[2].pos = (In[2].pos - center) * (power + 1) + center;

                TriStream.Append(In[0]);
                TriStream.Append(In[1]);
                TriStream.Append(In[2]);

                // 連続したトライアングルを終了
                TriStream.RestartStrip();
            }

            sampler2D _MainTex;

            fixed4 frag(v2f IN) : SV_Target
            {
                fixed4 c = tex2D(_MainTex, IN.texcoord);
                c.rgb = IN.color;
                c.rgb *= c.a;
                return c;
            }
            ENDCG
        }
    }
    Fallback "GUI/Text Shader"
}

解説

ポイントは回転アニメーションと同じなので、補足を追記します。

#pragma vertex vert
#pragma geometry geo
#pragma fragment frag

#pragma vertex vert : バーテックスシェーダの関数にvertを指定
 頂点単位で処理をする
#pragma geometry geo : ジオメトリシェーダの関数にgeoを指定
 プリミティブ単位で処理をする
#pragma fragment frag : フラグメントシェーダの関数にfragを指定
 ピクセル単位で処理をする

#pragma target 3.5

 //省略

Fallback "GUI/Text Shader"

#pragma target 3.5 : 対象にするシェーダーモデルを指定(デフォルト:2.5)
 SV_VertexID:3.5以上必要
 ジオメトリシェーダ:4.0以上必要
Fallback "GUI/Text Shader" : シェーダーモデルが対応していない場合は"GUI/Text Shader"を用いて描画する

他にも色々

demoEx3.gif

demo5.gif

demoEx4.gif

demo6.gif

オリジナルなテキストアニメーションを作成してみよう。

使用例

点数が変化する時にテキストアニメーションを行うことで、得点を稼いでいることを実感させる。

usecase.gif

 android → https://play.google.com/store/apps/details?id=com.secretcommandlab.taptapdrop
  iOS → https://appsto.re/jp/fMfpjb.i

参考資料

30
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
17