Godot 4のコンポーネント指向開発シリーズ、今回は敵AIの基礎となる**「TargetFollower (ターゲット追尾)」**です。

ゾンビ、追跡ミサイル、ペットなど、「何かを追いかける動き」を実装する際、毎回移動コードを書くのは大変です。このコンポーネントを使えば、アタッチするだけで勝手にターゲットを追いかけるオブジェクトを作成できます。


このコンポーネントは、シーン内から「特定のグループ(例: player)」に所属するノードを自動で探し出し、その場所に向かって親ノードを移動させます。

1. コンポーネントのコード (Full Code)

以下のコードをコピーして、TargetFollower.gd という名前で保存してください。

class_name TargetFollower
extends Node

## 指定したグループの対象を自動で追尾するコンポーネント
## 親ノードは CharacterBody2D を想定しています。

# --- 設定パラメータ ---
@export_group("AI Settings")
@export var target_group: String = "player"  ## 追尾対象が所属しているグループ名
@export var speed: float = 100.0             ## 移動速度
@export var stop_distance: float = 20.0      ## ターゲットの手前で止まる距離(重なり防止)

# --- 内部変数 ---
var _parent: CharacterBody2D
var _target: Node2D = null

func _ready() -> void:
	# 親ノードの取得
	_parent = get_parent() as CharacterBody2D
	if not _parent:
		push_error("TargetFollower: 親が CharacterBody2D ではありません。")
		set_physics_process(false)
		return

	# ターゲットの検索 (シーン読み込み完了後に行うため少し待つ)
	await get_tree().process_frame
	_find_target()

func _physics_process(_delta: float) -> void:
	# ターゲットがいなければ何もしない(または再検索する)
	if not is_instance_valid(_target):
		return

	# ターゲットへのベクトル(方向と距離)を計算
	var direction_vector = _target.global_position - _parent.global_position
	var distance = direction_vector.length()

	# 指定距離より遠ければ移動する
	if distance > stop_distance:
		# 正規化(長さを1にする)して速度を掛ける
		_parent.velocity = direction_vector.normalized() * speed
		_parent.move_and_slide()
		
		# (オプション) 進行方向を向く場合は以下を有効化
		# _parent.look_at(_target.global_position)
	else:
		# 近すぎる場合は止まる
		_parent.velocity = Vector2.ZERO

func _find_target() -> void:
	# 指定したグループに所属するノードのリストを取得
	var targets = get_tree().get_nodes_in_group(target_group)
	
	if targets.size() > 0:
		# とりあえず最初の1人(プレイヤー)をターゲットにする
		_target = targets[0] as Node2D
	else:
		# 見つからなかった場合(プレイヤーが死んでいる等)
		# print_debug("TargetFollower: ターゲットが見つかりません")
		pass

2. 使い方チュートリアル

このコンポーネントを動かすには、「追いかける側(敵)」と「追いかけられる側(プレイヤー)」の準備が必要です。Godotの便利な機能**「グループ」**を使用します。

手順①:プレイヤー(ターゲット)の準備

まず、追いかけられる対象を作ります。

  1. プレイヤーのシーン(またはノード)を開きます。
  2. ノードタブの 「グループ (Groups)」 を選択します。
  3. player と入力して追加ボタンを押します。
    • これで、このノードに「player」というタグが付きました。

手順②:敵(親ノード)の作成

  1. 新しいシーンを作成し、ルートノードを CharacterBody2D にします(名前は Enemy など)。
  2. CollisionShape2DSprite2D を追加して、敵の見た目を作ります。

手順③:コンポーネントのアタッチ

  1. Enemy ノードの子として Node を追加し、名前を TargetFollower にします。
  2. 先ほどのスクリプト TargetFollower.gd をアタッチします。
  3. インスペクター設定を確認します:
    • Target Group: player (手順1で付けた名前と同じか確認)
    • Speed: 100 (適当な速さ)

シーン構成図:

Enemy (CharacterBody2D)
 ├── Sprite2D (敵の画像)
 ├── CollisionShape2D
 └── TargetFollower (Node)  <-- これが自動操縦エンジンになります

手順④:実行

メインシーンに「プレイヤー」と「敵」の両方を配置して実行してください。

敵が一直線にプレイヤーに向かって動き出し、近づくとピタリと止まれば成功です!


3. このコンポーネントの活用・発展

複数の敵を一瞬で作る

この Enemy シーンを保存しておけば、メインシーンに10体でも20体でも配置するだけで、全員がプレイヤーを追いかけてくる「大量発生イベント」がすぐに作れます。コードをコピー&ペーストする必要はありません。

壁を避けるには? (NavigationAgent2D)

今回のコードは「壁があっても無視して直進(または壁に引っかかり続ける)」という単純なものです。

もし迷路のようなステージで壁を避けて追いかけさせたい場合は、Godot標準の NavigationAgent2D ノードと組み合わせるのが正解です。

このコンポーネントを改造して、NavigationAgent2D を使うようにすれば、「賢い追尾AI」にアップグレードできます。

ターゲットの切り替え

_find_target() のロジックを変えれば、挙動を変化させられます。

  • 一番近い敵を狙う: targets 配列の中身をループして、距離が最も近いノードを _target に設定する。
  • ランダムに狙う: targets.pick_random() を使用する。

このように、「誰を狙うか」のロジックと**「どう動くか」のロジック**をこのコンポーネント内に閉じ込めておけるのがメリットです。