STATION OOPS開発ログ⑦:Godot実装:定番の落とし穴編

初心者向け実践開発

※本記事にはアフィリエイトリンクを含みます。

ステージの実装が進むにつれて、Godot特有のつまずきもいくつも積み重なりました。今回は、その中でも特に印象に残った5つの不具合をまとめます。いずれも「見た目や仕様を思い込みで判断したこと」が根っこにある不具合でした。

スプライトシートを「グリッド」だと誤認していた

キャラクターのアニメーション素材として受け取った1280×1280のスプライトシート(複数のコマを1枚の画像にまとめたもの)を、最初は「同じポーズを5×5の25回繰り返しただけの画像」だと誤解していました。そのため、1コマ分だけを切り出して使う処理をしていました。

実際には、この画像は5×5=25フレームの本物のアニメーション(例えば歩行なら、実際に足が動くコマ送り)でした。この誤解のせいで、ゲーム内ではキャラクターが1枚絵のまま止まって見える不具合が発生しました。全キャラクター分を25フレームすべて抽出し直すことで解決しましたが、スプライトシートを処理する際は、各コマが本当に同一かどうかをピクセル差分(画像同士を重ねて、実際にどこが違うかを数値で比較する確認方法)で確認してから処理方針を決めるべきだった、という反省が残りました。見た目だけで「同じに見える」と判断しないことが大切です。

ジャンプアニメーションが「二段ジャンプ」に見える不具合

ジャンプ用のスプライトシート(25フレーム)の中に、「屈伸→頂点→屈伸」という動きが2周期分収録されていることに気づかず組み込んだ結果、1回のジャンプ入力でキャラクターが2回跳ねているように見える不具合が発生しました。

フレーム範囲を絞り込むことでこの症状自体は解決しましたが、それだけでは「アニメーションの再生時間」と「実際の物理的なジャンプの滞空時間」が噛み合いませんでした。最終的には、上昇と下降を別々のアニメーション(jump/fall)に分割し、フレーム範囲を頂点の前後で正確に分けた上で、実際の滞空時間から逆算してアニメーションの再生速度を設定することで、綺麗な弧を描くジャンプになりました。フレーム数を減らすだけでなく、物理的な動きの時間から逆算する必要がある、という学びです。

攻撃判定が特定のオブジェクトにだけ当たらない

プレイヤーの攻撃判定をArea2D.get_overlapping_bodies()(重なっている物理オブジェクトの一覧を取得する関数)で実装していたのですが、座標もコリジョンレイヤー(当たり判定同士が反応する対象を絞り込む仕組み)もシェイプサイズも完全に一致しているはずなのに、特定の破壊可能オブジェクトにだけ攻撃が当たらない不具合が発生しました。

物理エンジンへの直接クエリ(PhysicsShapeQueryParameters2Dintersect_shape()という組み合わせで、その場で直接重なりを調べる方法)で確認したところ、物理的には確実に重なっているのに、get_overlapping_bodies()だけが空の結果を返し続けていることが判明しました。

ここでの学びは、Area2Dの重なり検出は、キャッシュのような仕組みで動いているため、「プレイヤーが歩いて近づく」ような継続的なトリガー範囲には向いているものの、攻撃のように「一瞬だけ判定したい」ケースでは更新が追いつかないことがある、というものです。一瞬の判定には、監視機能ではなく直接の物理クエリを使う方が確実でした。

ステージ間の移動ができない不具合が二重に発生

あるステージで、次のエリアに進めないという不具合が起きました。調べてみると、原因が2つ重なっていました。1つ目は、そもそもステージの出口(ゴール)そのものが実装されていなかったこと。2つ目は、出口を追加した後も、ジャンプ練習用に置いていた足場の1枚が床の真上に隙間ゼロで配置されていたため、その列が実質的な「壁」になってしまい、出口にたどり着けなかったことです。

ジャンプ練習用の足場配置と、ステージのゴールへの導線は、別々に設計しているつもりでも、同じ座標系の中で干渉していることがあります。両方を俯瞰して衝突がないか確認する必要がある、という教訓でした。

シーン遷移時の物理コールバックエラー

body_entered(何かがそのエリアに入った瞬間に呼ばれる合図)の中で、直接change_scene_to_file()(シーンを切り替える関数)を呼んだところ、「物理的な処理の最中にコリジョンノードを削除しようとしている」という趣旨の警告が出ました。

対処は、call_deferred()(処理を1フレーム分だけ遅らせて実行する仕組み)を使い、シーン遷移の実行を1フレーム遅らせることでした。物理的な計算の途中でノードを消してしまう処理は、少し先送りにして安全なタイミングで実行する必要がある、というGodotの設計上の注意点です。

まとめ:Godotの癖として持ち帰った教訓

今回の5つの不具合を通じて、Godotの設計思想としていくつかの癖が見えてきました。タイルデータはtile_dataへの手書きを避けてset_cell()という関数をスクリプトで呼ぶこと、Area2Dを他のArea2Dから検知させたい場合はmonitoringだけでなくmonitorableも有効にする必要があることなど、今後のステージ実装でも繰り返し意識することになった教訓です。

次回は、キャラクターの「動きの気持ちよさ」を改善する過程と、過去に直したはずのバグを別の場所で再び踏んでしまった話を紹介します。

Godotの基礎的な仕組みを体系的に学びたい方は、以下の講座も参考になります。

コメント

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