完整课程入口: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 加计数日志)→ 父节点聚合逻辑是否覆盖了该子节点。状态驱动的系统里,问题永远落在"数据、订阅、聚合"三点之一,比在十几个界面文件里翻事件调用快得多。