前言
在游戏开发中,红点提示系统可以通过树形结构和策略模式进行抽象,实现高扩展性。以下是基于Lua的实现方案:
一、定义红点节点类型
节点分为两种类型:
- 叶子节点:直接绑定条件函数(如检查新道具)
- 组合节点:自动聚合子节点状态(任一子节点激活则激活)
-- ========================
-- 红点系统核心管理器
-- ========================
local RedPointManager = {
-- 所有注册的红点节点,以节点ID为键
nodes = {},
-- 没有父节点的根节点集合(例如HUD入口)
rootNodes = {},
-- 事件监听器(扩展功能:事件触发自动更新)
eventListeners = {}
}
-- ========================
-- 节点评估策略模块
-- ========================
-- 叶子节点评估方法:直接执行条件函数
local function evaluateLeaf(node)
-- 当且仅当条件函数返回true时激活
return node.condition and node.condition()
end
-- 组合节点评估方法:检查任意子节点是否激活
local function evaluateComposite(node)
for _, child in ipairs(node.children) do
if child.isActive then
return true -- 任一子节点激活则组合节点激活
end
end
return false
end
二、节点注册与管理
-- ========================
-- 节点注册与管理模块
-- ========================
--- 注册一个红点节点
--- @param nodeId string 唯一节点标识(例如"WeaponTab")
--- @param parentNodeId string|nil 父节点ID(nil表示根节点)
--- @param nodeType "leaf"|"composite" 节点类型
--- @param condition function|nil 仅叶子节点需要的条件函数
function RedPointManager:RegisterNode(nodeId, parentNodeId, nodeType, condition)
-- 创建节点数据结构
local node = {
id = nodeId, -- 节点唯一标识
parent = parentNodeId and self.nodes[parentNodeId], -- 父节点引用
children = {}, -- 子节点列表(仅组合节点使用)
isActive = false, -- 当前激活状态
evaluation = nodeType == "leaf" and evaluateLeaf or evaluateComposite, -- 评估策略
condition = condition, -- 条件函数(仅叶子节点)
uiCallback = nil -- UI刷新回调
}
-- 构建父子节点关系
if node.parent then
-- 将当前节点添加到父节点的子节点列表
table.insert(node.parent.children, node)
else
-- 没有父节点时加入根节点列表
table.insert(self.rootNodes, node)
end
-- 将节点注册到全局管理器
self.nodes[nodeId] = node
return node
end
--- 为指定节点注册UI刷新回调
--- @param nodeId string 目标节点ID
--- @param callback function 回调函数(参数:isActive)
function RedPointManager:RegisterUICallback(nodeId, callback)
local node = self.nodes[nodeId]
if node then
node.uiCallback = callback -- 绑定UI更新逻辑
end
end
三、状态更新与冒泡机制
-- ========================
-- 状态更新模块
-- ========================
--- 标记某个节点需要重新评估状态
--- @param nodeId string 需要更新的节点ID
function RedPointManager:MarkDirty(nodeId)
local node = self.nodes[nodeId]
if node then
self:_EvaluateNode(node) -- 立即触发评估
end
end
-- 内部方法:递归评估节点状态
function RedPointManager:_EvaluateNode(node)
-- 执行节点对应的评估策略(叶子节点/组合节点)
local newActive = node.evaluation(node)
-- 仅当状态变化时触发后续操作
if newActive ~= node.isActive then
node.isActive = newActive -- 更新节点状态
-- 执行UI刷新回调(通知前端更新红点显示)
if node.uiCallback then
node.uiCallback(node.isActive)
end
-- 冒泡机制:状态变化时向上更新父节点
if node.parent then
self:_EvaluateNode(node.parent) -- 递归评估父节点
end
end
end
四、示例配置与使用
-- ========================
-- 示例用法模块
-- ========================
-- 初始化红点树结构
RedPointManager:RegisterNode("HUDEntry", nil, "composite") -- 根节点
RedPointManager:RegisterNode("PropPanel", "HUDEntry", "composite") -- 二级节点
RedPointManager:RegisterNode("WeaponTab", "PropPanel", "leaf", function()
-- 叶子节点的具体条件判断:玩家是否有新武器
return Player.HasNewWeapons()
end)
-- 绑定UI元素的红点显示逻辑
RedPointManager:RegisterUICallback("HUDEntry", function(isActive)
-- 当HUD入口红点状态变化时,调用HUD系统接口
HudView.SetRedDot("MainEntry", isActive)
end)
RedPointManager:RegisterUICallback("WeaponTab", function(isActive)
-- 武器标签红点状态变化时,更新道具界面
PropView.SetWeaponTabRedDot(isActive)
end)
-- ========================
-- 业务逻辑触发示例
-- ========================
-- 当玩家获得新武器时
function Player.OnNewWeaponAdded()
-- 直接标记叶子节点需要重新评估
RedPointManager:MarkDirty("WeaponTab")
end
-- ========================
-- 扩展功能:事件驱动更新
-- ========================
--- 注册事件监听(例如:道具变化、邮件到达等)
--- @param eventType string 事件类型
--- @param nodeId string 关联的节点ID
function RedPointManager:AddEventListener(eventType, nodeId)
self.eventListeners[eventType] = self.eventListeners[eventType] or {}
table.insert(self.eventListeners[eventType], nodeId)
end
-- 全局游戏事件处理器
function OnGameEvent(eventType, ...)
local nodes = RedPointManager.eventListeners[eventType]
if nodes then
for _, nodeId in ipairs(nodes) do
RedPointManager:MarkDirty(nodeId) -- 自动触发相关节点更新
end
end
end
-- 示例:将武器标签与新武器事件绑定
RedPointManager:AddEventListener("NEW_WEAPON", "WeaponTab")
五、结构示意图
HUDEntry(组合节点)
├─ PropPanel(组合节点)
│ ├─ WeaponTab(叶子节点,条件=有新武器)
│ └─ ArmorTab(叶子节点,条件=有新防具)
└─ MailPanel(组合节点)
└─ SystemMail(叶子节点,条件=未读邮件)
该设计通过分层抽象,实现以下优势:
- 低耦合:界面层仅关注回调,逻辑层管理状态
- 易扩展:新增红点只需注册节点+条件
- 高效更新:事件驱动+冒泡机制减少无效计算
- 灵活策略:支持自定义条件与聚合逻辑
六、关键机制说明
- 树形结构管理:
- 根节点(如HUD入口)→组合节点(如道具面板)→叶子节点(如武器标签)
- 父节点状态自动由子节点决定,无需手动维护
- 状态更新流程:
graph TD
A[玩家获得新武器] --> B(标记WeaponTab为脏)
B --> C{评估WeaponTab状态}
C -->|状态变化| D[更新武器标签UI]
D --> E{存在父节点?}
E -->|是| F[递归评估PropPanel]
F --> G{状态变化?}
G -->|是| H[更新道具面板父节点]
H --> I[继续冒泡到HUDEntry]
总结
该设计通过清晰的层级划分和事件驱动机制,使红点系统具备:
✅ 新增功能只需添加节点+条件
✅ 状态变更自动传播
✅ 界面与逻辑完全解耦
✅ 支持复杂嵌套规则(如:主界面红点=任务红点 OR 邮件红点)