概要
Godotのグループは、任意のノードに付与できるタグシステムです。UnityのTagと似ていますが、1つのノードに複数のグループを設定できるため、より柔軟な分類が可能です。
最初は「敵かどうか」を判定するのに、クラス名やノード名で判定していました。しかし、敵の種類が増えるにつれてコードが複雑になり、「もっとシンプルな方法はないか?」と悩んでいた時にグループ機能を発見しました。それ以来、グループは私のゲーム開発に欠かせない機能になりました。

グループの特徴
- 複数設定可能: 1つのノードに複数のグループを付与
- 動的変更: 実行時にグループの追加・削除が可能
- 高速検索: グループ内の全ノードを効率的に取得
- 型に依存しない: 異なる型のノードも同じグループに
設定方法
インスペクターから設定
- ノードを選択
- Node タブ → Groups
- グループ名を入力して追加
コードから設定
func _ready():
# グループに追加
add_to_group("enemies")
add_to_group("damageable")
# グループから削除
remove_from_group("invincible")
基本的な使い方
グループの判定
# 攻撃判定の例
func _on_sword_area_body_entered(body: Node2D):
if body.is_in_group("enemies"):
body.take_damage(attack_power)
elif body.is_in_group("pushable"):
push_object(body)
elif body.is_in_group("breakable"):
body.destroy()
グループ内の全ノードを取得
# 全ての敵を取得
var all_enemies = get_tree().get_nodes_in_group("enemies")
for enemy in all_enemies:
enemy.alert_player_position(player.global_position)
# グループ内のノード数を確認
var enemy_count = get_tree().get_nodes_in_group("enemies").size()
実践的な活用例
複数グループを活用した敵の分類
# Slime.gd
func _ready():
add_to_group("enemies")
add_to_group("slimes")
add_to_group("weak_to_fire")
# Boss.gd
func _ready():
add_to_group("enemies")
add_to_group("bosses")
add_to_group("immune_to_stun")
# 攻撃処理
func process_fire_attack(target):
if target.is_in_group("weak_to_fire"):
damage *= 2.0 # 火属性弱点
if not target.is_in_group("immune_to_stun"):
target.apply_stun()
インタラクション可能オブジェクトの管理
# InteractionSystem.gd
func check_nearby_interactables():
var interactables = []
for body in interaction_area.get_overlapping_bodies():
if body.is_in_group("interactable"):
interactables.append(body)
# 最も近いオブジェクトをハイライト
if interactables.size() > 0:
var closest = find_closest(interactables)
closest.show_interaction_prompt()
一括処理の実装
# GameManager.gd
func pause_all_enemies():
for enemy in get_tree().get_nodes_in_group("enemies"):
enemy.set_physics_process(false)
func damage_all_in_radius(position: Vector2, radius: float, damage: int):
for node in get_tree().get_nodes_in_group("damageable"):
if node.global_position.distance_to(position) <= radius:
node.take_damage(damage)
推奨されるグループ名
グループ名 | 用途 | ノード例 |
---|---|---|
enemies | 敵キャラクター | Slime, Goblin, Boss |
players | プレイヤー | Player, PlayerCompanion |
interactable | 調べられるオブジェクト | NPC, Chest, Door |
damageable | ダメージを受けるもの | Enemy, Player, Crate |
pushable | 押せるオブジェクト | Box, Boulder |
collectible | 収集アイテム | Coin, Heart, Key |
グループ vs 型チェック
# 型チェック(特定のクラスのみ)
if body is Enemy:
body.take_damage(10)
# グループチェック(より柔軟)
if body.is_in_group("enemies"):
body.take_damage(10)
グループの利点:
- 継承関係に依存しない
- 実行時に動的に変更可能
- 複数の分類を同時に持てる
パフォーマンスの考慮
# 毎フレーム実行は避ける
func _process(delta):
# 非効率
var enemies = get_tree().get_nodes_in_group("enemies")
# キャッシュして使用
var enemy_nodes = []
func _ready():
enemy_nodes = get_tree().get_nodes_in_group("enemies")
# ノードの追加・削除時に更新する仕組みを作る
注意点
- グループ名は大文字小文字を区別
- スペルミスに注意(定数化を推奨)
- 削除されたノードは自動的にグループから除外
実装して学んだこと
グループ機能の最大の魅力は、複数のグループを同時に持てることです。例えば、スライムは「enemies」「slimes」「weak_to_fire」という3つのグループに属しています。これにより、「全ての敵」「スライムのみ」「火属性に弱いもの」といった異なる角度からの処理が可能になります。
パフォーマンス面での注意点も学びました。get_tree().get_nodes_in_group()
は便利ですが、毎フレーム実行するとパフォーマンスに影響が出ます。必要に応じてキャッシュしたり、シグナルで更新する仕組みを作ると良いです。