アイテムに近づいたら拾う。敵の索敵範囲に入ったら追いかけられる。毒沼に立っている間だけHPが減る。イベントエリアに入ったら会話が始まる。
こうした「ぶつかって止めたいわけではないけれど、範囲に入ったことは知りたい」処理に使うのが Area2D です。

この記事でわかること
- Area2Dが「通り抜けるセンサー」である理由
body_entered/body_exited/area_enteredの使い分け- 敵の索敵範囲、毒沼、アイテム取得の実装例
- Collision Layer / Maskで検出対象を絞る考え方
Area2Dは「衝突」ではなく「検出」
Area2D は、物理的に押し返すためのノードではありません。キャラクターがArea2Dへ入っても、Area2D自体は壁のように動きを止めません。
| ノード | 主な役割 | プレイヤーの移動を止めるか |
|---|---|---|
StaticBody2D | 床、壁、固定障害物 | 止める |
CharacterBody2D | プレイヤー、敵、NPC | 動かし方次第で衝突する |
RigidBody2D | 箱、ボール、物理オブジェクト | 物理的に押し合う |
Area2D | センサー、範囲、トリガー | 止めない |
Area2Dは、ゲーム内では次のような用途に向いています。
- アイテム取得範囲
- 敵の索敵範囲
- 攻撃判定
- 毒沼や回復エリア
- ワープゾーン
- 会話やカットシーン開始トリガー
「触れたら反応したい」ならArea2D、「ぶつかって止めたい」なら物理ボディ、と考えると選びやすいです。
Area2Dの基本構成
Area2Dを機能させるには、子に CollisionShape2D または CollisionPolygon2D が必要です。Area2D本体だけを置いても、検出範囲がないため反応しません。
PickupArea (Area2D)
└─ CollisionShape2D
基本の流れは次の通りです。
Area2Dを作る- 子に
CollisionShape2Dを追加する - 検出したい範囲の形を設定する
body_enteredなどのシグナルを接続する- 必要ならCollision Layer / Maskを設定する
Area2Dは見えない範囲として使うことが多いため、エディタ上でCollisionShape2Dの形を見ながら調整します。
bodyとareaのシグナルを使い分ける
Area2Dで最初に迷いやすいのが、body_entered と area_entered の違いです。
| シグナル | 何が入ったときに呼ばれるか | 例 |
|---|---|---|
body_entered(body) | CharacterBody2D、RigidBody2D、StaticBody2D など | プレイヤーがアイテム範囲に入る |
body_exited(body) | 物理ボディが範囲から出る | プレイヤーが索敵範囲から出る |
area_entered(area) | 別の Area2D | 攻撃判定が敵の HurtBox に当たる |
area_exited(area) | 別の Area2D が範囲から出る | センサー同士の重なり終了 |
Area2Dのシグナルは、入った瞬間と出た瞬間を扱うのに向いています。範囲内にいる間ずっと処理したい場合は、入った対象を配列に入れ、出たら配列から外し、_physics_process() で処理する形が分かりやすいです。

extends Area2D
var targets: Array[Node2D] = []
func _on_body_entered(body: Node2D) -> void:
if body.is_in_group("player") and not targets.has(body):
# 入った瞬間にリストへ追加する
targets.append(body)
func _on_body_exited(body: Node2D) -> void:
if targets.has(body):
# 出た瞬間にリストから外す
targets.erase(body)
この形にすると、「入った瞬間の処理」と「範囲内にいる間の処理」を分けて考えられます。
実装例1: 敵の索敵範囲
敵の子に Area2D を置くと、索敵範囲を作れます。
Enemy (CharacterBody2D)
├─ Sprite2D
├─ CollisionShape2D
└─ SightArea (Area2D)
└─ CollisionShape2D
SightArea は敵本体の衝突とは別です。プレイヤーが索敵範囲に入っても、そこで止まるわけではありません。入ったことを検出して、敵AIの状態を変えます。

# enemy.gd
extends CharacterBody2D
@onready var sight_area: Area2D = $SightArea
var target: Node2D = null
var candidates: Array[Node2D] = []
func _physics_process(delta: float) -> void:
if candidates.is_empty():
target = null
return
# 範囲内に複数いる場合は、毎フレーム一番近い対象を選ぶ
target = find_closest_target()
if target != null:
chase_target(target, delta)
func _on_sight_area_body_entered(body: Node2D) -> void:
if body.is_in_group("player") and not candidates.has(body):
# 索敵範囲に入った候補を覚える
candidates.append(body)
func _on_sight_area_body_exited(body: Node2D) -> void:
if candidates.has(body):
# 範囲外に出たら候補から外す
candidates.erase(body)
func find_closest_target() -> Node2D:
var closest: Node2D = null
var closest_distance_sq := INF
for candidate in candidates:
if not is_instance_valid(candidate):
continue
var distance_sq := global_position.distance_squared_to(candidate.global_position)
if distance_sq < closest_distance_sq:
closest_distance_sq = distance_sq
closest = candidate
return closest
func chase_target(target_body: Node2D, delta: float) -> void:
var direction := global_position.direction_to(target_body.global_position)
velocity = direction * 120.0
move_and_slide()
この例では distance_squared_to() を使っています。距離比較だけなら平方根を取る必要がないため、distance_to() より軽く済みます。とはいえ、数体の敵なら大きな差は気にしなくて大丈夫です。大事なのは、Area2Dが敵の移動を止めるのではなく、AIへ「見つけた」と知らせる役割を持つことです。
実装例2: 毒沼の継続ダメージ
毒沼や炎上床のように、範囲内にいる間ずっと効果を与えたい場合があります。
この場合、body_entered だけでは足りません。body_entered は入った瞬間だけ呼ばれるためです。

小規模なエリアなら、get_overlapping_bodies() を使うと分かりやすいです。
# damage_area.gd
extends Area2D
@export var damage_per_second := 8.0
func _physics_process(delta: float) -> void:
for body in get_overlapping_bodies():
if body.is_in_group("player") and body.has_method("take_damage"):
# 1秒あたりのダメージをdeltaで割り振る
body.take_damage(damage_per_second * delta)
このコードは読みやすく、毒沼が数個ある程度なら十分です。
一方で、同じようなDamageAreaが大量にある場合は、毎フレーム get_overlapping_bodies() を呼ぶより、入った/出た対象をリスト管理する方が見通しも性能も安定しやすいです。
extends Area2D
@export var damage_per_second := 8.0
var bodies_inside: Array[Node2D] = []
func _physics_process(delta: float) -> void:
for body in bodies_inside:
if is_instance_valid(body) and body.has_method("take_damage"):
# 範囲内にいる間だけ継続ダメージを与える
body.take_damage(damage_per_second * delta)
func _on_body_entered(body: Node2D) -> void:
if body.is_in_group("player") and not bodies_inside.has(body):
bodies_inside.append(body)
func _on_body_exited(body: Node2D) -> void:
bodies_inside.erase(body)
どちらが正解というより、用途で選びます。小さなプロトタイプでは get_overlapping_bodies()、多くの範囲が出るゲームではリスト管理、という考え方で始めるとよいです。
実装例3: アイテム取得トリガー
アイテム取得は、Area2Dの最も分かりやすい使いどころです。
Coin (Node2D)
├─ Sprite2D
└─ PickupArea (Area2D)
└─ CollisionShape2D
# coin.gd
extends Node2D
@export var score_value := 10
@onready var pickup_area: Area2D = $PickupArea
func _on_pickup_area_body_entered(body: Node2D) -> void:
if not body.is_in_group("player"):
return
if body.has_method("add_score"):
# 取得した相手へスコア加算を依頼する
body.add_score(score_value)
queue_free()
このように、Area2Dは「プレイヤーが範囲 に入った」ことだけを検出します。スコア加算をプレイヤーに持たせるのか、Autoloadの PlayerData.add_score() に寄せるのかは、プロジェクト設計に合わせて選びます。
レイヤー/マスクで検出対象を絞る
Area2Dが反応しない、または関係ない敵やアイテムに反応してしまう場合、Collision Layer / Maskの設定を見直します。
基本は次の関係です。
検出する側の Mask が、検出される側の Layer を見ていると反応する。

たとえば、プレイヤーだけを検出したい索敵範囲なら、プレイヤーを Player Layer に置き、SightArea のMaskを Player Layer だけにします。
この設定をしておくと、Godotの物理側で不要な検出を減らせます。コード内で is_in_group("player") を書くこともありますが、レイヤー/マスクで先に絞っておくと、そもそも余計なシグナルが飛びにくくなります。
monitoringとmonitorable
Area2Dには monitoring と monitorable もあります。
| プロパティ | 意味 | 例 |
|---|---|---|
monitoring | このArea2Dが他を検出するか | 敵の索敵範囲はtrue |
monitorable | このArea2Dが他のArea2Dから検出される対象になるか | HurtBoxやアイテム判定 |
多くの初心者向け実装では、まずデフォルトのままでも動きます。うまく動くようになってから、不要な検出を減らしたい場面で見直すとよいです。
よくあるつまずき
CollisionShape2Dを付け忘れる
Area2D本体だけでは範囲がありません。子に CollisionShape2D を置き、Shapeを設定してください。
body_enteredが呼ばれない
まず次を確認します。
monitoringが有効か- Area2DのMaskが、相手のLayerを見ているか
- 相手が
CharacterBody2DやRigidBody2Dなどのbodyか CollisionShape2Dが無効化されていないか
相手もArea2Dなら、body_entered ではなく area_entered を使います。
範囲内にいる間の処理をbody_enteredだけで書く
body_entered は入った瞬間だけです。毒沼や回復エリアのように継続処理が必要なら、get_overlapping_bodies() または入退場シグナルで管理した配列を _physics_process() で処理します。
Area2Dを壁として使おうとする
Area2Dは移動を止めません。壁や床として使いたいなら StaticBody2D、動くキャラクターなら CharacterBody2D を使います。
おまけ: 先に知っておくと良いこと
HitBoxとHurtBoxを分けると攻撃判定が整理し やすい
アクションゲームでは、攻撃する範囲を HitBox、ダメージを受ける範囲を HurtBox として、どちらもArea2Dで作ることがあります。
Player
├─ HurtBox (Area2D)
└─ SwordHitBox (Area2D)
この形にすると、体に当たったか、剣に当たったか、敵の攻撃範囲に入ったかを分けやすくなります。後続のCollision Layers/Masks記事と組み合わせると、味方の攻撃は味方に当たらない、敵の攻撃だけ受ける、といった設定が作りやすくなります。
RayCast2DやShapeCast2Dとの違い
Area2Dは「範囲に入ったもの」を検出します。視線の先に壁があるか、地面があるか、直線上の最初の対象を知りたいなら RayCast2D が向いています。
| ノード | 向いている判定 |
|---|---|
Area2D | 円や四角の範囲に入ったもの |
RayCast2D | 直線上にある最初のもの |
ShapeCast2D | 形状を動かした先で当たるもの |
敵の大まかな索敵範囲はArea2D、壁越しに見えているかの確認はRayCast2D、というように組み合わせることもあります。
まとめ
- Area2D は、移動を止める壁ではなく、通り抜けるセンサー
- 物理ボディを検出するなら
body_entered/body_exited - Area2D同士を検出するなら
area_entered/area_exited - 範囲内にいる間の処理は、
get_overlapping_bodies()またはリスト管理で行う - 反応しないときは
CollisionShape2D、Layer/Mask、monitoringを確認する
Area2Dを使いこなすと、ゲーム世界が「近づいたら反応する」「範囲に入ったら変化する」ようになります。アイテム、敵AI、攻撃判定、イベント開始など、かなり多くの仕組みの土台になるノードです。