コリジョンレイヤーが重要な理由

コリジョンレイヤーは Godot 4 における全ての物理インタラクション(移動、ヒット判定、レイキャスト、Area トリガーなど)の基盤です。LayerMask の違いを正しく理解することは極めて重要で、これを誤解することが初心者にも経験者にも最もよくある落とし穴の一つです。

Layer vs Mask — 考え方

Godot のすべての物理オブジェクトは 2 つのビットマスクプロパティを持ちます:

重要なルール

A と B が衝突するのは、A の Mask に B の Layer が含まれている、または B の Mask に A の Layer が含まれている場合です。片方が相手を「見て」いれば衝突は発生します。

プロジェクト設定でレイヤーに名前をつける

コードを書く前に、まずレイヤーに名前をつけましょう。インスペクターが格段に使いやすくなり、プロジェクトが大きくなっても混乱を防げます。

プロジェクト設定 > レイヤー名 > 2D 物理(または 3D 物理):

レイヤー番号 名前 用途
1Playerプレイヤーキャラクター
2Enemy全ての敵キャラクター
3Environment壁、床、プラットフォーム
4Projectile弾丸、矢、魔法
5Pickupアイテム、コイン、回復パック
6Triggerセンサー、スポーンゾーン、チェックポイント

コードでレイヤーを設定する (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 説明
Player12, 3, 5, 6敵、環境、アイテム、トリガーを検出
Enemy21, 3プレイヤーと環境を検出
Environment3受動的 — 他から検出される
Projectile42, 3敵と壁に当たる
Pickup5受動的 — プレイヤーが検出
Trigger61プレイヤーのみ検出

トップダウン / ローグライク

オブジェクト Layer Mask 説明
Player12, 3, 5, 6敵、壁、NPC、センサーを検出
Enemy21, 3プレイヤーと壁を検出
Wall3受動的
Bullet41, 2, 3プレイヤー、敵、壁に当たる
NPC53壁のみと衝突
Sensor61プレイヤーの進入を検出

Area2D / Area3D のレイヤー

Area も同じ Layer/Mask システムを使います。さらに 2 つのトグルプロパティがあります:

シグナル: area_entered vs body_entered

body_enteredPhysicsBody(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)はマスクしません。これがレイヤー設定だけでフレンドリーファイアを防ぐ方法です — コード不要です。

デバッグのヒント

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 を入手 — $15
関連ツール: setup_collision set_physics_layers get_physics_layers get_collision_info add_raycast