完整课程入口:996 全套课程体系 | Lua 学习路径 | 幂尔框架 mirs.cn
红点是留存利器,也是 UI 层最容易写乱的功能:每个系统各写一套红点逻辑,按钮点击后忘了通知其他红点,红点闪烁与实际状态对不上。问题的根源是把"红点"当成了各界面自己的私事。正确做法是把红点抽成独立的状态驱动系统:红点只是数据的一种渲染,数据变了红点自然变。
把所有红点按界面层级组织成树:根节点"主界面"下挂"背包""任务""商城",背包下又挂"可穿戴""可出售"。叶子节点的红亮条件是一个注册进来的检测函数,父节点红亮 = 任意子节点红亮。数值变化时只重算受影响的一条链:
local RP = { nodes = {} }
function RP.reg(id, parent, check)
RP.nodes[id] = { parent = parent, check = check, on = false }
end
function RP.refresh(id)
local n = RP.nodes[id]
local on = n.check and n.check() or false
if not on then
for _, child in pairs(RP.nodes) do
if child.parent == id and child.on then on = true break end
end
end
if n.on ~= on then
n.on = on
UIsetIcon(id, on) -- 实际点亮/熄灭
if n.parent then RP.refresh(n.parent) end
end
end
数据变化处只需调一句 RP.refresh("bag"),整条链自动联动,任何界面都不需要自己维护红点状态。
面板刷新同理。新手常写"领奖励 → 调刷新背包 → 调刷新任务 → 调刷新主界面",每加一个系统就要往所有相关事件里插刷新调用,漏一处就出"数字不对"的 bug。状态驱动的做法:界面显示时从数据源全量刷新一次,数据层变更后广播"某数据变了"事件,订阅者各自刷新——界面永远反映数据的当前真实状态,不存在"忘了通知谁"。
性能上注意两点:高频数据(血量)的刷新做合并,一帧内多次变更只渲染一次;面板不可见时只更新数据不刷新控件,打开时全量刷一遍。
红点不亮/常亮,按固定顺序查三步:检测函数的输入数据对不对(打印 check 结果)→ refresh 是否被正确触发(在 refresh 加计数日志)→ 父节点聚合逻辑是否覆盖了该子节点。状态驱动的系统里,问题永远落在"数据、订阅、聚合"三点之一,比在十几个界面文件里翻事件调用快得多。
reddot(actor, win_id, btn_id, x, y, type, info)
给按钮增加红点,数据层状态变化时由统一入口写红点,界面不自己判断
| 参数 | 类型 | 说明 |
|---|---|---|
| actor | object | 玩家对象(必填参数) |
| win_id | integer | 窗口ID(必填参数) |
| btn_id | integer | 按钮ID/任务栏填任务ID(必填参数) |
| x | integer | X坐标(必填参数) |
| y | integer | Y坐标(必填参数) |
| type | integer | 红点模式0=图片1=特效(必填参数) |
reddot(actor, 104, 7, 15, 15, 1, 5055)
reddel(actor, win_id, btn_id)
给按钮删除红点,状态复位时清掉红点,漏清是红点系统最常见的坑
| 参数 | 类型 | 说明 |
|---|---|---|
| actor | object | 玩家对象(必填参数) |
| win_id | integer | 窗口ID(必填参数) |
| btn_id | integer | 按钮ID/任务栏填任务ID(必填参数) |
openwindows(actor, winID)
查看自己面板,打开面板时再拉一次数据,而不是提前缓存
| 参数 | 类型 | 说明 |
|---|---|---|
| actor | object | 玩家对象(必填参数) |
| winID | integer | 101=装备 102=状态 103=属性 104=技能 105=生肖 106=称号 1011=时装(必填参数) |
addbutton(actor, windowid, buttonid, icon)
增加自定义按钮,动态列表类界面靠它挂载
| 参数 | 类型 | 说明 |
|---|---|---|
| actor | object | 玩家对象(必填参数) |
| windowid | integer | 主窗口ID(必填参数) |
| buttonid | integer | 按钮ID(必填参数) |
| icon | string | 图标内容(必填参数) |
delbutton(actor, windowid, buttonid)
删除自定义按钮,界面关闭或数据缩短时必须回收
| 参数 | 类型 | 说明 |
|---|---|---|
| actor | object | 玩家对象(必填参数) |
| windowid | string | 主窗口ID(必填参数) |
| buttonid | integer | 按钮ID(必填参数) |
sendredvartoclient(actor)
立即推送前端变量,需要即时刷新的显示项走推送,避免整面板重建
| 参数 | 类型 | 说明 |
|---|---|---|
| actor | object | 玩家对象(必填参数) |