プレイヤーは壁にぶつかる。敵の攻撃はプレイヤーだけに当たる。プレイヤーの攻撃は敵だけに当たる。コインはプレイヤーだけが拾える。
ゲームを作っていると、こうした「何が何に反応するか」を大量に管理することになります。そこで使うのが Collision Layer と Collision Mask です。

この記事では、Layer/Maskを単なるチェックボックスとしてではなく、ゲーム全体の当たり判定を整理するための設計道具として解説します。
この記事でわかること
- LayerとMaskの役割の違い
- プレイヤー、敵、壁、攻撃、アイテムの実践的な分け方
- 攻撃判定やアイテム取得での設定例
- 衝突や検出が反応しないときの確認手順
Layerは所属、Maskは見に行く相手
Layer/Maskで最初に混乱しやすいのは、どちらも同じようなチェックボックスに見えることです。しかし役割はまったく違います。
| 項目 | 意味 | 考え方 |
|---|---|---|
| Collision Layer | 自分が所属している物理レイヤー | 「私はPlayerです」「私はEnemyです」 |
| Collision Mask | 自分が検出したい相手のLayer | 「私はWorldとEnemyを見ます」 |
Layerは名札です。プレイヤーなら Player レイヤー、敵なら Enemy レイヤー、壁なら World レイヤーに所属します。
Maskは探しに行く相手です。プレイヤーが壁にぶつかりたいなら、プレイヤーのMaskに World を入れます。プレイヤーの攻撃が敵だけに当たってほしいなら、攻撃判定のMaskに EnemyHurtBox を入れます。
インスペクタでは、同じ Collision セクションの中にLayerとMaskが並びます。上が自分の所属、下が見に行く相手です。

重要なのは、判定したい側のMaskが、相手のLayerを見ているか です。
PlayerAttackのMask -> EnemyHurtBoxのLayer
この関係ができていれば、PlayerAttack側はEnemyHurtBoxを検 出できます。反対に、EnemyHurtBox側のMaskがPlayerAttackを見ていなくても、PlayerAttack側のシグナルで処理する設計なら十分です。
両方にMaskを入れる必要があるのは、両方のオブジェクトがそれぞれ相手を検出したい場合です。例えば「敵もプレイヤーを検出し、プレイヤー側も敵を検出する」ような設計なら双方に設定します。攻撃判定のように、片方のArea2Dだけが反応すればよい場面では、検出する側のMaskを正しく設定することが先です。
まずレイヤーに名前を付ける
Godotのレイヤーは1番から32番まであります。初期状態では数字だけなので、開発が進むほど「3番って敵だっけ、アイテムだっけ」と迷いやすくなります。
最初に「プロジェクト設定」で名前を付けておくと、インスペクタのLayer/Maskが読みやすくなります。
- 「プロジェクト」から「プロジェクト設定」を開く
- 「レイヤー名」から「2D物理」を選ぶ
- 使うレイヤーに名前を付ける

たとえば2Dアクションなら、最初は次のような名前で十分です。
| 番号 | 名前 | 用途 |
|---|---|---|
| 1 | World | 床、壁、地形 |
| 2 | Player | プレイヤー本体 |
| 3 | Enemy | 敵本体 |
| 4 | PlayerHitBox | プレイヤーの攻撃判定 |
| 5 | EnemyHurtBox | 敵がダメージを受ける範囲 |
| 6 | EnemyHitBox | 敵の攻撃判定 |
| 7 | PlayerHurtBox | プレイヤーがダメージを受ける範囲 |
| 8 | Item | アイテム、コイン、回復薬 |
最初から32個を完璧に埋める必要はありません。むしろ、使いそうな主要カテゴリだけ決めて、後から必要になったものを追加するほうが破綻しにくいです。
アクションRPGのレイヤー設計例
Layer/Maskは、個別ノードの設定というより「ゲーム内の相互作用の表」です。どのオブジェクトが、どの相手を見るべきかを先に整理すると、設定ミスが減ります。

アクションRPGの一例として、次のように考えます。
| オブジェクト | Layer | Mask | 目的 |
|---|---|---|---|
| プレイヤー本体 | Player | World, Enemy | 壁に止められ、敵本体にも接触する |
| 敵本体 | Enemy | World, Player | 壁に止められ、プレイヤーと接触する |
| 壁、床、地形 | World | 必要に応じて空でもよい | 多くの場合、動く側がWorldを見る |
| プレイヤー攻撃 | PlayerHitBox | EnemyHurtBox | 敵のダメージ判定だけを検出する |
| 敵攻撃 | EnemyHitBox | PlayerHurtBox | プレイヤーのダメージ判定だけを検出する |
| アイテム | Item | Player | プレイヤーが入ったときだけ拾われる |
この表で大切なのは、敵本体 と 敵がダメージを受ける範囲 を分けていることです。
敵本体は移動や壁との衝突に使います。敵のHurtBoxは「攻撃を受けるためのセンサー」です。この2つを同じ判定にしてしまうと、プレイヤーの攻撃が敵の移動ボディに当たるのか、ダメージ用のArea2Dに当たるのかが曖昧になります。
小さなゲームなら本体だけでも動きます。しかし、ノックバック、盾、弱点部位、当たり判定の一時無効化などを入れ始めると、体と攻撃/被ダメージ判定を分けておくほうが圧倒的に管理しやすくなります。
実装例1: アイテムをプレイヤーだけが拾う
まずはシンプルな例です。回復薬やコインのようなアイテムは、敵や弾には反応せず、プレイヤーが入ったときだけ拾われてほしいはずです。
ノード構成は次のようにします。
PotionPickup (Area2D)
└─ CollisionShape2D
インスペクタでは、次のように設定します。
| 項目 | 設定 |
|---|---|
| Layer | Item |
| Mask | Player |
| Signal | body_entered を接続 |
Area2D は物理的に押し返すノードではありません。プレイヤーが範囲に入ったことを検出して、拾う処理を呼ぶセンサーです。
extends Area2D
@export var heal_amount := 20
func _ready() -> void:
# body_enteredはCharacterBody2DなどのPhysicsBody2Dが入ったときに発火します。
body_entered.connect(_on_body_entered)
func _on_body_entered(body: Node2D) -> void:
# Layer/MaskでPlayerだけに絞っていても、最後に役割を確認すると安全です。
if not body.is_in_group("player"):
return
# 実際の回復処理はプレイヤー側のメソッドに任せます。
body.heal(heal_amount)
queue_free()
ここでLayer/Maskとグループの役割が分かれます。
Layer/Maskは、Godotの物理エンジンに「そもそもPlayer以外は候補にしないで」と伝えるための設定です。グループは、スクリプト側で「このNodeはゲーム上のプレイヤーとして扱ってよいか」を確認するための保険です。
実装例2: 攻撃判定をHitBoxとHurtBoxに分ける
アクションゲームでは、攻撃する側の判定を HitBox、ダメージを受ける側の判定を HurtBox と分けることがよくあります。

プレイヤーの剣攻撃なら、次のような構成にします。
Player (CharacterBody2D)
├─ Sprite2D
├─ CollisionShape2D
└─ SwordHitBox (Area2D)
└─ CollisionShape2D
敵側には、ダメージを受けるためのArea2Dを置きます。
Enemy (CharacterBody2D)
├─ CollisionShape2D
└─ HurtBox (Area2D)
└─ CollisionShape2D
設定例は次のとおりです。
| ノード | Layer | Mask |
|---|---|---|
SwordHitBox | PlayerHitBox | EnemyHurtBox |
Enemy/HurtBox | EnemyHurtBox | 空でもよい |
攻撃判定を発生させる側が EnemyHurtBox を見に行くので、敵側のMaskは空でも成立します。もちろん、敵側でも「攻撃範囲に入られた」ことを検出したい設計なら、敵側のMaskにも PlayerHitBox を入れます。
extends Area2D
@export var damage := 10
func _ready() -> void:
# 攻撃判定はArea2D同士の重なりを見るのでarea_enteredを使います。
area_entered.connect(_on_area_entered)
func enable_for_one_swing() -> void:
# 攻撃していない間はShapeを無効にして、当たり続けないようにします。
$CollisionShape2D.disabled = false
await get_tree().create_timer(0.12).timeout
$CollisionShape2D.disabled = true
func _on_area_entered(area: Area2D) -> void:
# Layer/Maskで候補を絞ったあと、グループでゲーム上の役割を確認します。
if not area.is_in_group("enemy_hurtbox"):
return
# ダメージ処理はHurtBox側に受け渡すと、敵本体の構造を変えても壊れにくくなります。
area.apply_damage(damage)
この形にしてお くと、敵本体の CharacterBody2D は移動や壁との衝突に集中できます。ダメージを受ける範囲はHurtBoxが担当します。
たとえばボスの頭だけ弱点にしたい場合は、頭用のHurtBoxだけを enemy_weak_point グループに入れます。盾で前方攻撃だけ防ぐなら、盾用のHurtBoxを別Layerにするか、グループで受けるダメージを変えます。Layer/Maskで大まかに絞り、グループやメソッドでゲームルールを表現する、という分担が使いやすいです。
実装例3: 無敵時間だけ敵の攻撃を無視する
Layer/Maskは、実行中に切り替えることもできます。よくある例は、ダメージを受けた直後の無敵時間です。
プレイヤーのHurtBoxが通常は EnemyHitBox を検出し、無敵中だけ検出しないようにします。
extends Area2D
const LAYER_ENEMY_HITBOX := 6
var invincible := false
func start_invincible_time() -> void:
if invincible:
return
invincible = true
# 無敵中は敵の攻撃レイヤーをMaskから外し、攻撃Area2Dを検出しないようにします。
set_collision_mask_value(LAYER_ENEMY_HITBOX, false)
await get_tree().create_timer(1.0).timeout
# 無敵が終わったら、再び敵の攻撃を検出できる状態に戻します。
set_collision_mask_value(LAYER_ENEMY_HITBOX, true)
invincible = false
この例で変えているのは、プレイヤー本体の移動衝突ではなく、プレイヤーのHurtBox のMaskです。つまり「壁にぶつかる」「床に立つ」といった移動処理には影響させず、「ダメージ判定だけを一時的に無視する」設計にできます。
本体のCollision Maskを丸ごと変えると、無敵中に壁もすり抜ける、アイテムも拾えない、といった副作用が出ることがあります。何を一時的に無視したいのかを考え、その判定を担当しているArea2Dだけを切り替えるのが安全です。
コードからLayer/Maskを設定する
基本はインスペクタで設定するほうが読みやすいです。ただし、生成した弾や攻撃判定のように、スクリプトから初期化したい場面もあります。
Godotでは set_collision_layer_value() と set_collision_mask_value() を使うと、1番から32番のレイヤー番号をそのまま指定できます。
extends Area2D
const LAYER_WORLD := 1
const LAYER_PLAYER := 2
const LAYER_ENEMY := 3
func setup_as_player_bullet() -> void:
# 既存設定を消してから、弾に必要なLayer/Maskだけを入れます。
collision_layer = 0
collision_mask = 0
# この弾自身はPlayer側の判定として扱います。
set_collision_layer_value(LAYER_PLAYER, true)
# 弾は敵と壁だけを見る。味方やアイテムには反応しません。
set_collision_mask_value(LAYER_ENEMY, true)
set_collision_mask_value(LAYER_WORLD, true)
collision_layer と collision_mask は内部的にはビットマスクの整数です。慣れている人は 1 << 0 のようなビット演算で設定できますが、初心者のうちは set_collision_mask_value(3, true) のように番号で指定するほうが読み間違いにくいです。
ただし、番号だけを大量に書くと後から読めなくなります 。上の例のように const LAYER_ENEMY := 3 と名前を付けるか、インスペクタ中心で設定することをおすすめします。
反応しないときのチェックリスト
Layer/Maskの不具合は、原因が見た目だけでは分かりにくいです。「シグナルが来ない」「攻撃が当たらない」「アイテムを拾えない」ときは、順番に確認します。

| 確認項目 | 見る場所 | よくある原因 |
|---|---|---|
| Shapeがあるか | 子ノード | CollisionShape2D がない、または disabled がオン |
| Layerが合っているか | 相手側 | 相手が想定したLayerに所属していない |
| Maskが相手を見ているか | 検出する側 | Maskに相手Layerのチェックが入っていない |
| シグナルが接続されているか | Nodeタブ/コード | body_entered と area_entered を間違えている |
| 反応先のコードが正しいか | スクリプト | グループ名のtypo、メソッド名のtypo |
特に多いのは、body_entered と area_entered の取り違えです。
| 検出したい相手 | 使うシグナル |
|---|---|
CharacterBody2D, RigidBody2D, StaticBody2D | body_entered |
Area2D | area_entered |
アイテムがプレイヤー本体を検出するなら body_entered です。攻撃HitBoxが敵HurtBoxを検出するなら、Area2D同士なので area_entered です。
また、Layer/Maskが正しくても、シグナルの中で is_in_group("player") の名前を間違えていると処理は止まります。物理設定だけでなく、最後に実行されるコードまで確認するのが大切です。
おまけ: 先に知っておくと良いこと
Layer/Maskは物理の入口、グループはゲームルールの確認
Layer/Maskは、物理エンジンに「この組み合わせだけ判定して」と伝える入口です。ここで候補を絞ると、不要な判定やシグナルを減らせます。
一方で、Layerだけでゲーム内の意味をすべて表現しようとすると、すぐにレイヤーが足りなくなります。たとえば敵にはスライム、ゴブリン、ボス、召喚物などがいますが、それらを全部別Layerにすると管理が重くなります。
この場合は、物理的には同じ EnemyHurtBox Layerにまとめ、スクリプト側で enemy, boss, flying_enemy のようなグループやプロパティを確認します。
func _on_area_entered(area: Area2D) -> void:
if not area.is_in_group("enemy_hurtbox"):
return
# ボスだけはダメージ倍率を変える、というゲームルールはコード側で判断します。
var final_damage := damage
if area.is_in_group("boss"):
final_damage *= 0.5
area.apply_damage(final_damage)
Layer/Maskは「物理的に候補にするか」、グループは「ゲーム上どう扱うか」です。この分担にすると、レイヤー設計がシンプルになります。
レイヤーを細かくしすぎない
初心者のうちは、プレイヤー、敵、弾、アイテム、壁など、見える種類ごとにどんどんLayerを分けたくなります。しかし、Layerは増やすほど設定表も複雑になります。
判断基準は「物理的に別の相手を見たいか」です。
たとえば赤スライムと青スライムが同じようにプレイヤーの攻撃を受けるなら、同じ EnemyHurtBox で十分です。色や属性の違いは、グループやスクリプトのプロパティで扱います。
逆に、プレイヤーの攻撃と敵の攻撃は分ける価値があります。プレイヤーの攻撃がプレイヤーに当たってほしくない、敵の攻撃が敵に当たってほしくない、というように判定対象が違うからです。
迷ったら相互作用表を作る
Layer/Maskの設定で混乱したら、先に表を書きます。
何が反応する? 何に反応する?
PlayerHitBox -> EnemyHurtBox
EnemyHitBox -> PlayerHurtBox
ItemArea -> Player
EnemySight -> Player
この矢印の左側が、主にMaskを持つ側です。右側がLayerとして見られる側です。矢印を文章で説明できない設定は、あとで高確率でバグになります。
まとめ
Collision Layerは「自分は何者か」、Collision Maskは「何を見に行くか」です。
この2つを使うと、プレイヤー、敵、壁、攻撃判定、アイテム取得の関係をインスペクタ上で整理できます。特に攻撃判定では、本体、HitBox、HurtBoxを分けると、移動とダメージ処理が混ざりにくくなります。
反応しないときは、Shape、Layer、Mask、シグナル、グループ名の順に確認してください。Layer/Maskは難しい設定に見えますが、相互作用表を作ってから設定すれば、かなり安定して扱えるようになります。