追击类玩法要求怪物或护卫沿最短路径接近目标,996引擎自带寻路在复杂障碍地图里表现一般,Lua手写A星是更可控的方案。本文实现一个紧凑的A星,用于竞技场护卫追击玩家的场景。
竞技场地图摆了大量掩体柱子,引擎默认寻路经常卡在障碍边缘来回抖动。策划要求护卫穿越掩体群时绕行流畅,且三十个护卫同时追击不能拖垮服务端。手写A星可以控制迭代上限与启发权重,在效果与开销之间取平衡。
把竞技场抽象成二维网格,柱子标记为不可通行。开放列表取f值最小的格子扩展,f等于已走代价g加启发估计h,h用曼哈顿距离。找到终点后回溯得到路径点序列,怪物按序列逐点移动。
local open, closed = {}, {}
local function key(x, y) return x * 1000 + y end
table.insert(open, { x = sx, y = sy, g = 0, f = 0 })
local function popBest(list)
local bi, b = 1, list[1]
for i = 2, #list do
if list[i].f < b.f then bi, b = i, list[i] end
end
table.remove(list, bi)
return b
end
主循环扩展邻居节点,不可通行的格子直接跳过:
local dirs = { {1,0}, {-1,0}, {0,1}, {0,-1} }
for step = 1, 400 do
if #open == 0 then return nil end
local cur = popBest(open)
if cur.x == tx and cur.y == ty then return buildPath(closed, cur) end
closed[key(cur.x, cur.y)] = cur
for _, d in ipairs(dirs) do
local nx, ny = cur.x + d[1], cur.y + d[2]
if passable(nx, ny) and not closed[key(nx, ny)] then
local g = cur.g + 1
local h = math.abs(nx - tx) + math.abs(ny - ty)
table.insert(open, { x = nx, y = ny, g = g, f = g + h })
end
end
end
迭代上限400步,防止极端地图把服务端卡住。
passable函数接引擎的gridattr接口读格子属性,阻挡判定以服务端地图配置为准。路径点别每步都重算,目标移动超过三格再触发新的搜索。护卫密度高时给每只怪错开搜索时机,同一周期最多算五条路径。路径结果里的连续直线段可以合并,减少移动指令条数。
A星的代码量不大,工程量都在参数调校:迭代上限、启发权重、重算阈值共同决定流畅度与开销。先保守设置再逐步放开,比一步到位调参更稳。
大地图可以用二叉堆替换线性扫描取最小f值,单次扩展的开销从线性降到对数级。若地图静态障碍固定,启动时预计算关键点之间的跳转表,运行时搜索范围能再缩一个量级。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
实战应用:用在哪里 悬赏捕杀把玩家之间的恩怨变成可托付的系统行为:仇家难以亲自复仇时,花费金条发布悬赏,全服玩家接单击杀目标…
实战应用:用在哪里 策划改表是日常动作,漏填字段、引用了不存在的编号、数值越界这三类问题占了配表故障的绝大多数。等玩家报错再…
实战应用:用在哪里 行会擂台给中小行会提供了固定的对抗出口:挑战方击败守擂方后接管擂台,连胜次数决定每日结算奖励档位。接口层…
实战应用:用在哪里 世界首领战里玩家最关心的是伤害前十名:榜单要求随时插入、随时能按序取出,每来一条伤害记录都重新排序一次太…
实战应用:用在哪里 百人同屏的攻城战里,把每条移动、施法消息广播给全地图是最常见的性能失误。九宫格视野的做法是把地图切成等大…
实战应用:用在哪里 留言板、行会招募语、邮件标题这类字段会被玩家自由填写,并写入角色变量或转发给持久层。拼接式的写法一旦遇到…