年末にダウンロード版で買った ペルソナ5 をいまさらずっとやってて、このゲームはUI表現の宝庫ですね。勉強になります。
おもしろい表現たくさんあるのですが、キャラクターのセリフが乗ってるフキダシ枠の端っこが動くのがおもしろかったので、マネしてみました。
最終的なシェーダーコード
開幕からシェーダーのコードをぶっぱなします。これは2ラウンド目開始直後にザンギエフ *1 でクリティカルアーツ *2 を空ぶる *3 に等しい所業です。
ちなみにスト5 *4 も買いました。安かったからね。
// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt) Shader "Custom/Wobble" { 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 // 各頂点をどこまで動かすか _MovePosition00 ("Move Position 00", Vector) = (0, 0, 0, 0) _MovePosition01 ("Move Position 01", Vector) = (0, 0, 0, 0) _MovePosition10 ("Move Position 10", Vector) = (0, 0, 0, 0) _MovePosition11 ("Move Position 11", Vector) = (0, 0, 0, 0) _Scale ("Scale", Float) = 10 _CycleSec ("Cycle Sec", Float) = 1 } 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 { Name "Default" CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 2.0 #include "UnityCG.cginc" #include "UnityUI.cginc" #pragma multi_compile __ UNITY_UI_CLIP_RECT #pragma multi_compile __ UNITY_UI_ALPHACLIP struct appdata_t { float4 vertex : POSITION; float4 color : COLOR; float2 texcoord : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct v2f { float4 vertex : SV_POSITION; fixed4 color : COLOR; float2 texcoord : TEXCOORD0; float4 worldPosition : TEXCOORD1; UNITY_VERTEX_OUTPUT_STEREO }; sampler2D _MainTex; fixed4 _Color; fixed4 _TextureSampleAdd; float4 _ClipRect; float4 _MainTex_ST; // propertiesを受ける変数 fixed4 _MovePosition00; fixed4 _MovePosition01; fixed4 _MovePosition10; fixed4 _MovePosition11; half _Scale; half _CycleSec; v2f vert(appdata_t v) { v2f OUT; UNITY_SETUP_INSTANCE_ID(v); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); // texcoordとTimeから膨らませる位置を決定 half cycle = abs(sin(((_Time / _CycleSec) / _CycleSec) * 360)); float4 target = (_MovePosition00 * (saturate(1 - v.texcoord.x) * saturate(1 - v.texcoord.y))) + (_MovePosition01 * (saturate(1 - v.texcoord.x) * saturate(v.texcoord.y))) + (_MovePosition10 * (saturate(v.texcoord.x) * saturate(1 - v.texcoord.y))) + (_MovePosition11 * (saturate(v.texcoord.x) * saturate(v.texcoord.y))); v.vertex.xy = v.vertex.xy + (target * cycle * _Scale).xy; OUT.worldPosition = v.vertex; OUT.vertex = UnityObjectToClipPos(OUT.worldPosition); OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex); OUT.color = v.color * _Color; return OUT; } fixed4 frag(v2f IN) : SV_Target { half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color; #ifdef UNITY_UI_CLIP_RECT color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect); #endif #ifdef UNITY_UI_ALPHACLIP clip (color.a - 0.001); #endif return color; } ENDCG } } }
元コードがあるのでライセンス表示
Copyright (c) 2016 Unity Technologies Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
前準備
順番前後しますが、前準備を説明します。 (上のコードを全部コピペすれば動くので、厚顔無恥な人は一番最後のマテリアル設定まで飛んでも問題ありません)
上のコードは全部一から書いたわけではなく、Unityの公式サイトからダウンロードしたuGUI用デフォルトシェーダーをカスタマイズしています。
下の公式サイトから ビルトインシェーダー をダウンロードしましょう。
解凍して DefaultResourcesExtra/UI/UI-Default.shader を自分のプロジェクトにコピーし、適当に名前を変えます。
変更した点は以下の三点。
properties の追加
Properties { ... // 各頂点をどこまで動かすか _MovePosition00 ("Move Position 00", Vector) = (0, 0, 0, 0) _MovePosition01 ("Move Position 01", Vector) = (0, 0, 0, 0) _MovePosition10 ("Move Position 10", Vector) = (0, 0, 0, 0) _MovePosition11 ("Move Position 11", Vector) = (0, 0, 0, 0) _Scale ("Scale", Float) = 10 _CycleSec ("Cycle Sec", Float) = 1
MovePositionXX は、各四隅の点をどのくらい膨らませるかの数値です。0, 1 の値はそれぞれ texcoord の値に対応しています。
Scale は数値の係数です。RectTransformのスケール感に合わせる必要があるのでややこしいんですよね。
CycleSec はわきわきを何秒ごとに繰り返すかをあらわす周期です。
変数定義
// propertiesを受ける変数 fixed4 _MovePosition00; fixed4 _MovePosition01; fixed4 _MovePosition10; fixed4 _MovePosition11; half _Scale; half _CycleSec;
頂点シェーダー
v2f vert(appdata_t v) { v2f OUT; UNITY_SETUP_INSTANCE_ID(v); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); // texcoordとTimeから膨らませる位置を決定 half cycle = abs(sin(((_Time / _CycleSec) / _CycleSec) * 360)); float4 target = (_MovePosition00 * (saturate(1 - v.texcoord.x) * saturate(1 - v.texcoord.y))) + (_MovePosition01 * (saturate(1 - v.texcoord.x) * saturate(v.texcoord.y))) + (_MovePosition10 * (saturate(v.texcoord.x) * saturate(1 - v.texcoord.y))) + (_MovePosition11 * (saturate(v.texcoord.x) * saturate(v.texcoord.y))); v.vertex.xy = v.vertex.xy + (target * cycle * _Scale).xy; OUT.worldPosition = v.vertex; ...
「もっとやりかたあったやろ」と言われそうですが、とりあえず動いたのでよしとしましょう。
重要なのは下で
float4 target = (_MovePosition00 * (saturate(1 - v.texcoord.x) * saturate(1 - v.texcoord.y))) + (_MovePosition01 * (saturate(1 - v.texcoord.x) * saturate(v.texcoord.y))) + (_MovePosition10 * (saturate(v.texcoord.x) * saturate(1 - v.texcoord.y))) + (_MovePosition11 * (saturate(v.texcoord.x) * saturate(v.texcoord.y)));
入力点を四隅のどれか一つと判定して、プロパティで指定した方向にふくらませます。
マテリアルの設定
シェーダーが問題なくコンパイルできたら、マテリアルに設定します。
変数を微調整してあそびましょ。
ロゴに使ってみる
こんな感じにしてみました
*1:ストリートファイターシリーズのキャラクター。投げ技が得意
*2:いわゆる超必殺技。クリティカルアーツはストリートファイター5シリーズでの名前で、ゲージを全て消費して発動するすごく強い技
*3:相手に当たっていないのでゲージ全損になる、いわゆる"やっちゃった"行動
*4:ストリートファイター5の略称。最近の格ゲーに多い有料DLCキャラクターまとめてシーズンとして売り出す方法をとっているが、これも時流ってやつですかねえ