AIとゼロから作る脱出ゲーム開発日記 第8話:3ルート本編完成、そして「直したはず」のバグがまだ隠れていた話

Claude Codeのターミナル画面と、3つの行き先を選ぶ画面、3スロットのリュックアイコンが並ぶデスクのイメージ画像。 開発日記

前回までのあらすじ

前回は、17点の新アセットバッチを投入したら二段構えの透過バグに遭遇し、指示の行き違いや編集モードのバグも乗り越えて、本編第1弾(釣りルート)がようやく完成したところまで書きました。

今回は、その釣りルート単体のミニ版から、プール・虫取りを加えた3ルート・複数部屋を持つ本編へ、一気に拡張した回です。持ち物容量、アイテムの入れ替え、評価システムといった、これまで何度も「今は必須機能と考えていない」と見送ってきた要素に、ようやく本腰を入れて着手します。ただ、本編が形になって一段落——とはいきませんでした。後半では、これまで「目視で確認して問題なし」としてきたアセットに、想定よりずっと根深い透過バグが潜んでいたことが分かります。今回は少し長丁場です。

第1章:4つの新機能を「理論」ではなく実コードで監査

本編化にあたって、「ルート選択」「持ち物容量+入れ替え」「コミカルな失敗演出」「評価システム」という4つの追加機能案が出ました。ただ、いきなり実装に入るのではなく、まず既存のコードを実際に読んで、それぞれが「無改修で可能」「小さな追加で可能」「構造変更が必要」のどれに当たるかを判定することにしました。

結果は次の通りです。評価システムは、既にあるrequiresFlag・endingConditions・GAME_CONFIG.endingsの組み合わせだけで、エンジンに一切手を入れずに対応できると分かりました。ルート選択も、zoomタイプとtargetSceneの組み合わせで実現でき、部屋を複数ルートで共有するだけなら同じく無改修で済みます。一方、持ち物容量制限とアイテムの入れ替えだけは、既存の所持品ロジックに上限判定もドロップの仕組みも存在しておらず、構造的な追加実装が必要だと判定しました。

「思ったより無改修で行ける部分」と「本当に手を入れる必要がある部分」を、机上の憶測ではなく実装を読んだ上ではっきり切り分けられたことで、後の手戻りを防げました。

第2章:確定仕様の実装——ルート・容量・入れ替え・評価

判定結果を踏まえて、実際にエンジン層へ機能を追加していきました。

まずGAME_CONFIG.routesとobj.scoreByRouteです。ルートごとに必須アイテムを定義しておき、出発時にプレイヤーが実際に持っているアイテムの数に応じて、完璧・及第点・失敗の3段階で判定する仕組みです。次にGAME_CONFIG.inventoryCapacityと、ドロップ・入れ替えの機構です。所持数が上限に達した状態で新しいアイテムを拾おうとすると、既存のアイテムを外して入れ替えられるようにしました。gameState.itemSourcesで「どこで拾ったか」を逆引きできるようにしてあるので、外したアイテムはちゃんと元の場所に戻ります。

あわせて、zoomタイプのオブジェクトにsetFlag対応を追加し、3つの絵をクリックして今日行く場所を選ぶ、ルート選択画面を実現しました。シーンに入った瞬間に自動でヒントテキストを出すsceneConfig.entryHintというオプトイン機能も、この段階で追加しています。

3つのルートすべてで、「全部揃える→完璧」「1つ欠ける→及第点」「2つ以上欠ける→失敗」の判定と、容量オーバー時の入れ替え→再取得という一連の流れを、実際にブラウザを操作して確認しました。

第3章:「即ゲームオーバー」をやめて、その場で気づかせる演出に

持ち物が足りない状態で扉をクリックした際、当初の設計では即座にゲームオーバーにしていました。ところが、「ちょっと厳しい。メッセージを出すのと、一旦取ったものを外せるのも欲しい」という指摘が入ります。

対応として、scoreByRouteの失敗判定からゲームオーバーの呼び出しを削除し、鍵のかかったドアと同じ「ヒント表示+シェイクアニメーション」の演出に統一しました。「一旦取ったものを外す」という要望については、実は前の章で先に実装していたドロップ・入れ替えの機構がそのまま満たしていたので、追加の実装は不要でした。使われなくなったui.gameoverの設定は、おでかけ側のconfig.jsから削除しています。ただし、エンジン側の見出し色を変える機能自体は、将来また使うかもしれないので残してあります。

第4章:部屋の増設と、何度も変わったレイアウト

最小構成として、キッチンと洗面所の2部屋を追加しました。自分の部屋⇄キッチン⇄玄関⇄洗面所という、直線的なナビゲーションでつないでいます。窓については「カーテンの層はあえて設けず統一する」という方針にし、キッチンでは既存の窓画像を、コンロの両脇に2つ配置する形で使い回しました。

部屋の開始位置や並び順は、実は2回変更が入っています。最初は「玄関スタートで洗面所を右に」という配置だったのが、「やっぱり左から自分の部屋・キッチン・玄関・洗面所の順がいい」という形に変わりました。ここで効いたのが、前回までに積み上げてきた「座標をオブジェクト単位で持つ」設計です。シーンのナビゲーション設定と各オブジェクトのx・y座標を書き換えるだけで対応でき、画像の作り直しは一切発生しませんでした。

このタイミングで、「ルート選択画面の左上のテキストが見えない」という指摘にも対応しています。原因は、文字色がテーマ切り替え用の変数を参照していて、ライトテーマだと暗い背景の上に暗い文字が乗ってしまい読めなくなっていたことでした。固定色に変更して解決し、あわせて「どれを選べばいいか分からない」という声には、ルート選択画面にだけ案内文を出す形で応えています。

第5章:編集モードに「反転」機能を追加、そして地味なバグ

洗面所に置いた家具(洗面台の鏡)が2つとも同じ向きを向いていて不自然、という指摘がありました。まずobj.flipXという設定値で個別に対応したのですが、続けて「反転も編集モードでできないか」と聞かれ、これまでドラッグとリサイズしかできなかった配置編集モードに、反転専用のハンドルボタンを追加することにしました。

ところが、追加した反転ボタンが反応しない不具合が発生します。原因を調べると、編集モード中は画面全体を覆う形で「クリックイベントの捕捉フェーズで伝播を止める処理」が入っていて、これがclickイベントを対象にしていたため、同じくclickで実装していた反転ボタンのリスナーにイベントが届く前に止められていました。既存のドラッグ・リサイズのハンドルはpointerdownで実装されていたおかげでこの問題を回避できていたので、反転ボタンも同じくpointerdownに変更して解決しています。

第6章:自分の目で見つけた、4件の見落とし

3ルート本編が形になり、ひとしきり遊んでみたところで、思わぬ報告をすることになりました。これまでの透過チェック——白背景やチェッカー柄の背景に載せて目視で確認する方法——で「問題なし」と判断していたはずのアセットの中に、実際にゲーム画面で遊んでいて気づいた不具合が4件見つかったのです。虫かご、冷蔵庫の下段の裏、洗面所の棚の背景・瓶・タオル、洗濯カゴ。

実際に4件とも本物の不具合だと確認できました。ただ、それ以上に認めざるを得なかったのは、自分たちの検証方法そのものに欠陥があったという事実です。白い背景やチェッカー柄の上に画像を置いて目で見るだけでは、実際にゲーム内の背景に重なったときにだけ見えてくる薄い透過を見逃してしまいます。

以後、そのオブジェクトが実際に配置される背景画像から本物の色を直接サンプリングし、そこに重ねて確認する方法に統一しました。この方法で全画像を改めて再点検したところ、以前から指摘だけされて未対応のままだったtoybox.png(開いた蓋の内側)も、実は同じ不具合だったと確定し、あわせて修正しています。この時点で確認できた不具合は、door・cupboard・stove-sink-counter・bathroom-sink-mirror・fridge・bathroom-shelf・laundry-basket・bug-cage・toyboxの9点。少なくとも3回の異なる納品バッチにまたがって発生していて、納品済み約30点のPNGのうち3割前後に達していました。

第7章:「場当たり的な指摘対応」という根本原因

ここで、「そもそも、背景じゃないところが透過するような部品を作ってしまうこと自体が問題」という指摘がありました。これまでのOpenMontage側の対応を振り返ると、「グリーンスクリーン+ピクセル差分検証」という改善策自体はきちんと存在していたのですが、それが適用されていたのは個別に指摘した特定のファイルだけで、同じバッチや過去のバッチに含まれる他の未指摘ファイルには一度も適用されていませんでした。これが、同じ種類の不具合が何度も表面化していた直接の原因だと整理できました。

この分析を踏まえて、OpenMontage側に今後の恒久対応を依頼しています。透過画像を生成した直後に自動でPASS/FAIL判定するQAスクリプトの導入、PASSしたものだけを承認済みフォルダへ移し、FAILは隔離して修正対象にすること、1件でもFAILが出たら同じバッチや過去の同系統アセットまで自動で横串の再検査をかける仕組み——「気づいた分だけ直す」から抜け出すための提案です。窓のように意図的に背景が透ける部分は、無理に1枚絵にせず窓枠だけを透過素材として切り出す設計に変えることも依頼しました。

こちらからも3点補足しています。判定基準を「完全に透明」だけでなく、背景がうっすら透けて見えるレベルの半透明まで広げること。逆に、ガラス瓶のような意図的な半透明表現を誤って検知しないよう、対称性で判定すること。そして、自動判定が難しいグレーゾーンは無理にPASSかFAILか決めず、人間のレビューに回すこと。

第8章:直したはずのファイルに、もう一つの穴

指示を送った直後、「洗面所の棚がまだ透過してるよ」という報告がありました。前の章で確かに修正したはずのファイルです。

アルファチャンネルを画像として可視化し、チェッカー柄と合成して確認したところ、以前の修正は棚の背景パネルの穴だけを直していて、中段に畳まれたタオルの中央にあった、幅約230×高さ約90pxのもう一つの大きな穴を丸ごと見落としていたことが分かりました。原因は、画像の外周から到達できる透明領域を「本物の背景」として除外する判定ロジックが、この穴を家具の脚にある意図的な装飾用の透かし穴と細い経路でつながっていると誤認し、「外周に到達可能=問題なし」に分類してしまっていたことでした。

矩形領域を直接指定し、アルファ値のしきい値を緩めて再検出・再修正し、周辺の不透明なピクセルから色をサンプリングして自然に塗りつぶしました。ゲーム画面を実際に大きくズームして、床の色が透けていた箇所が塗り色に置き換わったことを目視で確認しています。前の章で提案したばかりの「半透明も検査対象に含めるべき」という教訓を、自分自身の再検証作業の中で早速裏付ける形になりました。

第9章:AIの「全数監査完了」報告と、独自検証での食い違い

OpenMontage側から、指示していた透過QAツール一式をリポジトリにコミットし、全アセットの監査(PASS 6/REVIEW 7/FAIL 17)を実施したという報告がありました。ただし「Codespaceの権限設定上プッシュができていない」とのことで、ローカルコミットのみだそうです。

いつも通り、報告をそのまま信じずに実際のGitHubリポジトリを確認しました。該当のツール一式はリモートには存在せず、「プッシュ未」という説明と矛盾はなかった一方、コードやREADMEの中身自体はこちらからは検証できないことも分かりました。

報告の中に、「網目っぽい形状は穴があって当然と決めつけず個別検証したところ、虫取り網の画像は本物のバグと判明した」という記述がありました。ところが、こちら側の過去の検証記録では、同じファイルを「クリーン」と判定していたのです。その場で再検証したところ、リングの内部が透けているのは虫取り網としては正しい表現で、持ち手の部分にも通常のアンチエイリアス以上の不具合は見つかりませんでした。判定基準に食い違いがありそうなので、具体的な座標と見た目の根拠を示してもらうよう申し送りにしています。

🎮 今回のバージョン

3ルート・複数部屋の本編に、ここまでの透過バグ修正を反映した版です。ただしbug-net.pngの判定食い違いはまだ解決していないので、見つけた方がいたら教えてください。
※:洗面所の棚等がまだ透過してますが、これは次のバージョンで対応予定です。

💬 読者へのヒアリング

3ルートとも実際に遊んでみて、持ち物の入れ替えや評価のタイミングに違和感があれば教えてください。見た目の透過やズレなど、気になった細かい点もぜひ。特にガラス瓶や網目のような「わざと透けている」表現と、本物の不具合の見分けが付きにくいところがあれば、率直な感想が参考になります。

まとめ&次回予告

今回は3つの教訓が残りました。1つ目は、白背景やチェッカー柄で目視確認するやり方では、透過バグの一部しか見つけられないということ。そのオブジェクトが実際に置かれる背景から本物の色を直接サンプリングして合成する方法に切り替えて、ようやく見落としを減らせました。2つ目は、一度「直した」ファイルでも、別の場所に別の穴が残っている可能性があるということ。境界フラッドフィルのような自動判定ロジックは、意図的な透過と本物の穴が細い経路でつながっていると誤判定します。「直った」で終わらせず、修正後に改めて全域をスキャンする習慣が必要でした。3つ目は、「意図的な透過」と「異常な透過」の線引きは、しきい値だけでは決まらないということ。ガラス瓶の半透明表現と、網目の透過表現と、本物の穴は、単純なアルファ値の閾値では区別できないことがあります。

AIが「直した」「監査した」と報告してきても、実データで裏取りする習慣が、今回も具体的な食い違い(虫取り網の判定)を1件見つける形で役に立ちました。報告を鵜呑みにせず、かといって感情的に否定するのでもなく、「ここが食い違っているので根拠を教えてほしい」と淡々と返すやり方が、うまく機能しています。

持ち越し事項も何点か残っています。虫取り網の判定食い違いの根拠待ち、OpenMontage側の新しいQAツールのコード・README未レビュー、作業用に立てたままのmeta.debug: true、escape-game-template配下に何度も指摘してきた旧おでかけ素材の未整理、洗面所の瓶の半透明表現をどうするか。次回はこのあたりを片付けつつ、次の展開を考えていきます。

コメント

タイトルとURLをコピーしました