Godotのリストに戻る

【Godot】グループによる柔軟なオブジェクト判定

作成: 2025-06-20更新: 2025-06-20ノード・シーンブログ記事を読む

概要

Godotのグループは、任意のノードに付与できるタグシステムです。UnityのTagと似ていますが、1つのノードに複数のグループを設定できるため、より柔軟な分類が可能です。

最初は「敵かどうか」を判定するのに、クラス名やノード名で判定していました。しかし、敵の種類が増えるにつれてコードが複雑になり、「もっとシンプルな方法はないか?」と悩んでいた時にグループ機能を発見しました。それ以来、グループは私のゲーム開発に欠かせない機能になりました。

グループ設定画面

グループの特徴

  • 複数設定可能: 1つのノードに複数のグループを付与
  • 動的変更: 実行時にグループの追加・削除が可能
  • 高速検索: グループ内の全ノードを効率的に取得
  • 型に依存しない: 異なる型のノードも同じグループに

設定方法

インスペクターから設定

  1. ノードを選択
  2. Node タブ → Groups
  3. グループ名を入力して追加

コードから設定

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()は便利ですが、毎フレーム実行するとパフォーマンスに影響が出ます。必要に応じてキャッシュしたり、シグナルで更新する仕組みを作ると良いです。