在開發途中,因為紅點的邏輯比較宏觀,所以很容易養成開發完功能,到處補紅點邏輯的壞習慣,也因此踩過不少坑,這兩天擼了下項目的紅點系統,順便自己也寫了另一版。
也分享下紅點的思路。
首先紅點系統的基礎機制基本為上圖關系
所以是剛好滿足多叉樹的結構關系,因此大部分紅點設計邏輯都是用多叉樹來做的
下面是用多叉樹實現的紅點關系管理樹
樹的外部接口:
綁定紅點數據變更后的代理事件
提供根據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
這樣的結構捋下來其實也是一個多叉樹。只是沒有內聚業務邏輯而已
在開發功能模塊的時候由於都是分布依賴在各個實例對象上的,所以整個紅點的“網”非常散,導致后續紅點邏輯維護比較困難。
不過這個兩者的設計初衷都是規避大量遍歷紅點對象造成的性能問題,目前使用起來也都能符合需求。
至於哪種好,要看具體的業務需求吧~