シリーズ: AI とともにコードを学ぶ

デモが静かにクラッシュしたとき

エンジンに静かに失敗することを拒否させる。

デモが静かにクラッシュしたとき 完成しているように見える。何もしない。何も語らない。 INFO シーン読み込み完了 INFO プレファブフォールバック... INFO ...他298行 INFOレベルに埋もれる 誰も気づかない 昇格 ERROR プレースホルダーモード: ゲームプレイのプレファブが欠落、 デモは機能しません どんなログフィルタも生き延びる grepで即座に見つかる
静かな劣化は欠陥だ。フォールバックは叫ばなければならない。

クラッシュは何かが間違っていることを教えてくれる。静かな失敗は、洗練された空っぽの殻をあなたのユーザーに——そしてパートナーに——出荷してしまう。信頼を勝ち取るエンジンは、ささやくことを拒否するエンジンだ。

クラッシュより悪いクラスのバグがある。クラッシュは少なくとも何かが間違っていることを教えてくれる。今週の土曜の朝に私が向き合ったのは、もう一方の種類だった。デモは起動した。シーンは読み込まれた。UIはレンダリングされた。ポーズメニューは反応しなかった。ゲームプレイはプレースホルダーだった。エラーなし。例外なし。INFO以上のログ行なし。エンジンは静かに失敗し、動いているゲームの動作を礼儀正しく続けていた。

これは、エンジンにそうすることを拒否させる方法についての投稿だ。

ユーザーにとってデモがどう見えたか

きれいに起動した。スプラッシュ画面。メニュー画面。「Play」をクリック。シーン遷移。きれいに見えるシーン。真ん中に船。右上にスコア表示。

船は動かなかった。入力は反応しなかった。呼び出されたポーズメニューは、視覚的には存在するがクリックに反応しなかった。スコアはゼロを表示し、ゼロのままだった。撃つべき敵はいなかった。世界は礼儀正しく、空っぽで、動いているように見える殻だった。

これを読む開発者にとって、結論は三十秒以内に明らかだ。これはプレースホルダーだ。実際のゲームプレイ・プレファブが欠落している。エンジンは劣化モードにフォールバックし、誰にもそれを伝えるのを忘れた。

ユーザーにとって、これは最悪の種類の失敗だ。アプリケーションは壊れているようには見えない。完成しているように見え、そして悪い。

実際に何が間違っていたか

二つの異なる問題、それぞれが微妙だった。

シーンローダーが静かに劣化していた。 SceneContentLoaderはゲームプレイ・プレファブを見つけ、それをアクティブなシーンにインスタンス化する責任を持つ。プレファブが(ビルドの問題、アセットパックの欠落、設定の不一致により)欠落している場合、プレースホルダー設定へとフォールバックする。フォールバックは明確なエラーをログに記録するはずだった。実際にはINFOレベルでログに記録されていた。エラーメッセージは、他の三百行のINFOと並んでフィルタされていないログストリームに埋もれていた。デモを実行しログをざっと見た誰もが、何も気づかなかった。

ポーズメニューのキャンバスにGraphicRaycasterが欠落していた。 これはUnity側の話だ。GraphicRaycasterコンポーネントを持たないキャンバスは、ポインターイベントを受け取れない。ポーズメニューは正しくインスタンス化され、正しくレンダリングされていたが、ユーザー入力に対しては完全に無反応だった。このコンポーネントの欠落は、複数のキャンバス設定を一つに統合したリファクタリングの中で紛れ込んでいた。その統合が、一つのキャンバスからレイキャスターを取り落としていた。

両方のバグは同じ味わいを持っていた。何かが静かに間違った方向に進んだ。コードパスは続行した。ユーザーは動いているように見えるが実際にはそうでない何かを見た。

どうやって見つけたか

監査は修正より時間がかかった。修正は土曜の午後で済んだ。監査は午前中いっぱいかかった。書き留めておきたいパターンがある。なぜならそれが繰り返されるからだ。

最初のヒントは、スコアがゼロに固定されていることだった。先週末のバグ(スコアの二重カウント)が過剰修正されたのだと思った。違った。スコアがゼロなのは敵がいなかったからだ。敵がいなかったのは、ゲームプレイ・プレファブが読み込まれていなかったからだ。プレファブが読み込まれていなかったのは、シーンコンテンツローダーがプレースホルダーへフォールバックし、それをERRORレベルではなくINFOレベルで報告していたからだ。

それが分かってしまえば、二つ目のバグは明らかになった。ポーズメニューが無反応であることは、同じデモの中で同じ土曜のセッションによって浮上した別の問題だった。監査のパターンは「一つの静かな失敗を見つけたら、隣接するものを探せ」だ。

何を修正したか

小さいが正確な一連の変更。

プレースホルダー警告をERRORに昇格。 シーンコンテンツローダーが実際のゲームプレイ・プレファブを見つけられずプレースホルダーモードにフォールバックするとき、今はERRORレベルで「PLACEHOLDER MODE: gameplay prefabs missing, demo will not function correctly」というメッセージをログに記録する。ERRORの重大度は、あらゆる合理的なログフィルタリングを生き延びさせる。正確な文字列「PLACEHOLDER MODE」は、ログをgrepしたとき見紛いようがない。

EnsureCrossPlatformInputManagerステップの追加。 プレースホルダーモードであっても、入力システムは動作すべきだ。開発者が(フルアセットパックなしでUIに取り組んでいるため)プレースホルダーモードでデモをテストしている場合、メニューを操作できる必要がある。ブートストラップは今、プレースホルダーであっても、あらゆるシーンでCrossPlatformInputManagerシングルトンが存在することを保証する。

必要なキャンバスにGraphicRaycasterを自動追加。 防御的な修正は、キャンバス生成ヘルパーがGraphicRaycasterコンポーネントの存在を確認し、欠落していれば追加するようにすることだ。これは同じ形の将来のバグを覆い隠すものではないが、この特定の失敗モードを起こりえないものにする。

AutoBootstrapでの詳細な起動ログ。 デモが起動すると、ログは今、どのシーンが読み込まれたか、どのモード(実際かプレースホルダーか)で読み込まれたか、どのサブシステムが存在するかの短い要約を出力する。ログを読む開発者は、「デモは期待したモードで起動したか」という質問に十秒以内で答えられる。今朝より前は、その答えには三百行のログを読んで推測する必要があった。

キャンバスヘルパーのユニットテスト。 バグがコンポーネントの欠落だったので、正しい種類のテストは、ヘルパー実行後にコンポーネントが存在することをアサートするものだ。そのテストは今スイートに入っている。誰かがキャンバスヘルパーをリファクタリングして再びレイキャスターを落としてしまったら、テストは大声で失敗する。

これが何に一般化するか

今朝から得た三つのパターン。

静かな失敗は最悪の失敗モードだ。 あなたのコードが劣化モードにフォールバックするあらゆる場所で、フォールバックは叫ばなければならない。INFOではなく。ERRORで。grepが見つける文字列とともに。ユーザーがフォールバックが発動したかどうか分からないなら、三日後にログを読む開発者にも分からない。

設定駆動システムにおけるコンポーネントの欠落にはテストが必要だ。 Unity、Unreal、シーンがコンポーネントで構成されるあらゆるエンジン:設定は静かにドリフトし得る。防御は「タイプXのシーンはコンポーネントA、B、Cを持つ」とアサートする小さな一連のテストだ。地味なテスト。重要なテスト。書く価値がある。

隣接領域を監査せよ。 静かな失敗を見つけたら、すべての隣接システムを見よ。バグは集まる。レイキャスターを落とした同じリファクタリングが、他のキャンバスの他のコンポーネントを落としているかもしれない。プレースホルダー警告を埋もれさせた同じロギング規律の緩みが、他の警告も埋もれさせているかもしれない。最初の発見だけでなく、その周辺を調査せよ。

パートナーとビルダーがここから学ぶべきこと

エンジンが複数の劣化モード(アセット欠落、ネットワーク断、ベンダーSDK利用不可)を持つものを構築しているなら、エンジンはどのモードにあるかを毎回大声で伝えなければならない。静かな劣化は欠陥だ。

パートナーシップのためにエンジンを評価しているなら、チームに静かな失敗をどう扱っているか尋ねてほしい。正しい答えは「grepで見つかる文字列とともにERRORレベルで表面化させ、それを見つける監査を持っている」だ。間違った答えは「その問題はまだ見たことがない」だ。

エージェント駆動のワークフローを実行していて、エージェントがリファクタリングを行っているなら、そのリファクタリングは時折、誤ってコンポーネントを落としたりログの重大度を下げたりする。修正は、重要なコンポーネントが存在すること、重要なログメッセージが正しい重大度で生き残ることを検証する小さなCIゲートだ。派手ではない。効果的だ。

土曜の午後。デモは何かが間違っているとき声を上げるようになった。ポーズメニューは再びクリックに反応する。礼儀正しい空っぽのデモというパーティーは終わった。

構築に戻る。

自分がどのモードにあるかを教えてくれるエンジン

RakuAIはあらゆる劣化モードを、grepで見つかる文字列とともに大声でERRORとして表面化させ、静かな失敗を最初に見つける監査を行う。それが、空間ランタイムがその上に構築するチームに対して負う信頼性の規律だ。

← すべての記事