在开发途中,因为红点的逻辑比较宏观,所以很容易养成开发完功能,到处补红点逻辑的坏习惯,也因此踩过不少坑,这两天撸了下项目的红点系统,顺便自己也写了另一版。
也分享下红点的思路。
首先红点系统的基础机制基本为上图关系
所以是刚好满足多叉树的结构关系,因此大部分红点设计逻辑都是用多叉树来做的
下面是用多叉树实现的红点关系管理树
树的外部接口:
绑定红点数据变更后的代理事件
提供根据Key查询红点状态的功能
使用这种方式,开发的时候可以很宏观的理清楚红点的父子逻辑,做配置项的时候红点的层级关系就会非常清晰明了了
--配置项 ===> redTree = { ["RedRoot"] = { ["ModuleRed"] = { ["Tab1"]={ ["业务1"] = {}, ["业务2"] = {}, ["业务3"] = {}, }, ["Tab2"] = { ["业务4"] = {}, }, ["Tab3"] = { ["业务5"] = {}, ["业务6"] = {}, }, } } })module("RedTree", package.seeall);
RedTree = class("RedTree") --class为项目封装的一层面向对象的实现,可不做深究。 RED_NODE_TYPE = { ROOT = 1, --根节点 FORK = 2, --分叉节点 LEAF = 3 --叶节点 } --创建一颗红点树 --如果有原来红点协议的key,绑上去,给根节点显示 function RedTree:ctor(key,serverRedkey) self.serverRedkey = serverRedkey self.redNodeDic = {} self.cachaKey = {} --防止创建的时候,父节点比子节点还要晚创建,如果出现这种情况,缓存一份关联数据,当父节点key被创建时再简历父子联系 self.nodeTree = {} local treeConfig = redTree self:creatTree(treeConfig[key],key) end --创建树 function RedTree:creatTree(config,rootKey) --根节点 self.nodeTree = self:getChild(config,rootKey,nil,true) end --递归节点 function RedTree:getChild(cfg,key,parentNode,isRoot) if next(cfg) == nil then local mNode = RedNode.new(parentNode,nil,key) self.redNodeDic[key] = mNode return mNode end local curNode = {} if isRoot then curNode = RedNode.new(nil,RED_NODE_TYPE.ROOT,key) self.root = curNode else curNode = RedNode.new(parentNode,nil,key) end self.redNodeDic[key] = curNode for _key,tab in pairs(cfg) do curNode:addChild(self:getChild(tab,_key,curNode,false)) end return curNode end --绑定外层红点开关显示方法 --可不绑,自己找红点显示的时机 function RedTree:bindRedDelegate(key,delegate) self.redNodeDic[key].changeCallBack = delegate end --获取某个节点的红点状态 function RedTree:getRedStateByKey(key) if self.redNodeDic[key].type == RED_NODE_TYPE.ROOT then else return self.redNodeDic[key]:getRedNum() end end --设置某个Key的红点状态 function RedTree:setRedNumByKey(key,num) self.redNodeDic[key]:setSelfRedNum(num) end RedNode = class("redNode") function RedNode:ctor(parentNode,nodeType,key,changeCallBack) self.key = key self.childList = {} self.parentNode = parentNode self.nodeType = nodeType self.selfRedNum = 0 self.redState = false self.changeCallBack = function(num) if changeCallBack~= nil then changeCallBack(num) end end end --计算子节点的红点 function RedNode:getRedNum() local mNum = 0 --遍历下层子节点的红点数量 for i,redNode in ipairs(self.childList) do local num = redNode.selfRedNum mNum = mNum + num end mNum = mNum + self.selfRedNum return mNum end function RedNode:addChild(node) table.insert(self.childList,node) end --设置自身红点数量 function RedNode:setSelfRedNum(num,state) --有变化才通知,无变化不做更新 if self.selfRedNum ~= num then self.selfRedNum = num if self.changeCallBack ~= nil then self.changeCallBack(self.selfRedNum) end --通知父层 self:sendRed(self.parentNode) end end --父层检查红点状态 --父层红点数量以父层 function RedNode:sendRed(node)
if node.nodeType == RED_NODE_TYPE.ROOT then return end node.selfRedNum = node:getRedNum() if node.changeCallBack ~= nil then node.changeCallBack(node.selfRedNum) end if node.parentNode == nil then error(“父节点为空”)end self:sendRed(node.parentNode) end
另外一种方式是项目目前使用的,不用树来实现层级关系,每个需要红点的游戏实例对象都绑定N个key
父层:绑定 业务1 业务2 业务3 3个key,每次变更,通过或的关系来处理父层自己的红点状态
子层:业务1绑定业务1key,业务2绑定业务2key,业务3绑定业务3key
这样的结构捋下来其实也是一个多叉树。只是没有内聚业务逻辑而已
在开发功能模块的时候由于都是分布依赖在各个实例对象上的,所以整个红点的“网”非常散,导致后续红点逻辑维护比较困难。
不过这个两者的设计初衷都是规避大量遍历红点对象造成的性能问题,目前使用起来也都能符合需求。
至于哪种好,要看具体的业务需求吧~