この記事は https://ziva.sh/blogs/godot-collision-not-working の日本語版です(英語の原文はZiva.shが2026年11月10日に公開)。
Area2Dのbody_enteredがいつまでも発火しない、またはプレイヤーが床をすり抜けて落ちていく場合は、下の7つの設定を確認してください。Area2Dに関する項目はGodot 4.7.2でテストし、床の項目はサンプルプロジェクトで再現しました。AIに原因を探させたい場合は、Godotエディターの中で動くAIエージェントのZivaが、わざと壊したシーンの4つのバグを1分34秒ですべて見つけて修正しました。使ったのはvalidate_physics_setupツールと、シーンの読み取りです。そのテストは記事の後半にあり、壊れたプロジェクトもダウンロードできます。
チェックリスト
上から順に確認してください。各項目の修正方法は1行で書いています。
| # | 原因 | 修正方法 |
|---|---|---|
| 1 | Area2DのMaskにボディのレイヤーが含まれていない | Area2DのCollision > Maskで、ボディのレイヤーにチェックを入れる |
| 2 | CollisionShape2DにShapeがない、またはDisabledになっている | Shapeに新しいRectangleShape2D(または同様のシェイプ)を設定し、Disabledのチェックを外す |
| 3 | Area2DのMonitoringがオフになっている | Area2DのインスペクターでMonitoringにチェックを入れる |
| 4 | シグナルが接続されていない、または誤ったノードに接続されている |
body_enteredを、そのメソッドを持つスクリプトのノードに接続する |
| 5 | ボディに対してarea_enteredを待ち受けている |
ボディにはbody_entered、ほかのArea2Dにはarea_enteredを使う |
| 6 | CharacterBody2Dが床をすり抜けて落ちる | キャラクターのCollision > Maskで、床のレイヤーにチェックを入れる |
| 7 | 床が物理レイヤーのないTileMapLayerになっている | TileSetに物理レイヤーを追加し、固いタイルにコリジョンポリゴンを描く |
項目1から5のテスト方法:ヘッドレスのGodot 4.7.2のスクリプトで、コリジョンレイヤー2にあるCharacterBody2Dを、60 x 60の矩形シェイプを持つArea2Dの中を通って落下させ、move_and_slide()で1物理フレームあたり4ピクセルずつ動かして、シグナルが発火したかを記録しました。正しく設定した場合は発火し、項目1から5の壊れた設定ではどれも発火しませんでした。
1. コリジョンのレイヤーとマスク:重要なのはArea2Dのマスク
Godotの物理の入門ページでは、2つのプロパティを次のように定義しています。レイヤーは「describes the layers that the object appears in」(そのオブジェクトが存在するレイヤーを表す)もの、マスクは「describes what layers the body will scan for collisions. If an object isn't in one of the mask layers, the body will ignore it.」(ボディが衝突を探すレイヤーを表す。オブジェクトがマスクのどのレイヤーにもなければ、ボディはそのオブジェクトを無視する)ものです。どちらも既定ではレイヤー1です。
Area2Dがボディを検出するには、ボディのレイヤーがArea2Dのマスクに含まれている必要があります。テストでは、マスクが1のArea2Dは、レイヤー2にあるボディを一度も検出しませんでした。逆方向は関係ありませんでした。Area2Dのレイヤーを0、ボディのマスクを0にしても、body_enteredは発火しました。そのため、コインやトリガーがプレイヤーに反応しないときは、まずトリガーのMaskを確認してください。
インスペクターではレイヤーの番号にチェックを入れますが、.tscnファイルやコードでは同じ値がビットマスクになります。レイヤー1は1、レイヤー2は2、レイヤー3は4、レイヤー4は8です。シーンにcollision_mask = 4と書かれていれば、スキャンするのはレイヤー4ではなくレイヤー3です。コードでは、set_collision_mask_value()を使えばこの計算を省けます。
extends Area2D
func _ready() -> void:
set_collision_mask_value(2, true)
body_entered.connect(_on_body_entered)
func _on_body_entered(body: Node2D) -> void:
print("entered: ", body.name)
2. シェイプがない、または無効になっている
テストでは、Shapeが空のCollisionShape2Dを子に持つArea2Dは何も検出せず、Shapeは設定されていてもDisabledにチェックが入っている場合も同じでした。CollisionShape2Dを選択し、空のShape欄をクリックしてシェイプを選び、スプライトに重なる大きさにしてください。
3. Monitoringがオフになっている
Monitoringをオフにすると、テストのArea2Dは何も検出しませんでした。Area2Dのリファレンスにも、body_enteredは「Requires monitoring to be set to true.」(monitoringがtrueである必要がある)と書かれています。既定ではオンなので、インスペクターと、これをfalseにしているスクリプトがないかを確認してください。
4. シグナルが接続されていない、または誤ったノードに接続されている
_on_body_entered関数は、シグナルが接続されていなければ何もしません。接続がない場合、テストでは何も発火しませんでした。Signalsドック(Inspectorの隣にあるSignalsタブ)で接続するか、上のスニペットのようにコードで接続してください。
接続先も、そのメソッドを持つスクリプトのノードである必要があります。サンプルのシーンでは、コインからレベルのルート(スクリプトなし)への接続では_on_body_enteredが一度も実行されず、Godotはエラーも出力しませんでした。ドックに接続が表示されているのに何も起きないときは、接続先を確認してください。
5. body_enteredとarea_entered
body_enteredは、物理ボディ(CharacterBody2D、RigidBody2D、StaticBody2D)と、TileSetにコリジョンシェイプがあるタイルマップに対して発火します。area_enteredは、ほかのArea2Dに対してだけ発火します。テストでは、CharacterBody2Dに対してarea_enteredを待ち受けても、一度も発火しませんでした。
6. CharacterBody2Dが床をすり抜けて落ちる
これは項目1の逆方向です。キャラクターが床とぶつかるのは、床のレイヤーがキャラクターのマスクに含まれている場合だけです。サンプルプロジェクトでは、プレイヤーはレイヤー2にあってcollision_mask = 4(レイヤー3)で、床はレイヤー1にありました。3秒後、プレイヤーはy 560の床よりはるか下のy 4327にいました。プレイヤーのマスクを1にすると、プレイヤーはy 520で床の上に止まりました。
7. 物理レイヤーのないTileMapLayerの床
TileMapLayerは、TileSetに物理レイヤーがあり、固いタイルにコリジョンポリゴンがあるまではコリジョンを持ちません。手順はTileMapLayerのガイド(英語)にあります。この場合も、キャラクターのマスクにその物理レイヤーのコリジョンレイヤーが含まれている必要があります。
原因ではなかったもの
次の2つは、テストでは検出を妨げませんでした。
-
move_and_slide()ではなくpositionを設定してボディを動かす:body_enteredは発火しました。テストのボディは1フレームあたり4ピクセル動かしました。薄いエリアを飛び越えてしまうような大きな移動はテストしていません。 -
ボディが最初からエリアの内側にある:
body_enteredは発火しました。
検証:コリジョンが効かない原因を見つけるAI
チャットのウィンドウから見えるのは貼り付けたコードだけですが、レイヤー、マスク、シェイプ、シグナルの接続はシーンファイルの中にあります。ジャンプしたキャラクターが空中に浮いたままになって困っていたGodotフォーラムのユーザーは、「chat gpt just scrambled my code to trash」(ChatGPTにコードをめちゃくちゃにされただけだった)と書いています(Godot Forum)。シーンを読んで実行できるエージェントなら、コリジョンのバグにもっとうまく対処できるのかを確かめたいと考えました。
ZivaはZiva.shが開発しており、このテストもZiva.shが作成しました。
テストは2026年11月10日に、Linux上のGodot 4.7.2で実行しました。Zivaは開発ビルドを使い、モデルは既定のZiva Smart、自動承認(auto-approve)はオンです。1つのプロジェクトで1回だけ実行した結果なので、ベンチマークではなく1つの例として見てください。
4つのバグを仕込んだ小さなシーンをつくりました。プレイヤーのマスクに床のレイヤーが含まれていない、コインのマスクにプレイヤーのレイヤーが含まれていない、コインのCollisionShape2Dにシェイプがない、コインの_on_body_enteredが接続されていない、の4つです。自分で試したい場合は、壊れたプロジェクトをダウンロード(ZIP、2 KB)してください。Godot 4.7.2でインポートして読み込めます。F5を押すと、プレイヤーが床をすり抜けて落ちます。
プロンプトは次のとおりです。一字一句そのまま渡しました(実際に使った英語のプロンプトと、その訳です)。
In res://collision_demo/level.tscn my player falls straight through the floor, and when the player touches the coin nothing happens (it should print "Coin collected" and disappear). Find out why and fix it. Run the scene to check it works.
訳:res://collision_demo/level.tscnで、プレイヤーがそのまま床をすり抜けて落ちます。また、プレイヤーがコインに触れても何も起きません(本当は「Coin collected」と出力して消えるはずです)。原因を見つけて修正してください。シーンを実行して、動作することを確認してください。
Zivaがしたこと:
- シーンツリー、
level.tscn、2つのスクリプトを読みました。 - PlayerとFloorに対して
validate_physics_setupを呼び出しました。返ってきた問題は1つで、/root/Playerに対する"collision_mask does not overlap with any platform collision_layer"と、提案の"Update collision_mask on /root/Player to include bits from platform layers"でした(プレイヤーのcollision_maskがどの足場のcollision_layerとも重ならないので、足場のレイヤーのビットを含めるように、という内容です)。 - 4つのバグをすべて挙げました。ツールが指摘したのは1つ目だけで、コインのマスク、シェイプの欠落、接続の欠落の3つは、Zivaが読んだシーンファイルから見つけました。
-
level.tscnを編集して修正しました。プレイヤーのマスクを4から1に、コインのマスクを1から2に変え、コインに24 x 24のRectangleShape2Dを設定し、body_enteredの接続を追加しました。 - 自分のミスに気づきました。最初はシグナルをレベルのルートに接続しましたが、
_on_body_enteredがコインのスクリプトにあることに気づき、何かを実行する前に接続先をCoinノードに変えました。 - もう一度
validate_physics_setupを実行し(問題なし)、シーンを実行しました。GodotはCoin collectedを出力し、Zivaのスクリーンショットには床の上にいるプレイヤーが写っていて、コインは消えていました。
かかったのは1分34秒、ツールの呼び出しは16回で、既定のモデルZiva SmartでのZivaの使用量は$0.0175でした。修正後のプロジェクトに対して別に実行した独自のチェックスクリプトでも、同じ結果になりました。プレイヤーはy 520で床の上に止まり、Coin collectedが出力され、コインは削除されていました。
validate_physics_setupが確認すること、しないこと
validate_physics_setupは、シーン、キャラクターのノード、足場のノードを受け取ります。キャラクターごとに、コリジョンマスクが0でないこと、マスクが少なくとも1つの足場のコリジョンレイヤーと重なっていること、シェイプを持つCollisionShape2Dの子があることを確認します。足場がTileMapLayerの場合は、TileSetが割り当てられていて、物理レイヤーを持っていることを確認します。結果は、問題と提案のリストとして返ります。
Area2D、Monitoring、シグナルの接続は確認しません。今回の実行で、このツールが見つけたのは床のバグだけでした。コインの3つのバグは、モデルがシーンを読んで見つけたものです。そのため、大きなシーンでは、この部分はZivaが正しいファイルを読むかどうかにかかっています。
Zivaがつまずいたところ
- シグナルの接続先の誤り(実行前に修正)。 最初の接続はレベルのルートを向いていて、上の項目4のとおり、エラーなしで失敗するところでした。Zivaはシーンを実行する前にこれを直しました。
- 小さなシーン1つだけ。 関係するノードは3つで、バグはすべて1つのファイルの中で見える状態でした。多数のシーンにまたがるコリジョンのバグや、実行時にレイヤーを変えるコードが原因のバグでは、まだZivaをテストしていません。
自分のプロジェクトで試すには、Zivaをインストールし、人に話すときと同じように症状を説明してください。たとえば「my player falls through the floor, find out why and fix it, then run the scene.」(プレイヤーが床をすり抜けて落ちるので、原因を見つけて修正し、シーンを実行して)のように伝えます。
よくある質問
GodotでArea2Dのbody_enteredシグナルが発火しないのはなぜですか?
Godot 4.7.2でのテストでは、次の場合にbody_enteredが発火しませんでした。Area2Dのコリジョンマスクにボディのコリジョンレイヤーが含まれていない場合、CollisionShape2Dにシェイプがないか無効になっている場合、Monitoringがオフの場合、シグナルが接続されていない場合、CharacterBody2Dに対してbody_enteredではなくarea_enteredを待ち受けている場合です。この順に確認してください。Area2D自身のレイヤーとボディのマスクは関係ありませんでした。
Godotのコリジョンレイヤーとマスクの違いは何ですか?
コリジョンレイヤーは、オブジェクトが属するレイヤーの集合です。コリジョンマスクは、オブジェクトが衝突を探すレイヤーの集合です。Area2Dがボディを検出するのは、ボディのレイヤーがArea2Dのマスクに含まれている場合だけです。CharacterBody2Dが床の上で止まるのは、床のレイヤーがキャラクターのマスクに含まれている場合だけです。.tscnファイルとコードでは値はビットマスクで、レイヤー1は1、レイヤー2は2、レイヤー3は4、レイヤー4は8です。
GodotでCharacterBody2Dが床をすり抜けて落ちるのはなぜですか?
まずキャラクターのコリジョンマスクを確認してください。床のコリジョンレイヤーが含まれている必要があります。サンプルでは、プレイヤーのマスクが4(レイヤー3)で、床はレイヤー1にあり、プレイヤーは3秒でy 4327まで落ちました。プレイヤーのMaskでレイヤー1にチェックを入れると直りました。CollisionShape2Dにシェイプがない場合や、TileMapLayerのTileSetに物理レイヤーがない場合も、同じ症状になります。
Godotでコリジョンが効かない原因を見つけてくれるAIツールはありますか?
Godotエディターの中で動くAIエージェントのZivaは、テスト用の壊れたシーンに仕込んだ4つのバグを1分34秒ですべて見つけて修正しました。プレイヤーのマスク、コインのマスク、コリジョンシェイプの欠落、未接続のシグナルの4つです。validate_physics_setupツールが指摘したのはプレイヤーと床のマスクの不一致だけで、残りの3つはZivaがシーンファイルを読んで見つけました。そのあとZivaはシーンを実行し、Coin collectedが出力されるのを確認しました。1つのプロジェクトで1回実行した結果です。
ボディのコリジョンマスクにArea2Dのレイヤーを含める必要はありますか?
Area2Dがボディを検出するためには、必要ありません。Godot 4.7.2でのテストでは、Area2Dのレイヤーを0、ボディのマスクを0にしても、Area2Dのマスクにボディのレイヤーが含まれていたので、body_enteredは発火しました。相手のレイヤーと一致している必要があるのは、検出する側のオブジェクトのマスクだけです。
この記事の原文(英語):https://ziva.sh/blogs/godot-collision-not-working
