BOSS 的行为、活动的流程、任务的阶段都是状态机:嵌套的 if 加标志位写到第十个状态时没人敢再改。Lua 的协程让状态机写成顺序脚本——每个状态一段线性代码,yield 让出等待事件,代码的形状就是流程的形状。
---协程驱动的状态机
function StateMachine.new(script)
local sm = { co = coroutine.create(script) }
function sm:fire(event)
if coroutine.status(self.co) == "dead" then
return false
end
local ok, wait = coroutine.resume(self.co, event)
if not ok then
ErrReport.push({ msg = tostring(wait) })
return false
end
return true
end
return sm
end
---BOSS 战的流程脚本:线性书写
function BossFight.script()
coroutine.yield("enter")
Boss.say("竟敢闯入祖玛教主的领地!")
coroutine.yield("phase1_dead")
Boss.enrage()
coroutine.yield("phase2_dead")
Boss.die()
end
状态机的脚本是一段顺序代码:执行到 yield 挂起等待事件,fire 唤醒并推进到下一个 yield。祖玛教主的两阶段战斗写成三行主线——入场喊话、一阶段倒下狂暴、二阶段倒下死亡,流程的阅读顺序与执行顺序一致,if 地狱的嵌套在协程里被拉平成直线。
---带超时的等待:防流程卡死
function StateMachine.wait(self, event, timeout)
local deadline = os.time() + (timeout or 300)
while true do
local ev = self:poll()
if ev == event then return true end
if os.time() > deadline then
return false, "timeout"
end
coroutine.yield("tick")
end
end
---任务的阶段脚本:三步交付
function Quest.script()
local ok = StateMachine.wait(self, "accept")
if not ok then return Quest.fail("未接取") end
StateMachine.wait(self, "collected", 86400)
StateMachine.wait(self, "submitted", 3600)
Quest.reward()
end
等待的语义封装进 wait:轮询事件队列,超时的兜底防流程卡死——BOSS 战的一阶段挂了没人打,300 秒后流程自动复位而不是永远悬挂。错误处理在 resume 的返回值里:脚本的运行时错误被捕获进错误报告,状态机的崩溃不外溢到触发器层。协程的切换成本极低,千个任务的协程同时挂起的内存只有几 MB,活动的万人流程也撑得住。调试的便利是协程状态机的隐藏福利:coroutine.traceback 打印协程的调用栈,流程停在哪一行一目了然。
状态机的单测按事件序列驱动:正确的序列走通全程、缺失事件的超时分支、异常事件的容错分支三条路径全覆盖;活跃协程的数量监控,只增不减即为泄漏。
协程曾经持有玩家对象的强引用,玩家销毁后协程还在操作悬空对象,引用的弱化加存活校验修复。yield 的返回值曾经被误当参数,resume 与 yield 的参数方向搞反让事件丢失,协议的记忆口诀(resume 传入、yield 返回)写进注释。超时曾经用帧数计算,掉帧时流程被提前打断,超时统一改时间戳。
流程类逻辑(BOSS 战、活动、任务)的模板化:脚本骨架进公共库,新流程只写业务段。协程的生命周期与宿主对象绑定,宿主销毁即关闭协程。状态机的监控带当前阶段标签,线上流程卡在哪一阶段一眼可见。
本文由 996 技术组基于 996 引擎官方知识库与浮生梦老师课程体系整理。团队长期从事传奇类引擎 Lua 后端逻辑、客户端界面与商业版本交付,内容以官方知识库与真实项目为出处,按版本持续修订。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
实战应用:用在哪里 沙巴克占领行会坐收税收却只进不出,非占领行会的玩家心里不平衡。税务官玩法把税收做成明账:占领行会对摆摊与…
实战应用:用在哪里 高端玩家婚姻系统只有仪式没有过程,社交绑定缺一个高光事件。比武招亲做成擂台玩法:擂主设擂摆彩头,攻擂者报…
实战应用:用在哪里 开服七日限时礼包发到第六天断了粮,9 月 1 日当天领奖按钮灰的,投诉当天破千。事故根因是一条数字日期循…
实战应用:用在哪里 玩家拔线不退游戏,连接半开着,人不在指令也没有,在线数虚高,容量预案被假数据带着跑。心跳探测给每个在线玩…
实战应用:用在哪里 周三发了合成系统补丁,周四又改强化概率,两条变更叠加后玩家分不清是哪个改出来的。发布日历把全月变更集中排…
实战应用:用在哪里 容量预案需要知道滚动一小时的副本进入峰值,而不是全天累计。滑动窗口用 60 个槽位每槽记一分钟的进入数,…