Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

3D 導覽總覽

Godot 提供多種物件、類別及伺服器,協助 2D 與 3D 遊戲進行基於格狀(Grid)或網格(Mesh)的導覽與尋路。以下將簡介 Godot 於 3D 場景中所有可用的導覽相關物件及其主要用途。

Godot 提供以下物件與類別來支援 3D 導覽功能:

  • Astar3D

    Astar3D 物件可用於在帶權重的**節點**圖(Graph)中尋找最短路徑。

    AStar3D 類別最適合用於格狀(Cell-based)的 3D 遊戲玩法,不要求角色可到任意位置,只需移動至預設的特定位置。

  • NavigationServer3D

    NavigationServer3D 提供強大的伺服器 API,可在由導覽網格定義的區域內搜尋兩點間的最短路徑。

    NavigationServer 最適合需要角色到達導覽網格區域內任意位置的 3D 即時遊戲。基於網格的導覽可有效擴展至大型遊戲世界,因為一大片區域通常只需一個多邊形即可定義,而格狀尋路則需大量格子。

    NavigationServer 會管理多個導覽地圖,每張地圖由數個包含導覽網格資料的區域組成。代理可被放置於地圖上進行避障計算。與伺服器溝通時,會以 RID 來引用內部地圖、區域與代理。

    可用的 NavigationServer RID 型別如下。
    • 導覽地圖 RID

      引用特定的導覽地圖,地圖中包含區域與代理。地圖會根據區域間距離自動合併導覽網格,並於每個物理影格同步區域與代理。

    • 導覽區域 RID

      引用特定導覽區域,該區域可存放導覽網格資料。可啟用或停用區域,並可用導覽層(navigation layer)位元遮罩限制其用途。

    • 導覽連結 RID

      引用特定導覽連結,可連接導覽網格上兩個任意距離的位置。

    • 導覽代理 RID

      引用特定的避障代理,避障行為由半徑值定義。

    • 導覽障礙 RID

      引用特定避障障礙,可用來影響及限制代理的避障速度。

下列場景樹節點可做為輔助,協助操作 NavigationServer3D API。

  • NavigationRegion3D 節點

    存放導覽網格資源的節點,該資源定義 NavigationServer3D 的導覽網格。

    • 區域可設為啟用或停用。

    • 可透過 navigation_layers 位元遮罩進一步限制其在尋路時的用途。

    • NavigationServer3D 會依鄰近原則將區域的導覽網格合併為一個綜合導覽網格。

  • NavigationLink3D 節點

    此節點可連接導覽網格上兩個任意距離的位置,供尋路使用。

    • 連結可設為啟用或停用。

    • 連結可設為單向或雙向。

    • 可透過 navigation_layers 位元遮罩進一步限制其在尋路時的用途。

    連結會告知尋路系統有此連接,並指定其消耗。實際的代理控制與移動需於自訂腳本處理。

  • NavigationAgent3D 節點

    輔助節點,可簡化常用的 NavigationServer3D API 呼叫(如尋路與避障),建議搭配繼承 Node3D 的父節點使用。

  • NavigationObstacle3D 節點

    此節點可用來影響及限制啟用避障功能的代理的避障速度。注意:此節點**不會**影響代理的尋路,若要改變尋路必須修改導覽網格。

3D 導覽網格由下列資源所定義:

  • NavigationMesh 資源

    存放 3D 導覽網格資料的資源,並提供 3D 幾何烘焙選項,讓你可在編輯器內或執行時定義導覽區域。

    • NavigationRegion3D 節點使用此資源定義導覽區域。

    • NavigationServer3D 會使用此資源來更新各區域的導覽網格。

    • GridMap 編輯器會在每個格子有指定導覽網格時使用此資源。

也參考

你可以透過 3D Navigation 範例專案 來實際觀察 3D 導覽的運作方式。

3D 場景設定

以下步驟展示如何建立最基本的 3D 導覽功能,將會使用 NavigationServer3D 與 NavigationAgent3D 來進行路徑移動。

  1. 在場景中新增一個 NavigationRegion3D 節點。

  2. 點選區域節點,並新增一個 NavigationMesh 資源到該區域節點。

    ../../_images/nav_3d_min_setup_step1.png
  3. 在區域節點下新增一個 MeshInstance3D 子節點。

  4. 選擇 MeshInstance3D 節點,新增一個 PlaneMesh,並將 xy 大小設為 10。

  5. 再選擇區域節點,並點擊上方工具列的「烘焙導覽網格」按鈕。

    ../../_images/nav_3d_min_setup_step2.png
  6. 現在會出現一個透明導覽網格,懸浮於 PlaneMesh 上方一段距離。

    ../../_images/nav_3d_min_setup_step3.png
  7. 在場景中新增一個 CharacterBody3D 節點,並加上基本的碰撞形狀及一些網格作為視覺效果。

  8. 在角色節點下新增一個 NavigationAgent3D 子節點。

    ../../_images/nav_3d_min_setup_step4.webp
  9. 將以下腳本加到 CharacterBody3D 節點。請確保在場景完全載入且 NavigationServer 完成同步後才設定移動目標。此外,也可加入 Camera3D、燈光及環境,方便觀察效果。

extends CharacterBody3D

var movement_speed: float = 2.0
var movement_target_position: Vector3 = Vector3(-3.0,0.0,2.0)

@onready var navigation_agent: NavigationAgent3D = $NavigationAgent3D

func _ready():
    # These values need to be adjusted for the actor's speed
    # and the navigation layout.
    navigation_agent.path_desired_distance = 0.5
    navigation_agent.target_desired_distance = 0.5

    # Make sure to not await during _ready.
    actor_setup.call_deferred()

func actor_setup():
    # Wait for the first physics frame so the NavigationServer can sync.
    await get_tree().physics_frame

    # Now that the navigation map is no longer empty, set the movement target.
    set_movement_target(movement_target_position)

func set_movement_target(movement_target: Vector3):
    navigation_agent.set_target_position(movement_target)

func _physics_process(delta):
    if navigation_agent.is_navigation_finished():
        return

    var current_agent_position: Vector3 = global_position
    var next_path_position: Vector3 = navigation_agent.get_next_path_position()

    velocity = current_agent_position.direction_to(next_path_position) * movement_speed
    move_and_slide()

備註

在第一幀時,NavigationServer 的地圖尚未同步區域資料,此時任何路徑查詢都會回傳空結果。請於腳本中等待一幀,讓 NavigationServer 完成同步。