前言
在游戏开发过程中,红点是我们非常常用的一个功能,他便于通知玩家用户去点击某些按钮,进入到策划者希望玩家看到的页面,包括游戏中的活动系统、获得新道具后等。
不过他有一个痛点就是,需要判断诸多条件,并且只要一个条件发送改变就需要对相应的红点的其他条件再进行判别。管理起来十分比较困难。
针对此痛点,完成红点树管理逻辑。
每一个逻辑功能,都是有一颗独立的红点树
分析思考
我们借鉴订阅者设计模式,来书写红点树管理的代码。
管理方案:红点状态事件和具体的物体是分离的,即你订阅了此红点状态以后,才会在UI关联,而具体的红点状态是如何变更的是关联于具体红点节点下的子事件的变化(某一判断条件就是一个子事件)
具体功能
既然红点功能可以很好的看作一个树结构。还有一个重要的特效就是父节点的红点状态不会影响子节点,但是子节点的红点状态可以影响其父节点。
- 相比于原来红点添加方式,原来每次红点更新都需要重新对多条件进行计算,产生了很多不必要的冗余运算
- 现在每个红点只在创建和变更的时候计算,会消耗一些空间保存,但是大大减少了红点计算次数和红点处理混乱的问题。
管理方案:红点状态与GameObject分离,即使没有Bind游戏物体,状态依旧存在。
以下列出关键函数
- CreateNode函数
创建一个NodeEvent,其可以供UI层进行绑定其红点状态 - Bind函数
将UI具体表现物件与NodeEvent状态绑定起来 - Unbind函数
将UI具体表现物件和NodeEvent状态解绑 - UnbindObject函数
通过gameObject解除其红点事件bind - Add函数(NodeStr,EventStr)
给NodeEvent(结点事件)添加一个ConditionEvent(条件事件) - Removed函数(NodeStr,EventStr)
去除NodeEvent(结点事件)中的一个ConditionEvent(条件事件) - Refresh函数(NodeStr,IsIgnoreParentList)
当一个NodeStr的ConditionEvent或SubNodeEvent发生变化,更新其自身节点以及以上关联节点的红点状态。 - AddParentNode函数
将一个NodeEvent与另一个NodeEvent建立父子关系 - RemoveParentNode函数
将一个NodeEvent与另一个NodeEvent断开父子关系
事件说明:
_N结尾为NodeStr或者说NodeEvent 可以用于bindGameObject 或者建立父子管理
_C结尾为ConditionEvent或者EventStr,可以驱动NodeEvet的红点状态
RedPointManager.lua
RedPointManager={
redList={},
objMapStr={}
}
function RedPointManager:CreateNode(nodeStr)
if not self.redList[nodeStr] then
self.redList[nodeStr]={}
self.redList[nodeStr].gameObjectList={}
self.redList[nodeStr].parentList={}
self.redList[nodeStr].eventList={}
self.redList[nodeStr].redParams={}
end
end
function RedPointManager:FreeNode(nodeStr)
if self.redList[nodeStr] then
self.redList[nodeStr].gameObjectList=nil
self.redList[nodeStr].parentList=nil
self.redList[nodeStr].eventList=nil
self.redList[nodeStr].redParams=nil
self.redList[nodeStr]=nil
end
end
function RedPointManager:Bind(nodeStr,gameObject,offset,size)
if not self.redList[nodeStr] then
self:CreateNode(nodeStr)
end
self.redList[nodeStr].gameObjectList[gameObject]=gameObject
self.redList[nodeStr].redParams[gameObject]={offset=offset,size=size}
if not self.objMapStr[gameObject] then
self.objMapStr[gameObject]={}
end
table.insert(self.objMapStr[gameObject],nodeStr)
self:Refresh(nodeStr,true)
end
function RedPointManager:UnBind(nodeStr,gameObject)
if self.redList[nodeStr] then
if not FairyGUITools.IsNil(self.redList[nodeStr].gameObjectList[gameObject]) then
FairyPointUtils.RemoveRed(gameObject)
end
self.redList[nodeStr].gameObjectList[gameObject]=nil
self.redList[nodeStr].redParams[gameObject]=nil
--是否还需要释放掉字符串
end
end
function RedPointManager:UnBindAll(nodeStr)
if self.redList[nodeStr] then
if(TableCount(self.redList[nodeStr].gameObjectList)~=0) then
for _,v in pairs(self.redList[nodeStr].gameObjectList) do
if not FairyGUITools.IsNil(v) then
FairyPointUtils.RemoveRed(v)
end
end
end
self.redList[nodeStr].gameObjectList={}
self.redList[nodeStr].redParams={}
-- self:FreeNode(nodeStr)
end
end
function RedPointManager:UnBindObject(gameObject)
if self.objMapStr[gameObject] then
for i=1,#self.objMapStr[gameObject] do
RedPointManager:UnBind(self.objMapStr[gameObject][i],gameObject)
end
self.objMapStr[gameObject]=nil
end
end
function RedPointManager:Add(nodeStr,eventStr)
if not self.redList[nodeStr] then
RedPointManager:CreateNode(nodeStr)
end
if not self.redList[nodeStr].eventList[eventStr] then
self.redList[nodeStr].eventList[eventStr]=true
self:Refresh(nodeStr,false)
end
end
function RedPointManager:Remove(nodeStr,eventStr)
if self.redList[nodeStr] then
if self.redList[nodeStr].eventList[eventStr] then
self.redList[nodeStr].eventList[eventStr]=nil
self:Refresh(nodeStr,false)
end
end
end
function RedPointManager:HasEvent(nodeStr,eventStr)
if not self.redList[nodeStr] then
return false
end
if not self.redList[nodeStr].eventList[eventStr] then
return false
end
return true
end
function RedPointManager:RemoveAllEvent(nodeStr)
if self.redList[nodeStr] then
self.redList[nodeStr].eventList={}
self:Refresh(nodeStr,false)
end
end
function RedPointManager:Refresh(nodeStr,isIgnoreParentList)
if self.redList[nodeStr] then
if TableCount(self.redList[nodeStr].gameObjectList)~=0 then
for _,v in pairs(self.redList[nodeStr].gameObjectList) do
if not FairyGUITools.IsNil(v) then
if TableCount(self.redList[nodeStr].eventList)~=0 then
FairyPointUtils.AddRed(v,self.redList[nodeStr].redParams[v].offset,self.redList[nodeStr].redParams[v].size)
else
FairyPointUtils.RemoveRed(v)
end
end
end
end
if not isIgnoreParentList then
if TableCount(self.redList[nodeStr].parentList)~=0 then
for _,v in pairs(self.redList[nodeStr].parentList) do
if TableCount(self.redList[nodeStr].eventList)~=0 then
self:Add(v,nodeStr)
else
self:Remove(v,nodeStr)
end
end
end
end
end
end
function RedPointManager:AddParentNode(nodeStr,parentNodeStr)
if not self.redList[nodeStr] then
RedPointManager:CreateNode(nodeStr)
end
if not self.redList[parentNodeStr] then
RedPointManager:CreateNode(parentNodeStr)
end
if self.redList[parentNodeStr] then
self.redList[nodeStr].parentList[parentNodeStr]=parentNodeStr
self:Refresh(nodeStr)
end
end
function RedPointManager:RemoveParentNode(nodeStr,parentNodeStr)
if self.redList[nodeStr] then
if self.redList[nodeStr].parentList[parentNodeStr] then
self.redList[nodeStr].parentList[parentNodeStr]=nil
if self.redList[parentNodeStr] then
self.redList[parentNodeStr].eventList[nodeStr]=nil
self:Refresh(parentNodeStr)
end
end
self:Refresh(nodeStr)
end
end
总结
红点树是我们常用的UI功能,几乎涉及到UI的游戏都会使用到,而如果没有管理好,在游戏开发后期他几乎可能成为你的灾难。
扩展:在游戏开发过程中,类似于常用的功能模块都可以提炼出来,成为你的工具链,即使用到其他项目中也能快速复用。