サムザップで『呪術廻戦 ファントムパレード』の主にアウトゲームの開発・マネージメントやグラフィック関連の開発をしています、クライアントエンジニアの木田です。 弊社が提供する『呪術廻戦 ファントムパレード(以下、ファンパレ)』は2023年11月21日にリリース。 メモリークエストは、クエストを進めてピースを獲得し、全てのピースを集める事でイベント限定報酬を獲得出来るものになります。 本記事では、メモリークエストのピース獲得時演出などで使用されている技術的な部分を一部紹介しようと思います。 メモリークエストのピース獲得時演出などで使用されている技術的な部分を紹介する前に、最終的にリリースされた演出について触れておきます。 このように、ピースを白く飛ばすような演出となっており、ピースの形は異なります。 そのピースを分割する際、回想残滓によってキャラの顔の位置が異なり、キャラの顔を分割しないようにする為には、分割の仕方が回想残滓ごとで変わります。 そこで最初に検討した実装方法は、 しかし、回想残滓の画像分割、Timeline制御を個別で対応するとなると、大量のリソース制作が必要となり、このイベントを開催する度にかかる運用コストが上がります。 そこで、なるべく制作するリソースを減らす形で実現したかった為、専用のShaderを用意しました。 実際に使用されている専用Shaderについて説明します。 マテリアルの設定パラメータは以下になります。 ShaderGUIで補完している部分や結果的にアニメーション制御で変更する必要の無いパラメータは見えないようにしています。 細かい処理の流れについての説明は、後述します。 実際のShaderパラメータは、画像のようになります。 CustomShaderを切って、ShaderGUIを通さない場合のパラメータが上の画像になります。 処理の流れは、以下のようになります。
また、画像は分かりやすいように、2ピース目の解放時にしています。 必要なテクスチャをサンプリングします。 コントロールテクスチャのRチャネルがトランジションを制御するものになっています。 また、トランジションのマスク計算をメソッド化して利用しています。 トランジションマスクの計算を利用して、対象ピースのマスクを計算します。 対象ピースのマスクを使って、ディゾルブの計算を行います。 ディゾルブ込みで、対象ピースのカラー計算を行います。 境界線部分も考慮したマスク計算で、解放済みと未解放のピースでカラーの出し分けを行います。 その結果が図のようになります。 演出のこだわりと運用する上での制作コストを天秤に掛けた時に、オミットした処理は以下になります。 ピース解放前のベーステクスチャを用意しなくても済むように、回想残滓のテクスチャから下記のように対応していました。 こうする事で、必須で制作するテクスチャに対して処理を行うため、ピース解放前のベーステクスチャは用意しなくて済みます。
しかし、ピース解放前の状態での色味にこだわりたいとの事で、オミットしました。 自身含め、『呪術廻戦』のいちファンとして、こだわりの強いメンバーがコミュニケーションしながら、運用・開発を行なっています。 演出をこだわりつつ、運用コストを減らせるような仕組み作りをこれからも意識して、運用・開発に携わっていきたいなと思います。
©芥見下々/集英社・呪術廻戦製作委員会 ©Sumzap, Inc./TOHO CO., LTD.はじめに
この度、半周年を迎え、TVアニメ第2期のストーリーを追体験できる新イベントとしてメモリークエスト「呪術廻戦 懐玉・玉折」をリリースしました。概要
また、追加アップデートする度にイベント限定報酬として、別の回想残滓が分割されたピースを埋めていく形になります。
実装したもの
マテリアルパラメータ
例えば、
Shaderパラメータ
処理の流れ
1.必要なテクスチャをサンプリング
この時、ImageのSourceに入っているテクスチャとカラー設定の計算も行います。// Mainテクスチャ
const half4 mainTexture = tex2D(_MainTex, IN.texcoord);
half4 mainColor = mainTexture * IN.color;
// Baseテクスチャ
const half4 baseTexture = tex2D(_SubTex, IN.texcoord);
// コントロールテクスチャ
half4 controlTex = tex2D(_ControlTex, IN.texcoord);
// ディゾルブテクスチャ
half4 dissolveTex = tex2D(_DissolveTex, IN.texcoord);
2.トランジションマスクの計算
これを用いて、トランジションマスクの計算を行います。// トランジションマスク
const float transitionMask = saturate(GetTransitionMask(controlTex.r, mainTexture.a, _TransitionSmoothness, 1 - _TransitionThreshold));
float GetTransitionMask(float transitionMaskValue, float colorAlpha, float smoothness, float threshold)
{
return saturate(lerp(0, colorAlpha, smoothstep(0,smoothness,clamp(lerp(0, 1 + smoothness, threshold) - transitionMaskValue, 0, smoothness))));
}
3.対象ピースのマスクを計算
この時、コントロールテクスチャのGチャネルが境界線のマスクになっているので、減算して境界線には掛からないようにします。// Piece
half4 pieceMask = saturate(GetTransitionMask(controlTex.r, mainTexture.a, _TransitionSmoothness, 1 - _PieceMaskThreshold) - transitionMask - controlTex.g);
4.ディゾルブの計算
#define USE_DISSOLVE _DissolveThreshold != 0
float pieceMainMask = pieceMask;
if(USE_DISSOLVE)
{
const half dissolveValue = _DissolveThreshold < dissolveTex.r ? 1 : 0;
pieceMainMask = pieceMask * dissolveValue;
}
5.対象ピースのカラー計算
fixed4 pieceColor = saturate(mainTexture * pieceMainMask + saturate(baseTexture) * (pieceMask - pieceMainMask));
5.残りのカラー計算
// エッジマスク
const float edgeMask = saturate(saturate(controlTex.g - (1 - _EdgeMaskThreshold))+ transitionMask);
#define USE_PIECE_MASK pieceMask.a != 0
if(USE_PIECE_MASK)
{
mainColor.rgb = lerp(mainColor.rgb, pieceColor.rgb, pieceMask);
}
half4 color = baseTexture * edgeMask + mainColor * (1 - edgeMask);
#ifdef UNITY_UI_CLIP_RECT
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) * IN.mask.zw);
color.a *= m.x * m.y;
#endif
#ifdef UNITY_UI_ALPHACLIP
clip (color.a - 0.001);
#endif
color.a *= IN.color.a;
color.rgb *= color.a;
return color;
こだわりとコストを天秤に掛けてオミットしたもの
ピース解放前のベーステクスチャをShader制御
最後に
その中でやはり、演出のこだわりと運用する上でのコストは、天秤に掛ける場面がよくあり、その落とし所を模索しています。