新手写界面:按钮回调里直接改数值、刷新文本、发包,一个函数干三件事。等第二个入口也要改同一个数值时,要么复制粘贴,要么到处打补丁。MVC 的价值就在这里:Model 管数据,View 管显示,Controller 管转换——数据变了界面自动跟着变,界面永远只是数据的镜像。
Lua 动态特性让"数据绑定"可以做得非常轻。核心思路:Model 的每个字段带观察者列表,写入时通知所有订阅的 View 刷新。
local function bind(model, key, onChange)
local raw = model[key] -- 闭包持有真实值
return setmetatable({}, {
__index = function() return raw end,
__newindex = function(_, _, v)
raw = v
onChange(v) -- 数据一变,界面自动刷新
end,
})
end
-- View 订阅
gold = bind(state, "gold", function(v)
goldLabel:setText("金币:" .. v)
end)
-- 任何地方只改数据,界面自动更新
gold = 8800
配合上一期的 EventBus 使用效果更佳:Controller 监听业务事件改 Model,Model 变更再广播刷新事件,三层各司其职。
第一,View 禁止持有业务状态。 界面控件里不存任何逻辑数据,只做渲染;刷新函数永远从 Model 取值。这样界面重建(开关面板、热更后刷新)不会丢数据。
第二,Model 不引用 View。 数据层出现 UI 对象引用是崩坏的开始,Model 只发事件/回调,谁关心谁来订阅。这保证了无界面环境(机器人压测、单元测试)下业务逻辑照样能跑。
第三,Controller 薄。 控制器只做参数校验、协议转换、流程编排,不堆业务算法;算法沉到 Model 的方法或独立模块里。Controller 越薄,热更替换越安全。
数据与视图分离后,热更界面逻辑(View/Controller)完全不必触碰运行中的 Model 数据,替换即生效、状态零丢失;反过来改数值规则也不必动界面代码。对需要高频更新活动的商业服来说,这个分离不是架构洁癖,而是直接的运维效率。从一个面板开始实践,把"回调里改三件事"的旧代码逐步拆干净,整套规范就能在团队里自然生长。
本文由 996 技术组基于 996 引擎官方知识库与浮生梦老师课程体系整理。团队长期从事传奇类引擎 Lua 后端逻辑、客户端界面与商业版本交付,内容以官方知识库与真实项目为出处,按版本持续修订。
openwindows(actor, winID)
查看自己面板,Controller 触发面板刷新的入口
addbutton(actor, windowid, buttonid, icon)
增加自定义按钮,动态生成的视图元素由 Controller 统一挂载
delbutton(actor, windowid, buttonid)
删除自定义按钮,数据缩水时同步回收,否则残留点击区
sendredvartoclient(actor)
立即推送前端变量,Model 变化后只推变化的字段,不整页重建
refreshitem(actor, item, type, position, value)
刷新物品信息到前端,列表类视图的局部刷新
reddot(actor, win_id, btn_id, x, y, type, info)
给按钮增加红点,把状态转换成视图可见的红点,而不是在界面里判断业务
reddel(actor, win_id, btn_id)
给按钮删除红点,状态复位时清标记
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
实战应用:用在哪里 战士的烈火剑法一击熄火后要等八秒冷却,连招断档感强烈。疾风烈火斩在烈火剑法命中后给一次连击窗口:三秒内再…
实战应用:用在哪里 阵营对抗打到中期容易疲软:打赢没有额外好处,输掉也没有代价。阵营声望体系给阵营战装上荣誉刻度:个人与阵营…
实战应用:用在哪里 拿下沙巴克之后呢?城主除了名字挂在城墙上,对城市毫无影响。城主施政玩法让占领变成治理:城主每周获得市政令…
实战应用:用在哪里 主线路光缆被挖断的深夜,全服掉线 20 分钟,玩家以为游戏倒闭。备用线路方案给接入层配第二条出口:主线路…
实战应用:用在哪里 行会官员由帮主任命的老模式弊端明显:任人唯亲、能上不能下。行会选举接口把官员换届做成投票制:每两个月一次…
实战应用:用在哪里 烈火剑法的伤害加成、冷却时间、下一级提升,全靠玩家记忆或去官网查。技能说明悬浮卡片在鼠标悬停技能图标零点…