跳转至

规则 2D 网格

2D 规则网格能力按职责拆成 GFGridCoordinateMath2DGFGridPathMath2DGFGridGenerationMath2DGFGridConnectionMath2D。它们都是纯算法工具,适合消消乐、连连看、推箱子、战棋格子、地图编辑器或生成前数据处理。GFGridMath 仍作为聚合 facade 保留常用旧入口,但新代码优先直接依赖对应专门类。

核心能力

  • GFGridCoordinateMath2D:范围、外环、矩形、直线、视线、泛洪、Chunk 坐标、窗口和集合差分。
  • GFGridPathMath2D:BFS、A、分步 A、路径视线抽稀和 Flow Field。
  • GFGridGenerationMath2D:矩形迷宫拓扑、二值细胞自动机报告和生成后连通区域后处理。
  • GFGridConnectionMath2D:最大转弯连接判定,适合连连看、管线连接和棋盘路径规则。
  • GFGridMath:薄 facade,便于旧代码或简单脚本一次性访问上述能力。

旋转、镜像或对角翻转格子模板时,使用同组的 GFGridTransform2D,避免把模板变换逻辑混进寻路或范围查询。

范围与形状

var move_area := GFGridCoordinateMath2D.get_range(unit_cell, 2, board_size)
var diagonal_area := GFGridCoordinateMath2D.get_range(unit_cell, 2, board_size, true)
var footprint := GFGridCoordinateMath2D.get_rectangle_cells(Vector2i(2, 2), Vector2i(4, 3), board_size)

var visible := GFGridCoordinateMath2D.has_line_of_sight(
    unit_cell,
    target_cell,
    func(cell: Vector2i) -> bool:
        return wall_cells.has(cell)
)

get_range()get_ring() 默认使用四方向移动对应的曼哈顿距离;include_diagonaltrue 时改用八方向移动对应的切比雪夫距离。get_line() 使用 Bresenham 格子直线,适合离散网格上的普通射线、指示线和简单视线判断。

Chunk 窗口

Chunk API 只做坐标和集合计算,不加载资源、不发信号、不创建节点。项目层可以把返回的 added 映射为加载请求,把 removed 映射为卸载请求,或把 kept 用作调试和排序。

var chunk_size := Vector2i(256, 256)
var current_chunk := GFGridCoordinateMath2D.world_to_chunk_cell(player.global_position, chunk_size)
var next_window := GFGridCoordinateMath2D.get_chunk_window(current_chunk, 2, &"circle")
var diff := GFGridCoordinateMath2D.diff_cells(previous_window, next_window)

for chunk in diff["added"]:
    _request_chunk_load(chunk)
for chunk in diff["removed"]:
    _request_chunk_unload(chunk)

get_chunk_window() 支持三种形状:"circle" / "euclidean" 使用欧氏半径,"square" / "chebyshev" 使用方形窗口,"diamond" / "manhattan" 使用菱形窗口。返回顺序按 y/x 稳定排序,便于测试、缓存和可复现工具链。

路径与连接

var path := GFGridPathMath2D.find_path_bfs(
    Vector2i(8, 8),
    Vector2i(0, 0),
    Vector2i(5, 4),
    func(cell: Vector2i) -> bool:
        return not blocked_cells.has(cell)
)

var can_link := GFGridConnectionMath2D.can_connect_with_max_turns(
    Vector2i(10, 6),
    Vector2i(1, 1),
    Vector2i(8, 4),
    func(cell: Vector2i) -> bool:
        return board[cell.y][cell.x] == null
)

同步 find_path_bfs() / find_path_a_star() 适合小图或一次性查询。需要跨帧预算时,使用 GFGraphPathSearchState 分步搜索句柄:

var search := GFGridPathMath2D.begin_path_a_star_search(
    Vector2i(64, 64),
    unit_cell,
    target_cell,
    func(cell: Vector2i) -> bool:
        return not blocked_cells.has(cell),
    false,
    func(_from_cell: Vector2i, to_cell: Vector2i) -> float:
        return terrain_costs.get(to_cell, 1.0)
)

var report := {}
while not report.get("finished", false):
    report = GFGraphMath.advance_path_search(search, 48)

分步句柄复用 GFGraphMath 推进,GFGridPathMath2D 只负责网格边界、邻居、通行、代价和启发函数适配。

迷宫拓扑

generate_rect_maze_backtracker() 使用确定性随机源生成矩形网格的开放边和邻接表。它只输出拓扑报告,不创建墙体、门、TileMap、碰撞体或房间节点。

var maze := GFGridGenerationMath2D.generate_rect_maze_backtracker(
    Vector2i(16, 12),
    Vector2i.ZERO,
    func(cell: Vector2i) -> bool:
        return not blocked_cells.has(cell),
    { "seed": 42 }
)

if maze["ok"]:
    var open_edges: Array = maze["edges"]
    var connections: Dictionary = maze["connections"]
    _build_tiles_from_maze(open_edges, connections)

报告中的 edges 每项包含 fromtodirectionconnectionsVector2i 为 key,值为该格开放连接到的邻居数组。complete 表示从起点是否访问了全部可用格:如果项目传入的可用格规则把地图分成多个不连通区域,报告仍会保留起点连通分量,但 completefalse

如果报告要进入日志、CI、HTTP 或外部编辑器进程,先调用 GFGridCoordinateMath2D.to_json_compatible_report(report) 或 facade 上的 GFGridMath.to_json_compatible_report(report)。它会把 Vector2i key、PackedArray、非有限浮点和其他 Godot Variant 转为可安全 JSON.stringify() 的结构;项目内继续处理原始格子 key 时仍应使用原始报告。

细胞自动机

generate_cellular_automata_map() 生成二值格子状态,默认使用八邻域洞穴平滑规则。它只返回 cellsalive_cells 和统计字段,不创建 TileMap、地形、碰撞或房间节点。

var cave := GFGridGenerationMath2D.generate_cellular_automata_map(
    Vector2i(64, 48),
    Callable(),
    {
        "seed": 42,
        "alive_chance": 0.45,
        "iterations": 4,
        "outside_alive": true,
    }
)

if cave["ok"]:
    for cell in cave["alive_cells"]:
        _paint_wall_cell(cell)

如果项目已有初始掩码,可以传入回调;规则阈值也可以替换成草地、雾区、装饰候选等项目自己的语义:

var smoothed := GFGridGenerationMath2D.generate_cellular_automata_map(
    board_size,
    func(cell: Vector2i) -> bool:
        return rough_mask.has(cell),
    {
        "iterations": 2,
        "include_diagonal": true,
        "outside_alive": false,
        "survive_min": 3,
        "survive_max": 8,
        "birth_min": 4,
        "birth_max": 8,
    }
)

连通区域后处理

find_cell_regions() 可以把任意二维格子集合按四邻域或八邻域拆成连通区域;filter_cell_regions_by_size() 会基于同一规则输出保留和移除的格子。它们适合在细胞自动机、噪声阈值、候选散布或编辑器批处理后剔除小孤岛,但不会解释墙体、地形、房间、实体或可通行语义。

var region_report := GFGridGenerationMath2D.find_cell_regions(
    cave["alive_cells"],
    { "include_diagonal": false }
)

var cleaned := GFGridGenerationMath2D.filter_cell_regions_by_size(
    cave["alive_cells"],
    16,
    { "include_diagonal": false }
)

if cleaned["ok"]:
    for cell in cleaned["kept_cells"]:
        _keep_generated_cell(cell)

区域报告中的 regionsArray[Array[Vector2i]]region_indices 可以从格子反查区域索引;largest_region_indexlargest_region_size 便于项目只保留主区域或在 UI 中提示生成质量。进入日志或外部工具前,同样先使用 to_json_compatible_report() 转换。

代价与流场

var path := GFGridPathMath2D.find_path_a_star(
    Vector2i(32, 32),
    unit_cell,
    target_cell,
    func(cell: Vector2i) -> bool:
        return not blocked_cells.has(cell),
    false,
    func(_from_cell: Vector2i, to_cell: Vector2i) -> float:
        return terrain_costs.get(to_cell, 1.0)
)

var field := GFGridPathMath2D.build_flow_field(
    Vector2i(32, 32),
    [target_cell],
    func(cell: Vector2i) -> bool:
        return not blocked_cells.has(cell)
)

var direction := (field["directions"] as Dictionary).get(unit_cell, Vector2i.ZERO)

路径抽稀

路径已经找到后,可以用现有视线规则移除多余中间格:

var simplified := GFGridPathMath2D.simplify_path_line_of_sight(
    path,
    func(cell: Vector2i) -> bool:
        return wall_cells.has(cell)
)

抽稀只判断格子直线是否被阻挡,不执行单位移动、不避让动态碰撞,也不替项目解释转向动画。

使用边界

这些 2D 网格工具只接收通行、代价、阻挡、初始状态和候选回调,不规定障碍、阵营、地形、棋子语义、移动动画、迷宫墙体表现、细胞状态含义或胜负规则。项目层负责把自己的地图数据转换成回调,并解释返回的范围、直线、路径、方向、迷宫拓扑、细胞自动机状态或连接结果。