概要
Godotでゲーム開発を進める中で、「すべての敵に同じ命令を送りたい」「プレイヤーの攻撃が当たったのが『敵』なのか『アイテム』なのか判定したい」といった場面に遭遇します。ノードの親子関係やクラス名だけでオブジェクトを管理しようとすると、コードが複雑化し、保守性を低下させる原因となります。
これらの課題をエレガントに解決するのが、Godotのグループ (Groups) 機能です。
グループとは?柔軟な「タグ付け」システム

グループは、シーンツリーの階層やノードの種類とは独立して、ノードに仮想的な 「タグ」 を付けるためのシステムです。例えば、CharacterBody2Dのノードに「enemies」や「players」というタグを付けたり、Area2Dに「collectible_items」というタグを付けたりできます。
グループの主なメリット:
- 柔軟な分類: 親子関係に縛られず、「役割」や「属性」といった意味的なまとまりでノードを分類できます。
- 一括操作: 「enemies」グループに属する全てのノードを取得し、同じメソッドを呼び出すといった処理が簡単に記述できます。
- 疎結合な設計: ノード同士が直接互いを参照しなくても、グループという共通の関心事を通じて連携できます。
グループの設定方法:エディタとコード
グループへの追加は、主に2つの方法で行います。
1. エディタで設定する(静的なノード向け)
シーンに配置済みのノードにグループを設定する最も手軽な方法です。
- シーンツリーで対象のノードを選択します。
- インスペクタの隣にある「ノード」タブを開きます。
- 「グループ」サブタブを選択します。
- テキストボックスにグループ名(例:
enemies)を入力し、「追加」ボタンを押します。
2. コードで設定する(動的なノード向け)
ゲーム中に動的に生成されるノード(弾、エフェクトなど)をグループに追加する場合に用います。
# Bullet.gd
func _ready():
# このノード(弾)を "player_bullets" グループに追加する
add_to_group("player_bullets")
# ターゲットにヒットしたらグループから抜ける(任意)
func _on_hit_target():
# 処理が不要になったらグループから削除
remove_from_group("player_bullets")
queue_free()
実践的な活用シナリオとコード例
シナリオ1:柔軟な衝突判定システム
is_in_group()は、衝突した相手が何者かを判定する際の常套手段です。if body.name == "..."のようなハードコーディングを避け、拡張性の高いコードを書きましょう。
# PlayerAttackArea.gd (Area2D)
# プレイヤーの剣の当たり判定エリア
func _on_body_entered(body: Node):
# 衝突相手が "enemies" グループに属しているか?
if body.is_in_group("enemies") and body.has_method("take_damage"):
body.take_damage(attack_power)
print("敵にヒット!")
# 衝突相手が "destructible_objects" グループに属しているか?
elif body.is_in_group("destructible_objects") and body.has_method("destroy"):
body.destroy()
print("オブジェクトを破壊!")
このコードの美点は、PlayerAttackAreaが攻撃対象の具体的なクラス(SlimeやGoblinなど)を一切知る必要がない点です。新しい敵を追加する際も、このスクリプトを修正する必要はなく、新しいノードを適切なグループに追加するだけで済みます。
シナリオ2:グループへの一括命令(ブロードキャスト)
get_tree().call_group()を使えば、特定のグループに属する全ノードのメソッドを一斉に呼び出せます。
注意:
call_group()で指定したメソッドが対象ノードに存在しない場合、エラーにはなりませんが何も実行されません。グループ内のノードが確実に該当メソッドを持つようにするか、has_method()でチェックするパターンを検討してください。
# AlarmSystem.gd
# 警報システムが作動した
func _on_alarm_triggered():
print("警報作動!全警備員に通知します。")
# "guards" グループに属する全ノードの "enter_alert_mode" メソッドを呼び出す
get_tree().call_group("guards", "enter_alert_mode", get_player_last_known_position())
# Guard.gd (CharacterBody2D)
func enter_alert_mode(target_position: Vector2):
state = STATE.ALERT
nav_agent.target_position = target_position
print("%sが警戒モードに移行。目標地点: %s" % [name, target_position])
func _ready():
add_to_group("guards")
この例では、AlarmSystem