完整课程入口:996 全套课程体系 | Lua 学习路径 | 幂尔框架 mirs.cn
任务 NPC 的分支对话、多语言选项、按玩家状态变化的台词——对话系统用硬编码 if-else 维护,两三个分支后就难以扩展。对话树方案把对话内容与流转规则做成结构化数据(表或文本),脚本只负责驱动与渲染,策划配数据即可新增分支。
local dialog = {
start = "root",
nodes = {
root = { text = "冒险者,需要帮忙吗?", options = {
{ text = "接取任务", next = "quest" },
{ text = "打听消息", next = "info" },
{ text = "离开", next = nil },
}},
quest = { check = function(p) return p.level >= 30 end,
text = "有个讨伐任务交给你。", next = "quest_accept" },
info = { text = "北边狼群最近很凶。", next = nil },
quest_accept = { action = "giveQuest", text = "干得漂亮,去吧。" },
},
}
节点四要素:text(显示文本)、options(玩家选项)、check(显示条件)、action(执行动作)。渲染端展示 text 与可选项,玩家选择后跳 next 节点;check 不通过的节点对当前玩家隐藏。
local function showDialog(p, tree)
local cur = tree.nodes[tree.start]
while cur do
if cur.check and not cur.check(p) then break end
local choice = renderAndWait(p, cur) -- 等待玩家选择,await 风格
if not choice or not cur.options[choice] then break end
cur = tree.nodes[cur.options[choice].next]
end
if cur and cur.action then doAction(cur.action, p) end
end
渲染等待用协程 await 封装(参考 await 篇),对话逻辑全程顺序可读。
对话数据与代码分离存放(Lua 表文件或 JSON),策划改文案不触碰脚本;action 名集中注册表管理,未注册的 action 在加载期报错;超长对话树按任务拆分为多棵子树,入口节点相互衔接。对话系统的数据结构定稿后,新增一个三分支 NPC 的成本从半天降到半小时——结构化数据的价值就在复用与低门槛。
对话文本支持变量插值(玩家名、任务进度)时,占位符在渲染端统一替换,替换表从玩家上下文取值。分支节点超过 30 个的对话树按任务拆子树,子树之间用 jump 类型节点衔接,单树规模控制在策划可心算的范围。nn## 与任务系统的联动nn对话树的任务节点在 action 中调用任务系统接口,接取与交付的状态变化通过事件广播给任务面板与红点系统。对话数据、任务数据、红点数据三者通过事件衔接,任何一个系统的改动都不需要同步修改另外两个,这是结构化数据带来的解耦收益。测试时用低等级号与满级号分别走一遍分支,隐藏节点的逻辑正确性当场可见。