Giriş

Godot 4, NavigationServer2D/3D, NavigationRegion düğümleri ve NavigationAgent düğümleri üzerine inşa edilmiş sağlam bir navigasyon sistemine sahiptir. Bu, Godot 3'ün sistemine göre önemli bir gelişmedir — daha esnek, daha yüksek performanslı ve dinamik engeller ile birden fazla agent türü gibi karmaşık senaryolar için yapılandırması çok daha kolaydır.

Hızlı genel bakış: NavigationRegion, agentların nereye yürüyebileceğini tanımlar. NavigationAgent, tekil varlıklar için yol bulmayı yönetir. NavigationServer ise her şeyi arka planda yönetir.

Temel Kavramlar

  • NavigationRegion2D / NavigationRegion3D — Bir NavigationPolygon (2D) veya NavigationMesh (3D) kullanarak yürünebilir bir alan tanımlar. Birden fazla bölgeye sahip olabilirsiniz; sunucu bunları otomatik olarak birleştirir.
  • NavigationAgent2D / NavigationAgent3D — Yol bulmayı yönetmek için bir CharacterBody'ye eklenir. Bir sonraki yol konumunu hesaplar, kaçınmayı yönetir ve navigasyon tamamlandığında sinyaller yayar.
  • NavigationServer2D / NavigationServer3D — Tüm navigasyon verilerini yöneten singleton. Onunla doğrudan nadiren etkileşime girersiniz, ancak harita güncellemelerini, yol sorgularını ve bölge bağlantılarını yönetir.

2D Navigasyon Kurulumu

Adım 1: NavigationRegion2D Ekleyin

  1. Sahnenize bir NavigationRegion2D düğümü ekleyin.
  2. Inspector'da yeni bir NavigationPolygon kaynağı oluşturun.
  3. 2D düzenleyicide yürünebilir poligonu çizin. Poligon, agentların nereye yürüyebileceğini tanımlar.

TileMap entegrasyonu: Bir NavigationPolygon, TileMap verilerinden bake edilebilir. Döşemelerinizde TileSet içinde tanımlanmış navigasyon poligonları varsa, yalnızca bir NavigationRegion2D ekleyip bake edin — yürünebilir alanı döşeme düzeninizden otomatik olarak oluşturur.

Bir NavigationAgent2D Eklemek

CharacterBody2D'nizin bir alt düğümü olarak bir NavigationAgent2D ekleyin. İşte eksiksiz bir hareket betiği:

extends CharacterBody2D

@export var speed: float = 200.0
@onready var nav_agent: NavigationAgent2D = $NavigationAgent2D

func _ready() -> void:
    # Wait for the navigation map to be ready
    await get_tree().physics_frame

    nav_agent.path_desired_distance = 4.0
    nav_agent.target_desired_distance = 4.0

func set_target(target_pos: Vector2) -> void:
    nav_agent.target_position = target_pos

func _physics_process(delta: float) -> void:
    if nav_agent.is_navigation_finished():
        return

    var next_pos := nav_agent.get_next_path_position()
    var direction := global_position.direction_to(next_pos)
    velocity = direction * speed
    move_and_slide()

Önemli: NavigationServer'ın senkronize olması için bir fizik karesine ihtiyacı vardır. _ready() içinde ilk hedef konumunu ayarlamadan önce her zaman await get_tree().physics_frame çalıştırın, aksi takdirde agent geçerli bir yol bulamayabilir.

Önemli NavigationAgent2D Özellikleri

  • target_position — Agentın gitmek istediği yer
  • path_desired_distance — Agentın bir sonrakine ilerlemek için her yol noktasına ne kadar yakın olması gerektiği
  • target_desired_distance — Navigasyonun tamamlanmış sayılması için agentın hedefe ne kadar yakın olması gerektiği
  • max_speed — Kaçınma hesaplamaları için kullanılır (hareket kodunuzu sınırlamaz)

3D Navigasyon

3D navigasyon sistemi, yalnızca farklı düğüm türleriyle olmak üzere, 2D ile aynı şekilde çalışır:

  • NavigationRegion3D + NavigationMesh
  • NavigationAgent3D
  • NavigationServer3D
extends CharacterBody3D

@export var speed: float = 5.0
@onready var nav_agent: NavigationAgent3D = $NavigationAgent3D

func _ready() -> void:
    await get_tree().physics_frame
    nav_agent.path_desired_distance = 0.5
    nav_agent.target_desired_distance = 0.5

func set_target(target_pos: Vector3) -> void:
    nav_agent.target_position = target_pos

func _physics_process(delta: float) -> void:
    if nav_agent.is_navigation_finished():
        return

    var next_pos := nav_agent.get_next_path_position()
    var direction := global_position.direction_to(next_pos)
    velocity = direction * speed
    velocity.y -= 9.8 * delta  # Apply gravity
    move_and_slide()

Navigasyon Mesh'lerini Bake Etmek

3D'de, navigasyon mesh'ini genellikle elle çizmek yerine seviye geometrinizden bake edersiniz.

Bake Yapılandırması

NavigationMesh kaynağındaki önemli özellikler:

  • Agent Radius — Agentların duvarlardan ne kadar uzak durduğu. Daha büyük değerler daha temkinli yollar oluşturur.
  • Agent Height — Yürünebilir alanlar için minimum tavan yüksekliği.
  • Agent Max Climb — Agentların çıkabileceği maksimum basamak yüksekliği (örn. merdivenler).
  • Agent Max Slope — Derece cinsinden maksimum yürünebilir eğim açısı.
# Bake navigation mesh at runtime
var nav_region: NavigationRegion3D = $NavigationRegion3D
nav_region.bake_navigation_mesh()

# Wait for baking to complete
await nav_region.bake_finished
print("Navigation mesh baked!")

Navigasyon Katmanları

Navigasyon katmanları, farklı agent türleri için yürünebilir alanları ayırmanıza olanak tanır. Örneğin, kara birimleri ile uçan birimler farklı navigasyon mesh'lerine sahip olabilir.

# Set navigation layers on the agent
nav_agent.set_navigation_layer_value(1, true)   # Ground layer
nav_agent.set_navigation_layer_value(2, false)  # Not flying layer

# Set navigation layers on the region
nav_region.set_navigation_layer_value(1, true)   # This region is for ground
nav_region.set_navigation_layer_value(2, false)  # Not for flying

Tipik bir katman yapılandırması:

  • Layer 1 — Kara birimleri (askerler, araçlar)
  • Layer 2 — Uçan birimler (dronlar, kuşlar)
  • Layer 3 — Büyük birimler (yalnızca geniş koridorlar)

Kaçınma (Avoidance)

NavigationAgent, agentların üst üste binmesini önlemek için yerleşik yerel kaçınmaya sahiptir. Kaçınma etkinleştirildiğinde, agent diğer agentlardan uzaklaşan güvenli bir hız hesaplar.

extends CharacterBody2D

@export var speed: float = 200.0
@onready var nav_agent: NavigationAgent2D = $NavigationAgent2D

func _ready() -> void:
    await get_tree().physics_frame
    nav_agent.avoidance_enabled = true
    nav_agent.radius = 20.0
    nav_agent.max_speed = speed
    nav_agent.velocity_computed.connect(_on_velocity_computed)

func set_target(target_pos: Vector2) -> void:
    nav_agent.target_position = target_pos

func _physics_process(delta: float) -> void:
    if nav_agent.is_navigation_finished():
        return

    var next_pos := nav_agent.get_next_path_position()
    var direction := global_position.direction_to(next_pos)
    # Set the desired velocity — the agent will compute a safe one
    nav_agent.velocity = direction * speed

func _on_velocity_computed(safe_velocity: Vector2) -> void:
    velocity = safe_velocity
    move_and_slide()

Nasıl çalışır: Hızı doğrudan kullanmak yerine, nav_agent.velocity'yi istediğiniz hıza ayarlarsınız. Ardından agent, velocity_computed sinyali aracılığıyla yakındaki agentlardan kaçınan bir safe_velocity hesaplar. Bu güvenli hızı geri çağırma (callback) içinde uygularsınız.

Dinamik Navigasyon

Kapıları, yıkılabilir duvarları veya değişen araziyi ele almak için navigasyon mesh'ini çalışma zamanında değiştirebilirsiniz:

# Enable/disable a navigation region (e.g., open/close a door)
var door_region: NavigationRegion2D = $DoorNavigationRegion
door_region.enabled = false  # Block this path
door_region.enabled = true   # Open this path

# Add a navigation obstacle (blocks agent paths)
var obstacle := NavigationObstacle2D.new()
obstacle.radius = 30.0
add_child(obstacle)

# Re-bake after level changes (3D)
nav_region.bake_navigation_mesh()
await nav_region.bake_finished

Performans notu: Çalışma zamanında bir 3D navigasyon mesh'ini bake etmek maliyetlidir. 3D'deki dinamik engeller için yeniden bake etmek yerine NavigationObstacle3D kullanmayı tercih edin. 2D'de NavigationRegion2D.enabled'ı düşük maliyetle açıp kapatabilirsiniz.

Yaygın Desenler

Oyuncuyu Takip Eden Düşman Yapay Zekâsı

extends CharacterBody2D

@export var speed: float = 150.0
@export var chase_range: float = 300.0
@onready var nav_agent: NavigationAgent2D = $NavigationAgent2D
var player: Node2D

func _ready() -> void:
    await get_tree().physics_frame
    player = get_tree().get_first_node_in_group("player")

func _physics_process(delta: float) -> void:
    if not player:
        return

    var distance := global_position.distance_to(player.global_position)
    if distance > chase_range:
        return  # Too far, don't chase

    # Update target every frame (or throttle to every N frames)
    nav_agent.target_position = player.global_position

    if nav_agent.is_navigation_finished():
        return

    var next_pos := nav_agent.get_next_path_position()
    var direction := global_position.direction_to(next_pos)
    velocity = direction * speed
    move_and_slide()

NPC Devriye Rotaları

extends CharacterBody2D

@export var speed: float = 100.0
@export var patrol_points: Array[Vector2] = []
@onready var nav_agent: NavigationAgent2D = $NavigationAgent2D
var current_patrol_index: int = 0

func _ready() -> void:
    await get_tree().physics_frame
    if patrol_points.size() > 0:
        nav_agent.target_position = patrol_points[0]

func _physics_process(delta: float) -> void:
    if patrol_points.size() == 0:
        return

    if nav_agent.is_navigation_finished():
        # Move to next patrol point
        current_patrol_index = (current_patrol_index + 1) % patrol_points.size()
        nav_agent.target_position = patrol_points[current_patrol_index]
        return

    var next_pos := nav_agent.get_next_path_position()
    var direction := global_position.direction_to(next_pos)
    velocity = direction * speed
    move_and_slide()

Tıkla-ve-Hareket Et (Top-Down)

extends CharacterBody2D

@export var speed: float = 200.0
@onready var nav_agent: NavigationAgent2D = $NavigationAgent2D

func _ready() -> void:
    await get_tree().physics_frame

func _unhandled_input(event: InputEvent) -> void:
    if event is InputEventMouseButton and event.pressed:
        nav_agent.target_position = get_global_mouse_position()

func _physics_process(delta: float) -> void:
    if nav_agent.is_navigation_finished():
        return

    var next_pos := nav_agent.get_next_path_position()
    var direction := global_position.direction_to(next_pos)
    velocity = direction * speed
    move_and_slide()

Godot 3'ten Godot 4'e Değişiklikler

Godot 3 Godot 4
Navigation2D düğümü Kaldırıldı. NavigationRegion2D + NavigationAgent2D kullanın
Navigation.get_simple_path() NavigationServer2D.map_get_path()
Manuel yol takibi NavigationAgent.get_next_path_position() bunu yönetir
Yerleşik kaçınma yok NavigationAgent.avoidance_enabled
Navigasyon katmanı yok Harita başına 32 navigasyon katmanı
Engel yok NavigationObstacle2D/3D