ゲームキッチンが「狂気の石」を作るための3つの技術的課題

今年の初めに、The Game Kitchenは『Stone of Madness』を発表しました。これは、プレイヤーが5人の囚人が尋問のための刑務所から脱出するのを助ける戦術的RPGです。このゲスト投稿では、スタジオの3人の開発者が、開発中に直面したレンダリング、UI、およびテストの課題にどのように取り組んだかを共有します。
私たちはThe Game Kitchenで、最近PCとコンソール向けにThe Stone of Madnessをリリースしました。私たちは、最新プロジェクトの開発中に直面した最も緊急の課題のいくつかを、技術的な視点から実践的な例を交えて共有したいと考えています。この共同記事では、私たちのプログラミングチームがUnityで実装した主要なソリューションを分解し、パフォーマンスと開発効率の両方を最適化します。
まず、アドリアン・デ・ラ・トーレ(グラフィックスプログラマー)が、ゲームのアートパイプラインをどのように設計し、レンダリングして独特のビジュアルスタイルを実現したかを説明します。
次に、アルベルト・マルティン(UIプログラマー)が、ユーザーフィードバックに基づいてUXの改善を行い、UI開発を効率化するためにNoesisをどのように活用したかを詳しく説明します。
最後に、ラウル・マルトン(ゲームプレイプログラマー)が、サーバー上での複雑なゲーム内アクションのテストをどのように外部化し、自動化したかを紹介します。これにより、統合を妨げることなく、複数のコーナーケースが処理されることが保証されました。
狂気を美しく見せるカスタムレンダーパイプラインの概要
アドリアン・デ・ラ・トーレ、グラフィックスプログラマー、ザ・ゲームキッチン
狂気の石は2Dのビジュアルと3Dのゲームプレイメカニクスを組み合わせており、独自の技術的な課題を提示します。プレイヤーは2Dの世界を見ていますが、ゲームの基盤となるシステムは三次元空間で動作しており、そのデザインに独特の二重性を生み出しています。
この課題に対処するために、私たちの開発チームは、3Dゲームプレイ情報と2Dビジュアル表現のギャップを効果的に埋めるカスタムレンダリングパイプラインを作成しました。このソリューションは、視覚的一貫性を維持しながら意図されたゲームプレイの深さを保持するために、複数のレンダリングパスと専門的な技術を実装しており、3D要素をゲームの独特な2Dアートスタイルにシームレスに変換できるようにしています。
狂気の石では、フレームの描画に寄与する2つの主要なシナリオがあります。
最初のシナリオ、私たちがプロキシシナリオと呼ぶものは、最終フレームの照明を計算する幾何学的プリミティブで構成されています。

第二のシナリオはキャンバスシナリオで、プロキシジオメトリの形状と位置に一致するスプライトで構成されています。キャンバスは、3D空間をシミュレートし、動くゲーム要素との適切なZソートを達成するために、レイヤーで配置されています。
以下のセクションでは、フレームレンダリングのためのグラフィックスパイプラインの各ステップについて詳しく説明します。
1.視野
視野またはゲーム能力のコーンが有効になると、パイプラインの最初のステップが開始されます。NPCの視点(PoV)にカメラを配置し、その視野(FoV)内のプロキシの深さをレンダリングします。

次に、別のレンダー テクスチャで、カメラはプレイヤーの起点からの距離のグラデーションを B チャンネルに出力します。これはスキルエリア効果に使用されます。

NPCの視点レンダーテクスチャを使用して、視野のコーンカメラは、障害物と距離に関する情報を持つコーンを前のテクスチャのRおよびGチャネルにレンダリングします。

最終パスはアルファチャンネルに音波をレンダリングします。

このステップで作成された最終的なテクスチャであり、シーンのスプライトをレンダリングするためにCanvas Cameraステップで使用されます。

2.キャンバスレンダーIDカメラ
私たちのプロジェクトの各プロキシには、関連するレンダーID(浮動小数点値)が付いています。プロキシとその関連スプライトは、同じレンダーIDを共有しています。このステップでは、Render IDのfloat値をレンダーテクスチャにレンダリングします。

次のステップでは、このテクスチャを使用して、プロキシシナリオで計算された照明情報をキャンバスシナリオのスプライトと一致させます。
3.照明
私たちのゲームの照明は次のようになります:
- 焼き付け照明常にアクティブな自然光、例えば外部照明のような
- 混合照明シーン内でオンオフできる静的な光、例えばキャンドルなど
- リアルタイム照明シーン全体を移動し、オンとオフを切り替えられる光(これをアルフレドのオイルランプの一例で実装しました)
RenderIDテクスチャを使用して、プロキシシーンからの照明情報を含むレンダーテクスチャを作成します。

4.キャンバスカメラ
すべてのレンダー テクスチャを作成した後、カメラはライティング、スキルの効果範囲、視野の円錐、ノイズ波に関する情報を使用してスプライトのレンダリングを開始します。
5.ポストプロセッシング
カラーグレーディング、ビネット、その他の効果は、ポストプロセッシングパスで適用されます。
6.UI
最後に、UIがオーバーレイされます。
HUDの狂気UIプロセスの高速化
アルベルト・マルティン、UIプログラマー、ザ・ゲームキッチン
狂気の石の最終リリース版は、50以上のユーザーインターフェースを備えています。その数字の背後にある理由は、このゲームがユーザーに表示するための多くのデータを持っているからです。私たちのUI作業は非常に時間がかかりました。特に、最初のチームが小さかったため、私たちはプロセスを継続的に最適化し、できるだけ短時間で良い結果を達成できるようにしていました。
私たちのUI作業はプロジェクト全体にわたったため、私たちのUI/UXデザイナーが実装する必要のあるすべての機能を明確に理解することが重要でした。私たちのゲームが良いユーザー体験を提供し、楽しくプレイできるようにするために、プログラミングチームとデザインチームの間でオープンなコミュニケーションを保つことに注意しました。
すべてのUIコンポーネントの最良のバージョンを作成するために、私たちは技術チームとクリエイティブ/リサーチチームの間のサイロを取り除く必要がありました。そうすることで、全員がゲームの開発に積極的に関与できるようになりました。この二部構成のワークフローにどのように取り組んだかを説明します。
UIデザインにおけるリサーチとクリエイティブの役割
私たちのUI/UXデザイナーは、最終的なゲームでUI要素がどのように見えるかを定義し、満足のいくユーザーエクスペリエンスを提供することを保証します。これを念頭に置いて、彼らは各要素を最小限の技術的負荷で作成し、潜在的なユーザーと共にそれを検証し始めました。そのプロセスは次のように見えました:
- 必要条件プレイヤーのニーズを理解し、ゲームのニーズとユーザーの目標のリストを作成する
- 調査似たような問題にどのように対処したかを見るために他のゲームを見ています
- ワイヤーフレームスキーマと構造に取り組んでいます(この時点では最終アートはありません)
- モックアップこの時点で、以前に作成した要素(ボタン、スクロール、フレームなど)を使用して、ほぼ完全に設計されたインターフェースをマウントし、あまり労力をかけずに反復作業を行うことができます。
- プロトタイプ私たちは、モックアップを使用してFigmaでプロトタイプを構築し、ゲームパッドやキーボード/マウスとのインタラクションをシミュレートして、実際の環境でどのように機能するかを示します。
- ユーザーテスト私たちが以前に作成したプロトタイプを使用して、ユーザーテストを開始し、ステップ1で特定したニーズと目標を検証します。
- イテレーションフェーズユーザーテストが期待に応えた場合、それは技術的なプロセスに引き継がれ、さらに反復を行ったり、便利であれば追加のテストを実施したりします。
技術的なUI実装
以前にも述べたように、『狂気の石』のUI要素の数は膨大です。UIエンジンの開発は高額であるため、私たちは学びやすく、適切なツールとワークフローを備えたフレームワークを使用する必要がありました。ミドルウェアの範囲を評価した結果、モデル-ビュー-ビューモデル(MVVM)パターンに従ったNoesis GUIを選択しました。
私たちはNoesisを選びました。なぜなら、それはWPF(Windows Presentation Framework)に基づいており、MVVMモデルに従っているため、ほとんどのドキュメント、参考文献、フォーラムのエントリなどを再利用して、大部分の問題をトラブルシューティングできるからです。このフレームワークはしばらくの間存在しており、最初のリリースから18年が経過しています。多くのUI開発者にとって馴染みのあるものであり、私たちのスタジオはプロジェクトのインターフェースやツールを実装するために、比較的大きな人材プールから採用するオプションを持っています。Noesisについてのもう一つの重要な点は、WPFの同じツールを使用できるということです。
XAMLを使用して、私たちのUIクリエイティブチームは、レイアウト作業に関与し、最小限の技術的関与で全ての要素を磨き上げました。MVVMアプローチのおかげで、私たちの技術的なUIプログラマーは機能に集中し、必要に応じて特定の分野でクリエイティブチームをサポートすることができました。
テスト(または、システムデザインでゲームを作る際にどうやって狂わないか)
ラウル・マルトン、ゲームプレイプログラマー、テクスタジオ
『狂気の石』のゲームプレイは、3つの基本的な柱に基づいています:プレイヤースキル、NPC AI、シーンインタラクション。これらの3つのシステムは基本的に相互に絡み合っており、プレイヤーが制御する必要がある状況の数が指数関数的に増加します。そして、私たちがテストする必要があるシナリオの数も増加します。
プロジェクトを始めるとすぐに、従来のQAシステムでは不十分であることに気付きました。特定の方法で相互に作用するいくつかの要素に依存するシナリオがあまりにも多く、制御されていない状況を生み出していました。さらに、これらの状況は、QAチームが快適にテストするにはあまりにも短い時間のウィンドウで発生する可能性があります。
これらの問題を解決するために、私たちは自動テストのスイートを作成しました。私たちの開発チームが特定のシステムに関連して直面する可能性のあるすべてのシナリオ/状況を考慮し、シミュレーションされたゲーム環境でより効率的に自動テストできるというアイデアでした。
例を挙げると、『狂気の石』の主要キャラクターの一人、アメリア・エクスポジトは、スリの能力を持っています。このスキルを実装する際、私たちは次のことを確実にするために一連のテストを開始しました:
- スキルの基本的な機能は正しかった。NPCから盗むと、スリのミニゲームが開き、ゲームは終了するまで一時停止します。
- あまり一般的でない状況もカバーされています:NPCから盗もうとすると、別のNPC(警備員など)があなたを見ている場合や、NPCが走っている場合、その行動は不可能です。

統合テストの作成
私たちが作成した各統合テストは、以下の要件に基づくセットアップを必要としました:
1.この特定の状況を作り出すために特別に用意されたシーン
スリのスキルをテストするために、2人の警備員と1人のプレイヤーのシーンを作成しました。各キャラクターを、状況を正確にテストするために必要な方向を向くように配置しました(プレイヤーは、ガードの視野内にいる場合、スリを使用できないことを忘れないでください)。
さらに、シーンにはシナリオをテストするために必要な最小限の要素のみを含めるべきです。余分な要素は測定にノイズを加える可能性があります。これが私たちの例のシーンにHUD、手動入力システム、効果音などがない理由です。
- このステップでは、ゲームの構造が適切に区分けされている必要があります。これは少し努力が必要ですが、一度達成すれば、それだけの価値があります!😉
2.テストされる状況を強制することができるテストコード
手動で作成するのが難しく、時間がかかるテストが必要な状況の多くは、開始するためにコードのプッシュが必要です。
例えば、NPCが移動していない限り、決してネズミ捕りの上を踏まないことを確認するためのテストシナリオを作成したい場合、指示の連鎖は次のようになります:
- シーンを開始する
- 1秒待ってください
- NPCの下にネズミ捕りを設置する
- もう一秒待って
- NPCに任意の方向に歩き始めるよう指示する
このプロジェクトのこの部分は、開発中の変更に非常に敏感です(ゲーム仕様の変更やさまざまな予期しないシナリオなどの要因に依存します)。したがって、テストコードとその結果のフィードバックはできるだけ明確であることが重要です。
何が実際にうまくいっていないのかについて明確な情報を提供せずに失敗するテストほど悪いものはありません。
3.シナリオが意図した通りに機能しているか、テストが論理のエラーを検出したかを確認する信頼できる方法
自動テストには依然として監視が必要です。テストの特異性が高まるにつれて、何がテストされているのかを監視することが難しくなる場合があります。また、シナリオが統計的に有意になるまで十分にテストされないこともあります。これらの問題を回避するために、私たちはカスタムツールを作成しました。
例えば、私たちのテストのいくつかは、シーン内のいくつかのNPC間の組み合わせた相互作用を含んでいました。これらのケースを適切に監視するために、テスト中にNPCが循環するさまざまなAI状態を記録するシステムを作成しました。

私たちは、現在のゲーム状態(NPCが気絶しているかどうか)を可視化する良いAPIも必要でした。NPCはルーティング状態に入っていますか?何回ですか?どのプレイヤーキャラクターが捕まったのですか?そしてそのように。
4.すべてのテストを迅速に実行できるシステム
ユニットテストとは異なり、自動テストはゲームがリアルタイムで実行されている状態で行う必要があります。これにより、これらのテストを実行するのが非常に遅くなる可能性があります。
このような状況では、私たちのゲームがUnityの標準アップデートシステムを使用していないという事実を利用することができます。代わりに、私たちのすべてのコンポーネントはTick()関数を使用しており、これはUnityの更新をシミュレートしますが、私たちのゲームエンジンによって制御された方法で起動されます。
これにより、私たちはテストでいくつかの異なる目標を達成することができました。
- まず、ゲームの各フレームに対して複数のコードフレームを実行する強制関数を使用することで、実行を加速できるかもしれません。
- 第二に、これらのテストはリアルタイムで実施されるため、テストシナリオを実行しているコンピュータのフレームレートによって引き起こされる変動に非常に影響を受けやすいです。制御されたフレームレートに変換することで、この変動を回避します。もしあるテストが1台のマシンで合格すれば、すべてのマシンで合格し、逆もまた然りです。
そして、これが結果です。
安全なテストがどのようにして壊れたビルドを回避するのに役立つか
このテストスイートの作成に伴い、バグが含まれている場合にブランチのマージを自動的に中断するセーフガードを実装する必要がありました。これを確実にするために、メインプロジェクトブランチに変更がコミットされるたびに起動する自動マージスクリプトを作成します。
このスクリプトは、これらすべてのテストを起動し、その結果を監視することを確認します。テストが失敗した場合、エラー検出を返し、マージを中断します。
このシステムを使用することで、表面的には孤立しているシステムの変更が相互作用する他のメカニズムを壊すような状況を回避できます。
この「狂気の石」の開発の舞台裏を共有してくれたThe Game Kitchenに感謝します。私たちのSteamキュレーターページで、Made With Unityのゲームをさらに探索し、Unityのリソースページで開発者の洞察を得てください。



