コリジョンレイヤーが重要な理由
コリジョンレイヤーは Godot 4 における全ての物理インタラクション(移動、ヒット判定、レイキャスト、Area トリガーなど)の基盤です。Layer と Mask の違いを正しく理解することは極めて重要で、これを誤解することが初心者にも経験者にも最もよくある落とし穴の一つです。
Layer vs Mask — 考え方
Godot のすべての物理オブジェクトは 2 つのビットマスクプロパティを持ちます:
- Layer = 「自分はこのレイヤーに存在する」(自分が何であるか)
- Mask = 「自分はこのレイヤーをスキャンする」(自分が見える/当たれる対象)
重要なルール
A と B が衝突するのは、A の Mask に B の Layer が含まれている、または B の Mask に A の Layer が含まれている場合です。片方が相手を「見て」いれば衝突は発生します。
プロジェクト設定でレイヤーに名前をつける
コードを書く前に、まずレイヤーに名前をつけましょう。インスペクターが格段に使いやすくなり、プロジェクトが大きくなっても混乱を防げます。
プロジェクト設定 > レイヤー名 > 2D 物理(または 3D 物理):
| レイヤー番号 | 名前 | 用途 |
|---|---|---|
| 1 | Player | プレイヤーキャラクター |
| 2 | Enemy | 全ての敵キャラクター |
| 3 | Environment | 壁、床、プラットフォーム |
| 4 | Projectile | 弾丸、矢、魔法 |
| 5 | Pickup | アイテム、コイン、回復パック |
| 6 | Trigger | センサー、スポーンゾーン、チェックポイント |
コードでレイヤーを設定する (GDScript)
Godot 4 では 2 つの方法があります — 値ベースの API(推奨)と、ビットマスクの直接代入です:
# Godot 4 API — value-based (1-indexed, recommended)
set_collision_layer_value(1, true) # I am on layer 1
set_collision_mask_value(2, true) # I detect layer 2
# Or use bitmask directly
collision_layer = 1 # Layer 1 only (bit 0)
collision_mask = 6 # Layers 2 and 3 (bits 1 + 2 = 6)
# Read current state
var on_layer_1: bool = get_collision_layer_value(1)
var scans_layer_2: bool = get_collision_mask_value(2)
ビットマスクの注意点
set_collision_layer_value() のレイヤー番号は 1始まり ですが、内部のビットマスクは 0始まり です。レイヤー1 = bit 0 = 値 1、レイヤー2 = bit 1 = 値 2、レイヤー3 = bit 2 = 値 4。迷ったら値ベースの API を使いましょう。
よくあるレイヤー構成例
プラットフォーマー / 横スクロール
| オブジェクト | Layer | Mask | 説明 |
|---|---|---|---|
| Player | 1 | 2, 3, 5, 6 | 敵、環境、アイテム、トリガーを検出 |
| Enemy | 2 | 1, 3 | プレイヤーと環境を検出 |
| Environment | 3 | — | 受動的 — 他から検出される |
| Projectile | 4 | 2, 3 | 敵と壁に当たる |
| Pickup | 5 | — | 受動的 — プレイヤーが検出 |
| Trigger | 6 | 1 | プレイヤーのみ検出 |
トップダウン / ローグライク
| オブジェクト | Layer | Mask | 説明 |
|---|---|---|---|
| Player | 1 | 2, 3, 5, 6 | 敵、壁、NPC、センサーを検出 |
| Enemy | 2 | 1, 3 | プレイヤーと壁を検出 |
| Wall | 3 | — | 受動的 |
| Bullet | 4 | 1, 2, 3 | プレイヤー、敵、壁に当たる |
| NPC | 5 | 3 | 壁のみと衝突 |
| Sensor | 6 | 1 | プレイヤーの進入を検出 |
Area2D / Area3D のレイヤー
Area も同じ Layer/Mask システムを使います。さらに 2 つのトグルプロパティがあります:
-
monitoring—trueにすると、この Area が他のオブジェクトの進入を能動的に検出します。 -
monitorable—trueにすると、他の Area がこの Area を検出できます。
シグナル: area_entered vs body_entered
body_entered は PhysicsBody(CharacterBody、RigidBody、StaticBody)が Area に入った時に発火します。area_entered は別の Area が入った時に発火します。用途に合ったシグナルを接続してください。
# Pickup Area2D — detect when the Player body enters
func _ready() -> void:
body_entered.connect(_on_body_entered)
func _on_body_entered(body: Node2D) -> void:
if body.is_in_group("player"):
collect()
queue_free()
RayCast のコリジョンマスク
RayCast ノードには Mask のみがあります(Layer はありません)。検出器であり物理ボディではないためです。Mask を設定してレイの衝突対象をフィルタリングします:
# RayCast that only detects enemies (layer 2)
$RayCast2D.collision_mask = 2 # Layer 2 = Enemy
# Or use the value-based API:
$RayCast2D.set_collision_mask_value(1, false) # Ignore Player
$RayCast2D.set_collision_mask_value(2, true) # Detect Enemy
$RayCast2D.set_collision_mask_value(3, false) # Ignore Environment
# Line-of-sight ray that ignores projectiles
$LineOfSight.collision_mask = 0
$LineOfSight.set_collision_mask_value(1, true) # Player
$LineOfSight.set_collision_mask_value(3, true) # Environment
実践例: Player – Enemy – Projectile
典型的なアクションゲーム向けの完全なレイヤー設定です。各選択の理由をコメントで示します:
# Player (CharacterBody2D)
# Layer: 1 (Player) — "I am the player"
# Mask: 2 (Enemy), 3 (Environment), 5 (Pickup)
# — I collide with enemies, walls, and can pick up items
# Enemy (CharacterBody2D)
# Layer: 2 (Enemy) — "I am an enemy"
# Mask: 1 (Player), 3 (Environment)
# — I collide with the player and walls
# Player Bullet (Area2D)
# Layer: 4 (Projectile) — "I am a projectile"
# Mask: 2 (Enemy), 3 (Environment)
# — I hit enemies and walls, but NOT the player who fired me
# Enemy Bullet (Area2D)
# Layer: 4 (Projectile) — "I am a projectile"
# Mask: 1 (Player), 3 (Environment)
# — I hit the player and walls, but NOT the enemy who fired me
ヒント: フレンドリーファイアの防止
Player Bullet はレイヤー2(Enemy)をマスクしますがレイヤー1(Player)はマスクしません。Enemy Bullet はレイヤー1(Player)をマスクしますがレイヤー2(Enemy)はマスクしません。これがレイヤー設定だけでフレンドリーファイアを防ぐ方法です — コード不要です。
デバッグのヒント
- コリジョンシェイプの表示: エディタでデバッグ > コリジョンシェイプを表示を有効にすると、実行時に全てのコリジョンシェイプが描画されます。欠落やズレのあるコライダーをすぐに発見できます。
- インスペクターで確認: ノードを選択し、インスペクターでCollision > Layer と Collision > Mask を展開します。各ビットにカーソルを合わせると名前が表示されます(プロジェクト設定で命名済みの場合)。
-
ランタイムで確認:
print("Layer: ", collision_layer, " Mask: ", collision_mask)でゲームプレイ中のビットマスク値を確認できます。
Godot 3 → 4 の移行変更点
| Godot 3 | Godot 4 |
|---|---|
set_collision_layer_bit(bit, value) |
set_collision_layer_value(layer, value) |
set_collision_mask_bit(bit, value) |
set_collision_mask_value(layer, value) |
bit パラメータは 0始まり
|
layer パラメータは 1始まり
|
| 20 レイヤー使用可能 | 32 レイヤー使用可能 |
移行時の落とし穴
Godot 3 プロジェクトを移植する場合、set_collision_layer_bit(0, true) は set_collision_layer_value(1, true) になります。インデックスが +1 ずれます。これを見落とすと全てのレイヤーが 1 つずれてしまいます。
よくあるミス
1. Mask の設定忘れ
オブジェクトに Layer はあるが Mask が空(全てゼロ)。オブジェクトは物理世界に存在しますが、何も検出しません。マスクが一致する他のオブジェクトからは検出されますが、このオブジェクトの move_and_slide() は全てをすり抜けます。
2. Layer と Mask の混同
プレイヤーの Mask ではなく Layer を 2(Enemy)に設定してしまう。物理エンジンからするとプレイヤーが敵になってしまいます。常に覚えておきましょう:Layer = 自分が何か、Mask = 何をスキャンするか。
3. レイヤー番号の代わりにビットマスク値を使う
set_collision_layer_value(4, true) でビットマスク値 4(レイヤー1+2)を設定したつもりが、実際にはレイヤー4を有効にしています。値ベースの API はビット値ではなくレイヤー番号を取ります。
4. 双方向を期待しているのに片方向だけ検出される
A が B のレイヤーをマスクしているが、B は A のレイヤーをマスクしていない。A の move_and_slide() は B と衝突しますが、B の move_and_slide() は A をすり抜けます。2 つの CharacterBody が互いにブロックするには、両方が相手のレイヤーを自分のマスクに含める必要があります。
Godot MCP Pro で物理設定を自動化
レイヤーのチェックボックスを手動で切り替えるのはやめましょう。AI がレイヤーの命名、マスクの割り当て、レイキャストの追加を含むコリジョン設定全体を数秒で構成します。
Godot MCP Pro を入手 — $15setup_collision
set_physics_layers
get_physics_layers
get_collision_info
add_raycast