在开发途中,因为红点的逻辑比较宏观,所以很容易养成开发完功能,到处补红点逻辑的坏习惯,也因此踩过不少坑,这两天撸了下项目的红点系统,顺便自己也写了另一版。
也分享下红点的思路。

首先红点系统的基础机制基本为上图关系
所以是刚好满足多叉树的结构关系,因此大部分红点设计逻辑都是用多叉树来做的
下面是用多叉树实现的红点关系管理树
树的外部接口:
绑定红点数据变更后的代理事件
提供根据Key查询红点状态的功能
使用这种方式,开发的时候可以很宏观的理清楚红点的父子逻辑,做配置项的时候红点的层级关系就会非常清晰明了了
1--配置项 ===> 2redTree = { 3 ["RedRoot"] = { 4 ["ModuleRed"] = { 5 ["Tab1"]={ 6 ["业务1"] = {}, 7 ["业务2"] = {}, 8 ["业务3"] = {}, 9 }, 10 ["Tab2"] = { 11 ["业务4"] = {}, 12 }, 13 ["Tab3"] = { 14 ["业务5"] = {}, 15 ["业务6"] = {}, 16 }, 17 } 18 } 19})module("RedTree", package.seeall); 20 21RedTree = class("RedTree") --class为项目封装的一层面向对象的实现,可不做深究。 22RED_NODE_TYPE = { 23 ROOT = 1, --根节点 24 FORK = 2, --分叉节点 25 LEAF = 3 --叶节点 26} 27--创建一颗红点树 28--如果有原来红点协议的key,绑上去,给根节点显示 29function RedTree:ctor(key,serverRedkey) 30 31 self.serverRedkey = serverRedkey 32 self.redNodeDic = {} 33 self.cachaKey = {} --防止创建的时候,父节点比子节点还要晚创建,如果出现这种情况,缓存一份关联数据,当父节点key被创建时再简历父子联系 34 self.nodeTree = {} 35 local treeConfig = redTree 36 self:creatTree(treeConfig[key],key) 37end 38 39 40--创建树 41 function RedTree:creatTree(config,rootKey) 42 --根节点 43 self.nodeTree = self:getChild(config,rootKey,nil,true) 44end 45--递归节点 46 function RedTree:getChild(cfg,key,parentNode,isRoot) 47 48 if next(cfg) == nil then 49 local mNode = RedNode.new(parentNode,nil,key) 50 self.redNodeDic[key] = mNode 51 return mNode 52 end 53 local curNode = {} 54 if isRoot then 55 curNode = RedNode.new(nil,RED_NODE_TYPE.ROOT,key) 56 self.root = curNode 57 else 58 curNode = RedNode.new(parentNode,nil,key) 59 end 60 self.redNodeDic[key] = curNode 61 for _key,tab in pairs(cfg) do 62 curNode:addChild(self:getChild(tab,_key,curNode,false)) 63 end 64 return curNode 65end 66 67 68 69--绑定外层红点开关显示方法 70--可不绑,自己找红点显示的时机 71function RedTree:bindRedDelegate(key,delegate) 72 self.redNodeDic[key].changeCallBack = delegate 73end 74 75 76 77--获取某个节点的红点状态 78function RedTree:getRedStateByKey(key) 79 if self.redNodeDic[key].type == RED_NODE_TYPE.ROOT then 80 81 else 82 return self.redNodeDic[key]:getRedNum() 83 end 84end 85 86 87--设置某个Key的红点状态 88function RedTree:setRedNumByKey(key,num) 89 self.redNodeDic[key]:setSelfRedNum(num) 90end 91 92 93RedNode = class("redNode") 94function RedNode:ctor(parentNode,nodeType,key,changeCallBack) 95 self.key = key 96 self.childList = {} 97 self.parentNode = parentNode 98 self.nodeType = nodeType 99 self.selfRedNum = 0 100 self.redState = false 101 self.changeCallBack = function(num) 102 103 if changeCallBack~= nil then 104 changeCallBack(num) 105 end 106 end 107end 108 109 110--计算子节点的红点 111function RedNode:getRedNum() 112 local mNum = 0 113 --遍历下层子节点的红点数量 114 for i,redNode in ipairs(self.childList) do 115 local num = redNode.selfRedNum 116 mNum = mNum + num 117 end 118 mNum = mNum + self.selfRedNum 119 return mNum 120end 121 122function RedNode:addChild(node) 123 table.insert(self.childList,node) 124end 125 126--设置自身红点数量 127function RedNode:setSelfRedNum(num,state) 128 --有变化才通知,无变化不做更新 129 if self.selfRedNum ~= num then 130 self.selfRedNum = num 131 if self.changeCallBack ~= nil then 132 self.changeCallBack(self.selfRedNum) 133 end 134 --通知父层 135 self:sendRed(self.parentNode) 136 end 137end 138 139 140--父层检查红点状态 141--父层红点数量以父层 142function RedNode:sendRed(node) 143 144 if node.nodeType == RED_NODE_TYPE.ROOT then 145 return 146 end 147 node.selfRedNum = node:getRedNum() 148 if node.changeCallBack ~= nil then 149 node.changeCallBack(node.selfRedNum) 150 end 151 if node.parentNode == nil then 152 error(“父节点为空”)end 153 154 self:sendRed(node.parentNode) 155 156end
另外一种方式是项目目前使用的,不用树来实现层级关系,每个需要红点的游戏实例对象都绑定N个key
父层:绑定 业务1 业务2 业务3 3个key,每次变更,通过或的关系来处理父层自己的红点状态
子层:业务1绑定业务1key,业务2绑定业务2key,业务3绑定业务3key
这样的结构捋下来其实也是一个多叉树。只是没有内聚业务逻辑而已
在开发功能模块的时候由于都是分布依赖在各个实例对象上的,所以整个红点的“网”非常散,导致后续红点逻辑维护比较困难。
不过这个两者的设计初衷都是规避大量遍历红点对象造成的性能问题,目前使用起来也都能符合需求。
至于哪种好,要看具体的业务需求吧~