Przeciwnik, który goni gracza, to jedna z najczęściej tworzonych mechanik w pierwszych projektach Godot. Taki przeciwnik może być szkieletem, potworem, robotem, strażnikiem albo dowolną inną postacią, która wykrywa gracza i porusza się w jego stronę.

W tym przykładzie stworzymy prostą wersję AI przeciwnika dla gry 2D. Przeciwnik będzie wykrywał gracza w określonym obszarze, a następnie będzie poruszał się w jego kierunku.
Przykładowa struktura sceny
Enemy (CharacterBody2D)
├── Sprite2D
├── CollisionShape2D
└── DetectionArea (Area2D)
└── CollisionShape2D
Enemy jest typu CharacterBody2D, ponieważ będzie poruszany skryptem i będzie korzystał z kolizji. DetectionArea odpowiada za wykrycie gracza.
Ustawienie grupy dla gracza
Najwygodniej oznaczyć gracza grupą player. W Godot można to zrobić w edytorze, w zakładce Node → Groups. Dodaj tam grupę:
player
Dzięki temu przeciwnik nie musi sprawdzać nazwy node’a. Wystarczy, że sprawdzi, czy wykryty obiekt należy do grupy player.
Skrypt Enemy.gd
extends CharacterBody2D
@export var speed: float = 120.0
var target: Node2D = null
func _ready() -> void:
$DetectionArea.body_entered.connect(_on_detection_area_body_entered)
$DetectionArea.body_exited.connect(_on_detection_area_body_exited)
func _physics_process(_delta: float) -> void:
if target == null:
velocity = Vector2.ZERO
move_and_slide()
return
var direction: Vector2 = global_position.direction_to(target.global_position)
velocity = direction * speed
move_and_slide()
func _on_detection_area_body_entered(body: Node) -> void:
if body.is_in_group(“player”):
target = body
func _on_detection_area_body_exited(body: Node) -> void:
if body == target:
target = null
Jak działa ten kod?
Gdy gracz wejdzie do DetectionArea, przeciwnik zapisuje go jako cel w zmiennej target. Od tego momentu w każdej klatce fizyki obliczany jest kierunek do gracza. Następnie przeciwnik ustawia swoją prędkość i wykonuje move_and_slide().
Gdy gracz wyjdzie z obszaru wykrywania, zmienna target zostaje ustawiona na null, a przeciwnik przestaje się poruszać.
Jak ustawić zasięg wykrywania?
Zasięg wykrywania zależy od kształtu CollisionShape2D wewnątrz DetectionArea. Najczęściej używa się CircleShape2D. Im większy promień, tym szybciej przeciwnik zauważy gracza.
Warto pamiętać, że zasięg wykrywania nie powinien być tym samym co zasięg ataku. Przeciwnik może wykrywać gracza z daleka, ale atakować dopiero wtedy, gdy znajdzie się bardzo blisko.
Dodanie zatrzymania przy graczu
W obecnej wersji przeciwnik będzie próbował wejść dokładnie na pozycję gracza. Można dodać minimalny dystans zatrzymania:
@export var stop_distance: float = 24.0
func _physics_process(_delta: float) -> void:
if target == null:
velocity = Vector2.ZERO
move_and_slide()
return
var distance: float = global_position.distance_to(target.global_position)
if distance <= stop_distance:
velocity = Vector2.ZERO
move_and_slide()
return
var direction: Vector2 = global_position.direction_to(target.global_position)
velocity = direction * speed
move_and_slide()
Dzięki temu przeciwnik zatrzyma się przy graczu, zamiast cały czas próbować wejść w jego środek.
Co można dodać później?
- patrolowanie między punktami,
- powrót na pozycję startową,
- atak po wejściu w zasięg,
- animację biegu i postoju,
- różne typy przeciwników,
- reakcję na hałas,
- wykrywanie gracza tylko w polu widzenia.
Podsumowanie
Przeciwnik goniący gracza to bardzo dobra mechanika do nauki podstaw AI w Godot 4. Wystarczy CharacterBody2D, Area2D, sygnały wykrywania i proste obliczenie kierunku ruchu. Na tej podstawie można później budować bardziej zaawansowane zachowania przeciwników.
Poprzedni wpis:
