前言
曾几何时,导师问我对红点系统的想法,我思考了一下大概说了一下我的思路,与项目组的红点系统的实现方式不一样,但是也具备理论可行性,于是导师让我自己实现一套红点系统,并能应用到项目当中去,最终实现成功,在此记录,代码中用到了一点项目中封装的方法。
一、设计思路
提供了一个结点类来实现内部逻辑,以及一个manager类提供对外接口。
1. 结点类 红点系统说白了就是树的数据结构,于是节点信息存储哪些便是关键,节点结构如下:
- 节点名(nodeName),我将节点名设置为结点的key,也就是唯一标识,这也就要求节点名不能重名。(也可以用ID来设置唯一标识)
- 父节点名(parentNodeName),可通过父节点名索引到父节点。
- 存储子节点信息的表(childNodeMap)。
- 计数(count),对子节点来说,count为1或者0即红点状态显示与否,对中间节点来说,count是它的所有红点显示的子节点的数量,当有子节点显示是即count > 0,则中间节点也显示,中间节点的表现应当完全由叶子节点决定。
- gameobject,每个节点对应unity中的游戏物体
除了节点结构,还维护了两张表,gameobjectMap{}用来存储gameobject和对应节点,nodeNameMap{}用来存储节点名和对应节点,防止每次创建新节点时检验避免重复创建。 此外还有一些方法来实现对应逻辑。包括创建节点,设置父节点,绑定游戏物体,递归刷新红点树等。
2. manager类 manager类里提供了一些对外的接口,后续可以根据需要不断优化。基础的有创建新节点,绑定gameobject,从叶节点开始刷新红点树等。
二、Lua实现
1.节点类
local RedPointNode = luaclass("RedPointNode") --封装的方法,lua实现类
--key是gameobject value是节点,存储信息保证gameobject和节点一对一
local gameObjectMap = {}
--key是nodeName,value是节点,nodeName作为唯一标识要求不能重名,防止重复创造红点节点
local nodeNameMap = {}
function RedPointNode:initialize(nodeName,parentNodeName)
self:Init(nodeName,parentNodeName)
end
--节点信息初始化
function RedPointNode:Init(nodeName,parentNodeName)
self.nodeName = nodeName
self.parentNodeName = parentNodeName
self.childNodeMap = {} --key是子节点名,value是子节点
self.count = 0
self.gameobject = nil
nodeNameMap[nodeName] = self
--将新加入的节点加到父节点的childMap中
if parentNodeName then
self:SetParentNode(RedPointNode:GetNodeByName(parentNodeName))
end
end
--红点是否显示
function RedPointNode:IsShow()
return self.count > 0
end
--添加子节点
function RedPointNode:AddChildNode(childNode)
self.childNodeMap[childNode.nodeName] = childNode
childNode.parentNode = self
end
--设置父节点,传入父节点,注意区分节点和节点名
function RedPointNode:SetParentNode(parentNode)
if not parentNode then
logError("父节点不存在")
return
end
self.parentNode = parentNode
if parentNode then
parentNode:AddChildNode(self)
end
end
--根据节点名从nodeNameMap中获取节点
function RedPointNode:GetNodeByName(nodeName)
return nodeNameMap[nodeName]
end
--根据节点名获取子节点
function RedPointNode:GetChildNode(nodeName)
for k,v in pairs(self.childNodeMap or {}) do
if k == nodeName then
return v
end
end
end
--移除子节点
function RedPointNode:RemoveChildNode(nodeName)
self.childNodeMap[nodeName] = nil
end
--从叶节点刷新红点状态
function RedPointNode:UpdateLeafNode(isShow)
if self:IsShow() == isShow then
logError("叶子节点状态不变")
return
end
self:UpdateNode(isShow)
end
--递归刷新红点树
function RedPointNode;UpdateNode(isShow)
if isShow == true then
self.count = self.count + 1
elseif isShow == false then
self.count = self.count - 1
end
self:RefreshVisible()
if self.parentNode then
self.parentNode:UpdateNode(isShow)
end
end
--为红点绑定gameobject,要求gameobject和节点一对一
function RedPointNode:SetGameObject(gameObject)
if gameObject then --把gameobject上绑定的节点取消,防止一个节点绑定多个gameobject
if gameObjectMap[gameObject] then
gameObjectMap[gameObject].gameObject = nil
end
end
if self.gameObject then --把节点原来对应的gameObject取消,防止一个节点绑定多个gameobject
gameObjectMap[self.gameObject] = nil
end
self.gameObject = gameObject
if gameObject then
gameObjectMap[gameObject] = self
end
self:RefreshVisible()
end
--刷新红点对象显示
function RedPointNode:RefreshVisible()
if self.gameObject then
if CSLuaHelper.IsNull(self.gameObject) == 1 then
logError("gameObject is destory")
return
end
self.gameObject:SetActive(self:IsShow())
end
end
2.红点树管理类,接口类
local RedPointClass = {}
--创建新节点,传入节点名(ID)和父节点
function RedPointClass:CreateNode(nodeName,parentNodeName)
if nodeNameMap[nodeName] then
logError("节点已经存在")
return
end
return RedPointNode(nodeName,parentNodeName)
end
--为该节点设置gameObject,传入一个gameobject
function RedPointClass:SetNodeGameObject(nodeName,gameObject)
if not nodeNameMap[nodeName] then
logError("该节点不存在")
return
end
nodeNameMap[nodeName]:SetGameObject(gameObject)
end
--从叶节点开始向上刷新红点树,调用者必须是叶节点
function RedPointClass:RefreshFromLeaf(nodeName,isShow)
if Linq.Count(nodeNameMap[nodeName].childNodeMap ~= 0 then --不是叶节点
logError("调用者必须是叶节点")
return
end
nodeNameMap[nodeName]:UpdateLeafNode(isShow)
end
function RedPointClass:Log()
logError("用于debug")
end
return RedPointClass
总结
该红点系统的使用流程为
- 创建节点
- 绑定gameobject
- 叶节点调用刷新红点树
需要注意的地方有:
- 必须先从根节点开始创建节点,子节点创建要保证父节点已经存在。
- 节点名(nodeName)在这个系统中是唯一标识,是每个节点的key,也就是说不允许重名出现,如果有重名需求,可以在节点信息中添加self.Id来作为key,再维护一张“ ID—节点” 表。
- 红点树的维护是在游戏init的时候就生成,而绑定gameObject是在页面initUI的时候操作,所以在每次绑定完gameObject需要再刷新一次表现。
- 中间节点完全由子节点影响表现,对于刷新红点树接口只能叶节点调用,递归刷新上层节点,中间节点的表现取决于它有没有子节点有红点存在。
- 对于叶子节点的count应当控制在1 or 0,即对应显示和不显示的表现,所以当对已经显示(count == 1)的叶子节点刷新isShow为显示的时候,企图给count+1,应当进行处理,判断叶子节点状态有没有改变,count是0亦如此。
- 根据需求可以在接口类实现更多接口例如获取子节点,从nodeNameMap中获取节点,删除节点等,也可以在节点类进行拓展以实现更多功能。
|