
島の選択マップと、倒せなかったボス ── AIと作るジャンプゲームの開発記録
前回 は、「次は島の選択マップです」と締めました。今回は予告どおり、島を並べます。 この日やったのは、島の選択マップ、輸送機の作り直し、表情の追加、そしてゲーム側のボス調整と配置ツールです。3DCGとゲーム実装はリポジトリを分けてあり、別々のセッションで並行して進めています。片方でモデルを焼いている間に、もう片方でゲームのバグを潰す。AIに指示して作らせ、出てきたものを確かめて突き返す、その繰り返しを一日ぶんまとめました。 作業の中身は違っても、詰まった場所は同じでした。3D側は拡大するまで、ゲーム側は遊ぶまで、できていないことに気づけない。 完成したという報告と、実際にできていることの差を、どう見つけたかの記録です。 まずは、島を置くだけで使えるようにする 島の選択画面に必要なのは、一枚の完成した地形ではありません。「島は1つ1つ分離して、使い回せるように」という作りです。そこで、草の饅頭島、火山島、環礁、岩礁、森の島の5種類を、それぞれ独立したビルダーに分けました。ビルダーというのは、指定すると島を一式組み立てるコードです。 もうひとつ決めたのが、すべての島で水際をローカル座標の z=0 に揃えることです。海面も z=0 にしておけば、別の場面へ持っていっても高さを調整せず、置くだけで水際が合います。前回消した島は、その場限りの背景でした。今回は、どの海にも持っていける部品として作り直したわけです。 5種類を縦画面へ並べると、ようやくステージを選ぶ地図らしくなりました。 ここまでは予告どおりです。問題は、この画面と同じゲームで使う輸送機を、真横向きの画像へ焼き直したところから始まります。 真横から見た輸送機は、飛行船だった 前回の到着ムービーでは、輸送機を4発プロペラ、高翼、T字尾翼の形まで作り直しました。ところが真横にすると、映画のカメラでは隠れていた粗が全部見えます。 機首の上には意味不明な突起があり、胴体は飛行船のように丸い。輸送機らしい跳ね上がった後部がなく、主翼はただの板です。「輸送機ってこんな形でしたっけ。米軍のを想像していた」と駄目出しし、C-130系を目標に機体を全面的に作り直させました。数秒の映像の中で輸送機に見えればよかったものを、単体で見せられる形にするということです。 ところが形を直して拡大すると、もっと妙なものが見えました。窓の影です。 窓が、機体から最大0.5も浮いていた 胴体は楕円体なので、中央は太く、機首と尾へ行くほど細くなります。それなのに窓は、胴体の太さに関係なく、すべて横方向の座標を x=±2.02 に固定していました。中央付近なら表面に見えても、細くなった端では最大0.5も空中へ離れます。真横から見ると、浮いた窓が胴体へくっきり影を落としていました。 直し方は、窓ごとにその位置の楕円体の表面を解き、胴体の太さに合わせて貼り付けることです。全窓に同じ座標を与えるのをやめると、端の窓まで機体へ降りてきました。 前回の動画では、この窓を不自然だと思っていませんでした。流れる映像なら輸送機に見える。止めて、拡大して、初めて窓が別の物体として宙に並んでいると分かります。 半目が、分類の隙間から黙って消えた 同じことが顔でも起きました。アプリ側に増えた6表情を実測し、3Dへ載せる既存の仕組みへ流したところ、眠い顔の片目がありません。 眠い顔の左右の半目は、形が少し違います。塗られている割合を示す充填率は、一方が0.66、もう一方が0.60でした。この差が分類のしきい値をまたぎ、片方だけが「丸い目」と判定されます。もう片方は、ほかのどの分類にも入りませんでした。エラーにも警告にもならず、黙って捨てられていたのです。同じ穴で「聴く」顔の片目も消えていました。 分類を増やすだけでなく、どこにも入らなかったものを無言で捨てない作りにして、両目を戻しました。あわせて、顔から浮いて影を落としていた眉と、飛び出していた目も表面へ埋め、内側まで黒く塗り潰されていた口の縁取りも、穴のあるリングとして張り直しています。窓と同じで、正面からは接して見えるのに、角度を変えると浮いている。影は、AIが置いた部品と本体の隙間を正直に描いていました。 ここまでが「拡大して見つけた嘘」です。ここからは、拡大しても見つからないほうの話になります。 「必ず倒せます」と報告したボスは、絶対に倒せなかった ゲーム側では、門を守る巨人を作っています。倒し方は、巨人の腹の下に潜り、下から突き上げて3発。腹の真下はフォークの歯が届かない安全地帯で、そこを取り合うのが駆け引きです。 このボスが倒せないと指摘すると、AIは配置の条件を直し、「腹の下に足場がある配置を保証しました」と報告してきました。テストも通っています。ところが自分で遊ぶと、やはり倒せません。 抜けていた条件は、その足場に立ったとき、頭が腹にめり込まないことでした。めり込む高さだと、体の押し返し判定が毎フレーム働いて横へ弾かれ、腹の下に居座れません。突き上げる姿勢を作れないので、何度挑んでも0発です。プレイヤーからは「絶対無理」に見えます。 「腹の下に板がある」までは合っていました。足りなかったのは、そこに人が立てるかです。画面を拡大しても、この嘘は映りません。立って、跳んで、初めて分かります。 私と同じ操作で、AIに遊ばせる そこで、実際に遊ぶ検証ボットを書かせました。使える操作は左右キーだけ。ジャンプは着地で自動的に跳ねる仕様なので、私が指で遊ぶのと完全に同じ条件です。座標を直接書き換える瞬間移動は禁止しました。それを許した瞬間、証明になりません。 最初の実行は、和音を集める前に落下死しました。次は、ボスの真下へ潜れず、68秒間ただ跳ね続けて終わりました。倒せないのではなく、そもそも潜れないという事実が、ここではっきりします。 配置の条件を直したあとの走りが、これです。和音を3つ集め、鍵を取り、突き上げ3発で撃破し、門を開けるまで約12秒でした。 このボットは回帰テストとしてリポジトリに常駐させました。攻略できなければ失敗として落ちます。「倒せる配置になっているか」を、私が毎回自分で確かめなくてよくなりました。 倒せるようにしたら、今度は怖くなくなった ところが直ったボスを遊んでみると、今度は拍子抜けしました。腹の下に潜れば絶対に刺されません。何も怖くないのです。 原因は、ボスを門をまたぐ高さに置いたことでした。フォークの高さがプレイヤーの到達上限より上へ出てしまい、安全地帯が広すぎたのです。「倒せない」を「怖くない」に付け替えただけでした。 そこで闘技場を中段へ降ろし、フォークの薙ぎがジャンプの弧と重なるようにしました。さらに突き上げの反動で横にも弾かれるようにして、一発ごとに潜り直す必要を作ります。ボットで測ると、攻略は成立したまま、歯とのニアミスが出るようになりました。倒せて、かつ危ない状態です。 配置は、数値では決まらなかった ここまでの修正はすべて、AIが座標を数値で置いては私が突き返す、の繰り返しでした。何度指示しても適格な場所に置かれず、この件だけで1〜2時間が溶けています。ここで方針を変えました。自分で置けるようにするしかありません。 PCの画面でステージ全体を俯瞰し、巨人をドラッグして置くエディタを作りました。置いた瞬間に、その板に立てるか、跳べば腹に届くか、門より下か、フォークが跳躍弧に重なるか(つまり怖いか)を判定し、OKとNGで出します。倒せない配置も置けます。禁止せず警告に留めました。道具の自由を奪えば、結局また数値の指示に戻るからです。 盤面ごと、左に寄っていた エディタで触ってみると、別の異常に気づきました。右にはいくらでも置けるのに、左には置けない。門も左に寄っている。 調べると、原因はレベルの側にありました。鍵盤は15レーンあり、横位置がそのまま音の高さです。ところがステージ1の和音はドミソで、使うのは15本中の0番、2番、4番。盤面ごと鍵盤の左3分の1に固まっていたのです。門は最上段の板の真上に立つ仕様だったので、門も一緒に左端へ寄っていました。右側の10本は、その島では一度も使わない死に幅です。 そこで、その島が実際に使うレーンの中心が画面中央へ来るよう鍵盤ごとずらし、門の中心は和音のまん中の音(ドミソなら「ミ」)の真上に変えました。左右に同じだけ余白ができ、巨人の入場も回り込みも、どちらの側でも成立します。 確認する道具も、確認しなければ壊れる 3D側では、11表情を横や斜めから見る確認動画を作りました。ところが視点を増やすと、カメラの狙いが外れて背景一色になり、次はキャラの内部へ入り、その次は床の下へ潜ります。表情を確かめる動画そのものが、表情を映していませんでした。目視で済ませず、全フレームにキャラの画素が十分あるかを数値で検査する手順を足しています。 ゲーム側の検証ボットも同じでした。最初のボットは操縦が下手で、板を外して落ちるたびに「ゲームが悪い」ように見えます。ボットの操縦ミスと、ゲームの不具合を切り分けられるようになるまで、何度も書き直しました。成果物を確かめる道具も成果物です。信用する前に、道具のほうを疑う必要があります。 AIが直したと報告した箇所ほど、私が触ると壊れているのも同じ構図でした。「向きを変えられるようにしました」と言うので探すと、ボタンのラベルは「入場: 自動」。これでは何のボタンか読めません。予告マークが出ないと指摘して調べさせると、出現の判定は新しい設定を見るよう直したのに、予告を消す判定だけ古いままで、毎フレーム消されていました。 GPUで速くなった話の訂正 前回の記事で、レンダリングをMacのGPUへ切り替えて速くなったと書きました。今回、新しいスクリプトがCPUで動いていたので調べ直すと、設定を各スクリプトへ手書きしていたため、追加のたびに漏れる作りでした。さらに、Metalを指定した既存スクリプトも、GPUとCPUの両方を有効にしていました。 実測すると、混在は57.9秒で終わる代わりに、CPU時間の合計は400秒を超えます。GPUだけなら87.3秒、CPU時間は68.8秒でした。待ち時間だけなら混ぜたほうが速い。しかしその間、CPUを食い潰します。「GPUにして速くなった」は、半分は本当で、半分は見ていませんでした。設定欄の METAL を確認したところで満足し、その下でCPUも働いているところを拡大していなかったわけです。 ...