Agent skill

Godot Navigation System

by 925236118 in 925236118/AlphaAgent

NavigationAgent2D使用、AStarGrid2D算法、自定义A实现、流式寻路及性能优化。用于2D/3D导航系统、动态障碍物避让、游戏AI导航等场景。

MITAuto-check passedGame Development

Install Godot Navigation System

skills CLI
$ npx skills add 925236118/AlphaAgent --skill godot-navigation-system -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install 925236118/AlphaAgent godot-navigation-system --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/925236118/AlphaAgent.git skills-src && mkdir -p .claude/skills && cp -r skills-src/addons/agent/skills/default_skills/godot-navigation-system .claude/skills/godot-navigation-system && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
godot-navigation-system
GitHub stars
103
Token cost
~5.9k tokens
SKILL.md length
113 words
Files
1
Skills in repo
17
Repo updated
First seen
Licence
MIT

At a glance

NavigationAgent2D使用、AStarGrid2D算法、自定义A实现、流式寻路及性能优化。用于2D/3D导航系统、动态障碍物避让、游戏AI导航等场景。

  • Works in 7 steps: NavigationAgent2D 使用 → AStarGrid2D 算法实现 → 自定义 A* 实现 → …
  • Tasks that involve Game development
  • SKILL.md covers 何时使用此技能, 1. NavigationAgent2D 使用, 2. AStarGrid2D 算法实现 and 3. 自定义 A* 实现, plus 6 more sections
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md

What it does

Godot Navigation System is an agent skill from 925236118/AlphaAgent. NavigationAgent2D使用、AStarGrid2D算法、自定义A实现、流式寻路及性能优化。用于2D/3D导航系统、动态障碍物避让、游戏AI导航等场景。

Its SKILL.md is about 5.9k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Game Development, covering Game development. It works with Godot. The repository describes itself as: 为Godot引擎编辑器开发的Ai Agent插件. The licence is MIT.

When your agent uses it

  • Tasks that involve Game development

Example prompts

  • “/godot-navigation-system”

Workflow steps

7 steps, taken from the step headings in SKILL.md.

  1. NavigationAgent2D 使用
  2. AStarGrid2D 算法实现
  3. 自定义 A* 实现
  4. 流式寻路(Flow Field Navigation)
  5. 性能优化
  6. NavLink 连接
  7. 完整示例:AI 单位导航系统

What it can do on your machine

Read from SKILL.md and the folder at commit ed0da30. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are gdscript).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Godot Navigation System loads about 5.9k tokens when it runs. Until then it costs about 27 tokens; SKILL.md has 113 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~27
When it runs · the whole SKILL.md, loaded when a task matches
~5.9k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from 925236118/AlphaAgent at commit ed0da30, republished under its MIT licence (© 925236118). 113 words, ~5,913 tokens.

Download SKILL.mdSave it as .claude/skills/godot-navigation-system/SKILL.md (or your agent's skills folder).
name
godot-navigation-system
description
NavigationAgent2D使用、AStarGrid2D算法、自定义A*实现、流式寻路及性能优化。用于2D/3D导航系统、动态障碍物避让、游戏AI导航等场景。

Godot 导航与寻路系统

Godot 4 导航系统完整指南,涵盖 NavigationAgent2D、AStarGrid2D、自定义 A* 算法、流场寻路及性能优化策略。

何时使用此技能

  • 需要 NavigationServer2D/3D 导航系统
  • 实现游戏AI寻路导航
  • 需要动态障碍物和NavLink连接
  • 优化大量单位的寻路性能

1. NavigationAgent2D 使用

NavigationAgent2D 是 Godot 4 推荐的 2D 导航解决方案,封装了 NavigationServer 的复杂操作。

基础设置
gdscript
# navigation_agent_2d.gd
class_name NavigationAgent2D
extends NavigationAgent2D

signal navigation_finished
signal path_changed
signal target_reached

@export var actor: CharacterBody2D
@export var move_speed: float = 200.0

var _target_position: Vector2 = Vector2.ZERO

func _ready() -> void:
    # 设置代理半径(障碍物避让)
    agent_height = 0
    agent_max_speed = move_speed

    # 连接信号
    navigation_finished.connect(_on_navigation_finished)
    path_changed.connect(_on_path_changed)
    target_reached.connect(_on_target_reached)

    # 等待NavigationServer同步
    await get_tree().physics_frame
    await get_tree().physics_frame

func _physics_process(delta: float) -> void:
    if actor and _target_position != Vector2.ZERO:
        if is_navigation_finished():
            return

        var next_pos := get_next_path_position()
        var current_pos := actor.global_position
        var new_velocity := (next_pos - current_pos).normalized() * move_speed

        actor.velocity = new_velocity
        actor.move_and_slide()

func set_target(world_position: Vector2) -> void:
    _target_position = world_position
    target_position = world_position

func _on_navigation_finished() -> void:
    navigation_finished.emit()

func _on_path_changed() -> void:
    path_changed.emit()

func _on_target_reached() -> void:
    target_reached.emit()
NavigationRegion2D 导航区域
gdscript
# navigation_region.gd
class_name NavigationRegion
extends NavigationRegion2D

@export var tile_map: TileMap
@export var bake_on_ready: bool = true

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

# 从 TileMap 几何数据生成导航多边形
func bake_from_tilemap() -> void:
    var polygon := NavigationPolygon.new()
    var outline: Array[Vector2] = []

    var used_rect := tile_map.get_used_rect()

    for x in range(used_rect.size.x):
        for y in range(used_rect.size.y):
            var cell := Vector2i(used_rect.position.x + x, used_rect.position.y + y)
            var tile_data := tile_map.get_cell_tile_data(0, cell)

            if tile_data and tile_data.get_custom_data("obstacle"):
                # 障碍物格子不加入导航
                continue

            var world_pos := tile_map.map_to_local(cell)
            outline.append(world_pos)

    if not outline.is_empty():
        polygon.add_outline(outline)
        polygon.make_polygons_from_outlines()

    navigation_polygon = polygon
    bake_navigation_polygon()
动态障碍物
gdscript
# dynamic_obstacle.gd
class_name DynamicObstacle
extends Area2D

@export var radius: float = 32.0
@export var navigation_region: NavigationRegion

var _last_position: Vector2

func _ready() -> void:
    area_entered.connect(_on_area_entered)
    area_exited.connect(_on_area_exited)

func _physics_process(_delta: float) -> void:
    if global_position != _last_position:
        _last_position = global_position
        _update_navigation()

func _update_navigation() -> void:
    # 简单实现:移动时重新烘焙导航网格
    # 生产环境建议使用 NavigationMesh::update()
    if navigation_region:
        navigation_region.bake_navigation_polygon()

func _on_area_entered(area: Area2D) -> void:
    # 障碍物进入逻辑
    pass

func _on_area_exited(area: Area2D) -> void:
    # 障碍物离开逻辑
    pass

2. AStarGrid2D 算法实现

AStarGrid2D 是 Godot 4 内置的高效网格寻路组件,适合规则网格的快速 A* 搜索。

基础 AStarGrid2D
gdscript
# astar_grid_2d.gd
class_name AStarGrid2D
extends Node

@export var tile_map: TileMap
@export var obstacles_layer: int = 0

var _grid: AStarGrid2D

func _ready() -> void:
    _setup_grid()

func _setup_grid() -> void:
    var region := tile_map.get_used_rect()

    _grid = AStarGrid2D.new()
    _grid.size = region.size
    _grid.offset = tile_map.tile_set.tile_size / 2
    _grid.cell_size = tile_map.tile_set.tile_size
    _grid.center = false
    _grid.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_NEVER

    _grid.update()

    # 标记障碍物
    for cell in tile_map.get_used_cells(obstacles_layer):
        var tile_data := tile_map.get_cell_tile_data(obstacles_layer, cell)
        if tile_data and tile_data.get_custom_data("obstacle"):
            _grid.set_point_solid(cell - region.position)

func find_path(start: Vector2i, end: Vector2i) -> PackedVector2Array:
    var region := tile_map.get_used_rect()
    var local_start := start - region.position
    var local_end := end - region.position

    if not _grid.is_point_inside(local_start) or not _grid.is_point_inside(local_end):
        return PackedVector2Array()

    if _grid.is_point_solid(local_start) or _grid.is_point_solid(local_end):
        return PackedVector2Array()

    var path := _grid.get_point_path(local_start, local_end)

    # 转换回世界坐标
    var world_path := PackedVector2Array()
    for point in path:
        world_path.append(tile_map.map_to_local(point + region.position))

    return world_path

func is_walkable(cell: Vector2i) -> bool:
    var region := tile_map.get_used_rect()
    var local_cell := cell - region.position

    if not _grid.is_point_inside(local_cell):
        return false

    return not _grid.is_point_solid(local_cell)

func set_obstacle(cell: Vector2i, obstacle: bool) -> void:
    var region := tile_map.get_used_rect()
    var local_cell := cell - region.position

    if obstacle:
        _grid.set_point_solid(local_cell)
    else:
        _grid.clear_point(local_cell)
带权重的 AStarGrid2D
gdscript
# weighted_astar_grid.gd
class_name WeightedAStarGrid
extends AStarGrid2D

var _cell_weights: Dictionary = {}

func _ready() -> void:
    _setup_grid()

func _setup_grid() -> void:
    var region := tile_map.get_used_rect()

    _grid = AStarGrid2D.new()
    _grid.size = region.size
    _grid.offset = tile_map.tile_set.tile_size / 2
    _grid.cell_size = tile_map.tile_set.tile_size
    _grid.center = false
    _grid.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES

    _grid.update()

    # 初始化权重
    for x in range(region.size.x):
        for y in range(region.size.y):
            var cell := Vector2i(region.position.x + x, region.position.y + y)
            _initialize_cell_weight(cell)

    # 标记障碍物
    for cell in tile_map.get_used_cells(obstacles_layer):
        var tile_data := tile_map.get_cell_tile_data(obstacles_layer, cell)
        if tile_data and tile_data.get_custom_data("obstacle"):
            _grid.set_point_solid(cell - region.position)

func _initialize_cell_weight(cell: Vector2i) -> void:
    var tile_data := tile_map.get_cell_tile_data(obstacles_layer, cell)

    if tile_data:
        var weight := tile_data.get_custom_data("weight")
        if weight != null:
            _cell_weights[cell] = weight
        else:
            _cell_weights[cell] = 1.0
    else:
        _cell_weights[cell] = 1.0

func find_path(start: Vector2i, end: Vector2i) -> PackedVector2Array:
    # 使用默认的 A* 路径(权重需要在使用时自定义处理)
    return super.find_path(start, end)

# 估计成本(启发式函数)
func _estimate_cost(from: Vector2i, to: Vector2i) -> float:
    var weight := _cell_weights.get(to, 1.0)
    return (from - to).length() * weight

3. 自定义 A* 实现

标准 A* 算法
gdscript
# custom_astar.gd
# 自定义 A* 寻路实现
class_name CustomAStar
extends Node

@export var tile_map: TileMap
@export var obstacles_layer: int = 0

var _grid_size: Vector2i
var _walkable: Dictionary = {}

class AStarNode:
    var cell: Vector2i
    var g_cost: float  # 从起点到当前节点的实际成本
    var h_cost: float  # 从当前节点到终点的估计成本
    var f_cost: float:  # g_cost + h_cost
        return g_cost + h_cost
    var parent: AStarNode = null

    func _init(c: Vector2i, g: float, h: float) -> void:
        cell = c
        g_cost = g
        h_cost = h

func _ready() -> void:
    _initialize_grid()

func _initialize_grid() -> void:
    _grid_size = tile_map.get_used_rect().size
    var origin := tile_map.get_used_rect().position

    for x in range(_grid_size.x):
        for y in range(_grid_size.y):
            var cell := Vector2i(origin.x + x, origin.y + y)
            var tile_data := tile_map.get_cell_tile_data(obstacles_layer, cell)
            _walkable[cell] = tile_data == null or not tile_data.get_custom_data("obstacle")

func find_path(start: Vector2i, end: Vector2i) -> Array[Vector2i]:
    if not _walkable.has(start) or not _walkable.has(end):
        return []

    if not _walkable.get(end, false):
        # 目标不可达,寻找最近的可通行点
        end = _find_nearest_walkable(end)
        if end == Vector2i(-1, -1):
            return []

    var open_set: Array[AStarNode] = []
    var closed_set: Dictionary = {}

    var start_node := AStarNode.new(start, 0.0, _heuristic(start, end))
    open_set.append(start_node)

    while not open_set.is_empty():
        # 找到 f_cost 最低的节点
        open_set.sort_custom(func(a, b): return a.f_cost < b.f_cost)
        var current := open_set.pop_front()

        if current.cell == end:
            return _reconstruct_path(current)

        closed_set[current.cell] = current

        for neighbor in _get_neighbors(current.cell):
            if closed_set.has(neighbor) or not _walkable.get(neighbor, false):
                continue

            var g_cost := current.g_cost + _get_move_cost(current.cell, neighbor)
            var h_cost := _heuristic(neighbor, end)

            var existing := _find_in_open_set(open_set, neighbor)

            if existing == null:
                var new_node := AStarNode.new(neighbor, g_cost, h_cost)
                new_node.parent = current
                open_set.append(new_node)
            elif g_cost < existing.g_cost:
                existing.g_cost = g_cost
                existing.parent = current

    return []

func _find_in_open_set(open_set: Array[AStarNode], cell: Vector2i) -> AStarNode:
    for node in open_set:
        if node.cell == cell:
            return node
    return null

func _heuristic(a: Vector2i, b: Vector2i) -> float:
    # 曼哈顿距离
    return absf(a.x - b.x) + absf(a.y - b.y)

func _get_move_cost(from: Vector2i, to: Vector2i) -> float:
    # 斜向移动
    if from.x != to.x and from.y != to.y:
        return 1.414
    return 1.0

func _get_neighbors(cell: Vector2i) -> Array[Vector2i]:
    return [
        cell + Vector2i(0, -1),
        cell + Vector2i(1, 0),
        cell + Vector2i(0, 1),
        cell + Vector2i(-1, 0),
        cell + Vector2i(1, -1),
        cell + Vector2i(1, 1),
        cell + Vector2i(-1, 1),
        cell + Vector2i(-1, -1),
    ]

func _find_nearest_walkable(target: Vector2i) -> Vector2i:
    var closest: Vector2i = Vector2i(-1, -1)
    var min_dist := INF

    for cell in _walkable.keys():
        if _walkable[cell]:
            var dist := (cell - target).length()
            if dist < min_dist:
                min_dist = dist
                closest = cell

    return closest

func _reconstruct_path(end_node: AStarNode) -> Array[Vector2i]:
    var path: Array[Vector2i] = []
    var current: AStarNode = end_node

    while current != null:
        path.push_front(current.cell)
        current = current.parent

    return path

4. 流式寻路(Flow Field Navigation)

流场寻路特别适合大量单位同时寻路的 RTS 游戏场景。

NavigationServer 流场
gdscript
# nav_flow_field.gd
# 使用 NavigationServer 实现流场
class_name NavFlowField
extends Node2D

@export var navigation_region: NavigationRegion2D
@export var tile_map: TileMap
@export var obstacles_layer: int = 0
@export var destination_layer: int = 1

var _nav_rid: RID
var _flow_map: Dictionary = {}  # Vector2i -> Vector2

var _update_needed: bool = false

func _ready() -> void:
    _nav_rid = navigation_region.navigation_rid
    _build_initial_flow_field()

func _physics_process(_delta: float) -> void:
    if _update_needed:
        _build_flow_field()
        _update_needed = false

func request_update() -> void:
    _update_needed = true

func _build_initial_flow_field() -> void:
    _build_flow_field()

func _build_flow_field() -> void:
    _flow_map.clear()

    # 获取所有可行走格子
    var walkable_cells: Array[Vector2i] = []
    var destination_cells: Array[Vector2i] = []

    for cell in tile_map.get_used_cells(0):
        var tile_data := tile_map.get_cell_tile_data(obstacles_layer, cell)
        if tile_data and tile_data.get_custom_data("obstacle"):
            continue
        walkable_cells.append(cell)

    for cell in tile_map.get_used_cells(destination_layer):
        destination_cells.append(cell)

    if destination_cells.is_empty():
        return

    # BFS 构建距离场
    var distance_field: Dictionary = {}
    var queue: Array[Vector2i] = destination_cells.duplicate()

    for dest in destination_cells:
        distance_field[dest] = 0.0

    while not queue.is_empty():
        var current := queue.pop_front()
        var current_dist := distance_field[current]

        for neighbor in _get_neighbors(current):
            if not _walkable(neighbor):
                continue

            if not distance_field.has(neighbor):
                distance_field[neighbor] = current_dist + 1.0
                queue.append(neighbor)

    # 构建流场
    for cell in distance_field.keys():
        _flow_map[cell] = _calculate_flow_direction(cell, distance_field)

func _walkable(cell: Vector2i) -> bool:
    var tile_data := tile_map.get_cell_tile_data(obstacles_layer, cell)
    return tile_data == null or not tile_data.get_custom_data("obstacle")

func _get_neighbors(cell: Vector2i) -> Array[Vector2i]:
    return [
        cell + Vector2i(0, -1),
        cell + Vector2i(1, 0),
        cell + Vector2i(0, 1),
        cell + Vector2i(-1, 0),
    ]

func _calculate_flow_direction(cell: Vector2i, distance_field: Dictionary) -> Vector2:
    var neighbors := _get_neighbors(cell)
    var best_dir := Vector2.ZERO
    var lowest_dist := INF

    for neighbor in neighbors:
        if distance_field.has(neighbor):
            var dist := distance_field[neighbor]
            if dist < lowest_dist:
                lowest_dist = dist
                best_dir = Vector2(neighbor - cell).normalized()

    return best_dir

func get_flow_direction(world_pos: Vector2) -> Vector2:
    var cell := tile_map.local_to_map(world_pos)

    if _flow_map.has(cell):
        return _flow_map[cell]

    return Vector2.ZERO

5. 性能优化

分组寻路(Pathfinding Batching)
gdscript
# batched_pathfinding.gd
# 分组批量寻路,减少每帧计算量
class_name BatchedPathfinding
extends Node

signal batch_completed(paths: Dictionary)

@export var max_paths_per_frame: int = 5

var _pending_requests: Array[Dictionary] = []
var _completed_paths: Dictionary = {}
var _current_batch: int = 0

class PathRequest:
    var requester_id: int
    var start: Vector2i
    var end: Vector2i
    var priority: int

    func _init(id: int, s: Vector2i, e: Vector2i, p: int = 0) -> void:
        requester_id = id
        start = s
        end = e
        priority = p

func _physics_process(_delta: float) -> void:
    _process_batch()

func request_path(requester_id: int, start: Vector2i, end: Vector2i, priority: int = 0) -> void:
    _pending_requests.append(PathRequest.new(requester_id, start, end, priority))

func _process_batch() -> void:
    if _pending_requests.is_empty():
        return

    # 按优先级排序
    _pending_requests.sort_custom(func(a, b): return a.priority > b.priority)

    var processed: int = 0

    while not _pending_requests.is_empty() and processed < max_paths_per_frame:
        var request := _pending_requests.pop_front() as PathRequest
        var path := _calculate_path(request.start, request.end)

        _completed_paths[request.requester_id] = path
        processed += 1

    if _pending_requests.is_empty():
        batch_completed.emit(_completed_paths)
        _completed_paths.clear()

func _calculate_path(start: Vector2i, end: Vector2i) -> Array[Vector2i]:
    # 这里使用自定义的 A* 或其他寻路算法
    var astar: CustomAStar = $CustomAStar
    return astar.find_path(start, end)

func get_completed_path(requester_id: int) -> Array[Vector2i]:
    if _completed_paths.has(requester_id):
        return _completed_paths[requester_id]
    return []
节流寻路(Throttled Pathfinding)
gdscript
# throttled_pathfinding.gd
# 节流寻路,避免频繁计算
class_name ThrottledPathfinding
extends Node

@export var throttle_duration: float = 0.2  # 秒

var _path_cache: Dictionary = {}
var _last_update_time: float = 0.0
var _pending_requests: Dictionary = {}
var _needs_update: bool = false

var _astar: CustomAStar

func _ready() -> void:
    _astar = $CustomAStar

func _physics_process(delta: float) -> void:
    if _needs_update:
        _last_update_time += delta

        if _last_update_time >= throttle_duration:
            _execute_throttled_update()
            _last_update_time = 0.0
            _needs_update = false

func request_path(id: int, start: Vector2i, end: Vector2i) -> void:
    _pending_requests[id] = {"start": start, "end": end, "path": null}
    _needs_update = true

func _execute_throttled_update() -> void:
    for id in _pending_requests.keys():
        var request := _pending_requests[id]
        var path := _astar.find_path(request.start, request.end)
        _path_cache[id] = path
        request.path = path

    _pending_requests.clear()

func get_path(id: int) -> Array[Vector2i]:
    return _path_cache.get(id, [])
LOD 寻路(Level of Detail)
gdscript
# lod_pathfinding.gd
# 分层寻路,远距离用粗糙网格
class_name LODPathfinding
extends Node

enum LODLevel { HIGH, MEDIUM, LOW }

@export var tile_map: TileMap
@export var obstacles_layer: int = 0

var _lod_grid_sizes: Dictionary = {
    LODLevel.HIGH: Vector2i(1, 1),
    LODLevel.MEDIUM: Vector2i(4, 4),
    LODLevel.LOW: Vector2i(8, 8),
}

var _lod_astar: Dictionary = {}

func _ready() -> void:
    _initialize_lod_grids()

func _initialize_lod_grids() -> void:
    for level in _lod_grid_sizes.keys():
        _create_lod_grid(level)

func _create_lod_grid(level: LODLevel) -> void:
    var grid_size := _lod_grid_sizes[level]
    var region := tile_map.get_used_rect()

    var astar := AStarGrid2D.new()
    var coarse_size := Vector2i(
        ceili(region.size.x / float(grid_size.x)),
        ceili(region.size.y / float(grid_size.y))
    )

    astar.size = coarse_size
    astar.cell_size = tile_map.tile_set.tile_size * grid_size
    astar.center = false
    astar.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_NEVER
    astar.update()

    _lod_astar[level] = astar

func find_path(start: Vector2i, end: Vector2i) -> Array[Vector2i]:
    var distance := (start - end).length()

    var level: LODLevel
    if distance < 200:
        level = LODLevel.HIGH
    elif distance < 500:
        level = LODLevel.MEDIUM
    else:
        level = LODLevel.LOW

    return _find_path_at_level(start, end, level)

func _find_path_at_level(start: Vector2i, end: Vector2i, level: LODLevel) -> Array[Vector2i]:
    var astar: AStarGrid2D = _lod_astar[level]

    # 转换到 LOD 网格坐标
    var grid_size := _lod_grid_sizes[level]
    var region := tile_map.get_used_rect()

    var local_start := (start - region.position) / grid_size
    var local_end := (end - region.position) / grid_size

    if astar.is_point_inside(local_start) and astar.is_point_inside(local_end):
        return astar.get_point_path(local_start, local_end)

    return []

NavLink 用于连接不连续的导航区域,实现跳跃、传送等效果。

gdscript
# custom_nav_link.gd
class_name CustomNavLink
extends NavigationLink2D

@export var link_type: int = 0  # 0: 传送, 1: 跳跃, 2: 桥梁

var _is_active: bool = true

func _ready() -> void:
    navigation_layers = 1  # 设置导航层

func _get_navigation_links(start_position: Vector2, end_position: Vector2) -> Array[Vector2]:
    if not _is_active:
        return []

    return [start_position, end_position]

func set_active(active: bool) -> void:
    _is_active = active
gdscript
# platform_nav_link.gd
class_name PlatformNavLink
extends NavigationLink2D

@export var jump_height: float = 100.0
@export var jump_duration: float = 0.5

var _start_pos: Vector2
var _end_pos: Vector2

func _ready() -> void:
    var owner := get_parent()
    if owner is Node2D:
        _start_pos = owner.global_position
        _end_pos = global_position

func get_jump_path(start: Vector2, end: Vector2) -> PackedVector2Array:
    if not _is_enabled():
        return PackedVector2Array()

    var path := PackedVector2Array()
    path.append(start)

    # 抛物线中间点
    var mid_point := (start + end) / 2.0
    mid_point.y -= jump_height

    path.append(mid_point)
    path.append(end)

    return path

func _is_enabled() -> bool:
    # 检查平台是否可用
    var platform := get_parent()
    if platform.has_method("is_active"):
        return platform.is_active()
    return true

7. 完整示例:AI 单位导航系统

gdscript
# ai_navigation_controller.gd
# 完整的 AI 单位导航控制器
class_name AINavigationController
extends CharacterBody2D

signal destination_reached
signal path_updated(path: PackedVector2Array)

@export var navigation_agent: NavigationAgent2D
@export var move_speed: float = 150.0
@export var path_reach_distance: float = 10.0

@export var use_flow_field: bool = false
@export var flow_field: NavFlowField

@export var use_lod: bool = false
@export var lod_controller: LODPathfinding

var _target_position: Vector2 = Vector2.ZERO
var _current_path: PackedVector2Array = []
var _path_index: int = 0

func _ready() -> void:
    navigation_agent.velocity_computed.connect(_on_velocity_computed)

    set_physics_process(false)

    await get_tree().physics_frame
    set_physics_process(true)

func _physics_process(delta: float) -> void:
    if navigation_agent.is_navigation_finished():
        destination_reached.emit()
        return

    var next_pos: Vector2

    if use_flow_field and flow_field:
        # 流场导航
        next_pos = _get_flow_field_next_position(delta)
    else:
        # 标准导航
        next_pos = navigation_agent.get_next_path_position()

    var current_pos := global_position
    var new_velocity := (next_pos - current_pos).normalized() * move_speed

    if navigation_agent.velocity_computed.size() > 0:
        velocity = new_velocity
        move_and_slide()
    else:
        navigation_agent.velocity = new_velocity

func _get_flow_field_next_position(delta: float) -> Vector2:
    var flow_dir := flow_field.get_flow_direction(global_position)

    if flow_dir.length() > 0.01:
        return global_position + flow_dir * move_speed * delta
    else:
        return global_position

func set_destination(world_position: Vector2) -> void:
    _target_position = world_position

    if use_lod and lod_controller:
        var start_cell := navigation_agent.get_current_navigation_region()
        var end_cell := (world_position / lod_controller.tile_map.tile_set.tile_size).floor()
        _current_path = Array(lod_controller.find_path(start_cell, end_cell))
        _path_index = 0
        path_updated.emit(_current_path)

    navigation_agent.target_position = world_position

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

性能优化建议

  1. 使用 AStarGrid2D:内置优化,比自定义 A* 更快
  2. 流场共享:大量单位共享流场,避免重复计算
  3. LOD 寻路:远距离使用粗糙网格
  4. 路径缓存:相同起点终点复用缓存结果
  5. 批量处理:每帧限制寻路请求数量
  6. 节流更新:动态障碍物变化后延迟更新

最佳实践

  • 优先使用 NavigationAgent2D,它与 NavigationServer 集成更好
  • 导航网格变化时使用 bake_navigation_polygon() 重新烘焙
  • NavLink 用于连接分离的导航区域
  • 大量单位使用流场寻路
  • 动态障碍物使用分块更新而非全局重算

© 925236118, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in addons/agent/skills/default_skills/godot-navigation-system of 925236118/AlphaAgent.

Open the folder on GitHubat commit ed0da30

Compare with similar skills

Godot Navigation System next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Godot Navigation System compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Godot Navigation System this skill925236118/AlphaAgent103—~5.9kAutomated safety check: PassMIT
2D Map and Scene Generator0x0funky/agent-sprite-forge4.4k—~2.9kAutomated safety check: PassMIT
Godot UI Integrationzimo-xiao-zheng/godot-ui-integration285—~1.5kAutomated safety check: PassMIT
Godot Wechat Minigame Adaptergodothub/godot-minigame187—~1.5kAutomated safety check: PassMIT
AI Game Art Pipelineybuild-ai/ai-game-art-pipeline-skill297—~1.1kAutomated safety check: PassMIT
MCP DriverRandallLiuXin/GodotMaker5501 repos~1.1kAutomated safety check: PassCustom licence

Similar skills

  • 2D Map and Scene Generator

    0x0funky/agent-sprite-forge

    Plans and builds 2D game maps and scenes, from tilemaps and parallax backgrounds to HD-2D plates, with collision checks, a playable HTML preview and Tiled, Godot or LDtk export.

    4.4k GitHub stars~2.9k tokensUpdated 3 days ago
    Game DevelopmentAuto-check passed
  • Godot UI Integration

    zimo-xiao-zheng/godot-ui-integration

    Build or revise Godot UI from an approved design, separated art, or a visual reference when scene structure, gameplay binding, and runtime visual verification all matter.

    285 GitHub stars~1.5k tokensUpdated 1 mo ago
    Game DevelopmentAuto-check passed
  • Godot Wechat Minigame Adapter

    godothub/godot-minigame

    Apply the bundled self-contained Godot WeChat Mini Game adapter kit to an official Godot checkout.

    187 GitHub stars~1.5k tokensUpdated 24 days ago
    Game DevelopmentAuto-check passed
  • AI Game Art Pipeline

    ybuild-ai/ai-game-art-pipeline-skill

    Provider-neutral open-source skill for planning and producing game-runtime art assets and animation: static props/icons, canonical character sheets, combat sprites, 3D/video motion references…

    297 GitHub stars~1.1k tokensUpdated 3 mo ago
    Game DevelopmentAuto-check passed
  • MCP Driver

    RandallLiuXin/GodotMaker

    Runtime debugging and live project inspection via godot-mcp.

    550 GitHub starsUsed in 1 repo~1.1k tokens
    Game DevelopmentAuto-check passed
  • Camera Systems

    ukanwat/overtime

    Build game cameras that feel good — 2D follow with a deadzone, look-ahead, smoothing, and level-bounds clamping; 3D third-person orbit with collision and first-person look; plus multi-target framing…

    387 GitHub starsUsed in 1 repo~2.2k tokens
    Game DevelopmentAuto-check passed

More from 925236118/AlphaAgent

All 17 skills in this repo
  • Godot Gdscript Patterns

    925236118/AlphaAgent

    Master Godot 4 GDScript patterns including signals, scenes, state machines, and optimization.

    103 GitHub starsUsed in 10 repos~5k tokens
    Auto-check passed
  • Godot Tscn Format

    925236118/AlphaAgent

    Godot 4.x .tscn 场景文件纯文本格式规范与 AI 操作指南 — 文件结构、资源引用、 Transform 变换、属性值格式、信号连接,以及 AI 直接读写 .tscn 文件的操作方法。

    103 GitHub starsUsed in 1 repo~2.5k tokens
    Auto-check passed
  • Godot Autoload Patterns

    925236118/AlphaAgent

    Godot 4 Autoload单例模式最佳实践,包含全局管理、事件总线、信号通信. An agent skill from 925236118/AlphaAgent.

    103 GitHub stars~1.5k tokensUpdated 2 days ago
    Auto-check passed
  • Godot Buff System

    925236118/AlphaAgent

    Godot 4 Buff系统完整实现,支持状态效果管理、持续时间、循环效果、层叠机制等。使用Resource+Component架构。

    103 GitHub stars~3.4k tokensUpdated 2 days ago
    Auto-check passed
  • Godot Building System

    925236118/AlphaAgent

    Godot 4 建造系统实现,包含GridMap方块管理、第一人称视角、射线交互. An agent skill from 925236118/AlphaAgent.

    103 GitHub stars~1k tokensUpdated 2 days ago
    Auto-check passed
  • Godot Ecs Component

    925236118/AlphaAgent

    Godot 4 ECS实体组件系统模式,包含组件化设计、数据与逻辑分离、组件通信. An agent skill from 925236118/AlphaAgent.

    103 GitHub stars~1.2k tokensUpdated 2 days ago
    Auto-check passed

Works with

Questions about Godot Navigation System

What does Godot Navigation System do?

NavigationAgent2D使用、AStarGrid2D算法、自定义A实现、流式寻路及性能优化。用于2D/3D导航系统、动态障碍物避让、游戏AI导航等场景。. Godot Navigation System is an agent skill from 925236118/AlphaAgent.

When should I use Godot Navigation System?

Godot Navigation System fits situations like: tasks that involve Game development.

How do I install Godot Navigation System in Claude Code?

Run `npx skills add 925236118/AlphaAgent --skill godot-navigation-system -a claude-code`. Or copy the skill folder (addons/agent/skills/default_skills/godot-navigation-system in 925236118/AlphaAgent) into .claude/skills/godot-navigation-system in your project. Claude Code loads it when a task matches its description.

How do I install Godot Navigation System in Codex?

Run `npx skills add 925236118/AlphaAgent --skill godot-navigation-system -a codex`. Or copy the skill folder (addons/agent/skills/default_skills/godot-navigation-system in 925236118/AlphaAgent) into .agents/skills/godot-navigation-system in your project. Codex loads it when a task matches its description.

Can I use Godot Navigation System in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add 925236118/AlphaAgent --skill godot-navigation-system -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/godot-navigation-system, .gemini/skills/godot-navigation-system, .github/skills/godot-navigation-system and .opencode/skills/godot-navigation-system in your project.

What does Godot Navigation System need to run?

SKILL.md names no scripts, command-line tools or credentials: Godot Navigation System is instructions for the agent only.

Does Godot Navigation System access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Godot Navigation System safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Godot Navigation System use?

Godot Navigation System is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Godot Navigation System use?

About 5.9k tokens (SKILL.md is roughly 24k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Godot Navigation System?

Skills that share tags, products or a category with Godot Navigation System: 2D Map and Scene Generator (0x0funky/agent-sprite-forge, 4.4k stars), Godot UI Integration (zimo-xiao-zheng/godot-ui-integration, 285 stars), Godot Wechat Minigame Adapter (godothub/godot-minigame, 187 stars) and AI Game Art Pipeline (ybuild-ai/ai-game-art-pipeline-skill, 297 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Godot Navigation System?

925236118 (a GitHub user) maintains it in 925236118/AlphaAgent, which has 103 GitHub stars. The repository holds 17 skills in this directory. The repository was last updated on October 8, 2026.

Source: 925236118/AlphaAgent on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.