サムザップの『呪術廻戦 ファントムパレード(以下、ファンパレ)』開発チームに所属している二宮です。リリースしてから半年が経ち、新コンテンツ「夢幻廻楼」をリリースしました。このコンテンツでは、3Dを使用してライティングを施したリッチなグラフィックを提供しています。また、各階層にいる敵を倒して登っていくと天候が変わるギミックもあり、上層では暗くなって空間に歪みも発生するなどの特徴があります。 この記事では夢幻廻楼の背景の演出システムについて、大きく2点に分けて紹介します。 夢幻廻楼は定期的な階層追加を予定しているコンテンツで、上へと伸びていきます。さらに、上の階層ほど強力な敵が出現するため、強敵の気配をより強く演出するために特定の階層よりも上にいくと天候が変わる機能も必要でした。これらの要件を満たすため、以下の仕組みを作りました。 塔、空、エフェクトを8階層分程度のブロックとして扱い、これを縦に積み上げることで塔を構成するようにしました。クリエイターと相談して、適切に絵が繋がるように各アセットを作ってもらいました。 今後階層が追加された時に、階層が足らなくなったらブロックを上に増やすことで、どこまでも塔を伸ばせるようにしています。
また、階層が伸びたからといって、素直にブロックを積み上げていくとパフォーマンスに影響が出てしまいます。そのため、見える範囲のブロックだけを生成し、生成していないエリアにカメラが移動したら適切にブロックを瞬間移動させることで、ブロックの生成数を抑えて快適にプレイするための工夫も行っています。 天候変化は、ライトマップ、カラーマップ、パーティクル、ポストエフェクトを天候ごとに用意して、テクスチャを切り替えることで行っています。さらに、夢幻廻楼では特定の階層をカメラが映した時に、シームレスに天候を切り替えたくてブレンディングによる切り替えを採用しています。 ライトマップのブレンディングはあまり一般的ではないため、ファンパレで使った手法を紹介します。まず、ライトマップはUnityによってベイクしたものを使用しました。これは、ライトマップの更新作業が楽になることを狙っています。今回開発したカスタムシェーダーを使用して、2枚のライトマップのブレンディングを行います。 強敵の気配を出すため、通常とは異なる雰囲気を出すために、塔の背景を歪ませる効果を追加しました。しかし、「塔の奥の空間が歪んでいる」という状況を作りたいので、手前の塔によって奥の歪みを隠す必要がありました。また、一部の演出にて歪み表現が二重でかかることも考慮し、システムを検討しました。 要件を精査してみたところNovaShaderのDistortion機能がほぼ全ての要件を満たしていそうだったので、部分的に導入を決めました。これによりずいぶん工数が削減されて、絵作りを高速に進めることができました。 しかし、クリエイターにテストを進めてもらっていたところ、NovaShaderの仕組みだけでは手前の塔が周辺のDistortionに巻き込まれるケースが見つかり、これを解決するためにファンパレ用に少し改変したものを使っています。 NovaShaderはDistortion効果によってUVがどれくらいずれるかを別バッファに描画し、この時ZTestを行うことで手前オブジェクト (夢幻廻楼では塔) による遮蔽を考慮します。最後にバッファに書き込んだ情報を元にフレームバッファのRGB値をずらし、Distortion効果を表現します。 UVズレの大きさをバッファに書き込む際のZTestによって塔自体は歪みませんが、塔の周辺画素を書き込む時には歪みが適用されるため、歪みの方向によっては塔の画素を取り込んでしまうことがあり、ファンパレではこれが課題となりました。 ファンパレでは、塔の近くの画素を描画する際に歪みを抑えるための改良を施しました。 改良内容をまとめます。 細かい説明は数値や数式と絡むので、ぼかしたマスク画像から歪みの強度を調整するShaderコードを載せておきます。 ファンパレ「夢幻廻楼」技術の裏側 ~ 天候変化編~ ということで、夢幻廻楼の演出システムについて紹介しました。この記事では、無限に登れる塔の設計や、天候が変化する演出、そして背景のDistortionエフェクトなど、技術的な工夫点や細かいこだわりを詰め込みました。特に、天候変化や背景の歪みといった視覚的な要素は、ゲームの雰囲気を一層引き立てる重要な要素となっています。 このような細部へのこだわりが、新鮮で魅力的な体験につながると考えています。これからも、常にユーザーの皆様の期待を超えるようなコンテンツを提供できるよう取り組んでいきます。 最後まで読んでいただきありがとうございました。この記事が、ゲーム開発に興味を持っている方々の参考になれば幸いです。今後も、ファンパレのさらなる進化にご期待ください。はじめに
無限に登れる塔 x 天候変化 を実現する仕組み
小さい塔を積み上げて無限に登れる塔を作る
天候変化の仕組み
// 元々, SAMPLE_GIで定義されていたメソッドの中身をTextureを外部から指定できる形に変更したメソッド
half3 SampleLightmap(TEXTURE2D(lightMapName), float2 staticLightmapUV)
{
#ifdef LIGHTMAP_ON
#ifdef UNITY_LIGHTMAP_FULL_HDR
bool encodedLightmap = false;
#else
bool encodedLightmap = true;
#endif
half4 decodeInstructions = half4(LIGHTMAP_HDR_MULTIPLIER, LIGHTMAP_HDR_EXPONENT, 0.0h, 0.0h);
half4 transformCoords = half4(1, 1, 0, 0);
return SampleSingleLightmap(
TEXTURE2D_LIGHTMAP_ARGS(lightMapName, sampler_LinearClamp), LIGHTMAP_SAMPLE_EXTRA_ARGS,
transformCoords, encodedLightmap, decodeInstructions);
#else
return half3(1,1,1);
#endif
}
// FirstLightmap, SecondLightmapをブレンディング
const half3 bakedGI1 = min(2, SampleLightmap(_FirstLightmap, input.lightmapUV));
const half3 bakedGI2 = min(2, SampleLightmap(_SecondLightmap, input.lightmapUV));
const half3 bakedGI = lerp(bakedGI1, bakedGI2, _LightmapWeight);
塔の背景を歪ませる表現
NovaShaderのDistortionの仕組みとファンパレにおける課題
解決策:手前背景の周辺画素の歪みを弱くする
// _DistortionWeightBuffer: 塔のシルエットをぼかしたマスク画像 (ぼかす前は塔の部分が0, 他が1)
half distortionWeight = SAMPLE_TEXTURE2D(_DistortionWeightBuffer, sampler_ScreenSpaceUvTexture, uv).r;
// 0(塔のエリア) , 1(塔以外のエリア)の2値画像をぼかしたもの. 値域は0 ~ 1. ただし境界は0.5になっているはず
// ぼかす前の塔が存在したエリアは、ぼかした後の値は0 ~ 0.5になる
// 今回 歪みを弱めたいエリアは塔の近くで、塔ではない部分なので, 0.5 < distortionWeight < 1のエリア.
// distortionWeight: 0.5 ~ 1のエリアを alpha: 0 ~ 1でブレンディングするために, 0.5 ~ 1.0のエリアを 0 ~ 1に変換. 1以上は1にする
distortionWeight = saturate(distortionWeight * 2.0 - 1.0);
// 手前オブジェクト周辺の歪みを弱めるため, distortionによるuvのずれ幅(dist) に対して乗算
dist *= distortionWeight;
おわりに
©芥見下々/集英社・呪術廻戦製作委員会 ©Sumzap, Inc./TOHO CO., LTD.