新手写界面:按钮回调里直接改数值、刷新文本、发包,一个函数干三件事。等第二个入口也要改同一个数值时,要么复制粘贴,要么到处打补丁。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 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
设计初衷 行会招人的门槛是双刃剑:无门槛的行会人满为患却乌合之众,高门槛的行会精英云集却关死了普通玩家的归属感。分层准入的目…
设计初衷 副本钥匙是副本经济的闸门:钥匙太贱,副本门口排大队、产出洪水冲垮经济;钥匙太贵,副本冷清、核心玩法没人体验。密钥平…
设计初衷 节日活动是在线峰值的引爆点:春节回城烟花一放,比奇主城同屏人数冲上日常的三倍,卡顿、掉线、排队接踵而至。峰值预案的…
底层原理 单线程的 Lua 没有并发,却有"重入":玩家的一次操作还没走完,同一次点击的第二个触发又进来了(双击、连点、宏)…
底层原理 一条业务链路往往穿越四五个函数:入口触发 → 校验 → 结算 → 通知。玩家对象、来源渠道、事务单号这些"全程都要…
业务场景 拜师系统的人数规则:一师最多三徒,出师空位才可再收。申请侧痛点:师父一次收到十几份申请无从挑起,口头答应后被人抢先…