准备阶段
基础知识
前缀树
节点的数据结构RedPointNode
节点名称name、节点作为中间节点的次数passCount、节点作为结束节点的次数endCount、当前对应红点的总数redPointCount 、子节点children、红点更新时回调updateCB。 接收一个string参数的构造函数。
红点树的行为RedPointTree
1.定义根节点root,在Init函数中创建根节点。 2.使用辅助类RedPointsNodeNames,注册节点名。根据基础知识,自定义常量、结构为类似“Root|A|Achild“。存放当前树中所有节点的List。 3.插入节点,封装InsertNode,接收一个string参数。 详解: 判断节点name是否为空以及是否已经加入; 获取根节点的引用node,所有新插入的节点必定经过根节点所以passCount++,按照”|“切割传入的name; 循环遍历切割后的字符串,将未加入的新节点加入当前node的子节点集合,然后更新node为当前路径对应的子节点,对应passCount++; 遍历完path后,node更新到最后一个节点,将其endCount++;
4.查询节点,SearchNode,接收一个string参数。 详解:传入一个路径后存在两种返回null的情况,1.路径不存在2.路径存在但是以最后一个节点为结尾的次数为0 name判空; 获取根节点root引用并切割name; 遍历pathList,若当前node的子节点不包含path则返回null; 遍历结束后node为最后一个节点(即我们需要查询的节点),node.endCount<=0说明不存在以该节点为结尾的值节点,返回null;
5.删除节点,DeleteNode,接收一个string参数 详解:什么是删除?将对应路径经过的节点passCOunt--,对应节点endCount--,并移除passCount==0的子节点。
6.修改节点的红点数,ChangeRedPointCnt,接收一个stirng参数和一个int参数delta 详解:使用增量的方式修改 先查询节点是否存在; 如果增量delta为负值且绝对值大于该节点的红点数,则进行修正; 遍历路径上的所有节点,并将它们的红点数依次变化delta,然后执行各自的回调函数
7.设置红点更新回调函数,SetCallBack,接收一个string参数和回调Action 8.查询节点红点数,GetRedPointCnt,接收一个string参数
红点管理器RedPointManager
添加对应控件包括但不限于Button和Text。 1.创建红点树,CreateTree 2.注册按钮点击事件,RegisterClickEvents 3.设置对应节点回调函数,SetCallBack 4.添加数据,AddData(考虑开放一个外部接口以供调用)
完整代码
第一部分
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MyRedPoint
{
public class RedPointsNodeNames
{
public const string Root = "Root";
public const string AModel = "Root|btnAModel";
public const string AModelSub1 = "Root|btnAModel|btnSubA1";
public const string AModelSub2 = "Root|btnAModel|btnSubA2";
public static List<string> NodeList = new List<string>()
{
Root,AModel,AModelSub1,AModelSub2//,ModelB,ModelB_Sub_1,ModelB_Sub_2
};
}
public class MyRedPointNode
{
public string name;
public int passCount;
public int endCount;
public int redPointCount;
public Dictionary<string, MyRedPointNode> children;
public Dictionary<string, Action<int>> updateCB;
public MyRedPointNode(string str)
{
name = str;
passCount = 0;
endCount = 0;
redPointCount = 0;
children = new Dictionary<string, MyRedPointNode>();
updateCB = new Dictionary<string, Action<int>>();
}
}
public class MyRedPointTree
{
private MyRedPointNode root;
public MyRedPointTree()
{
root = new MyRedPointNode(RedPointsNodeNames.Root);
}
/// <summary>
/// 待定的有参构造
/// </summary>
/// <param name="name"></param>
public MyRedPointTree(string name)
{
}
#region 增删查改
public void InsertNode(string name)
{
if (string.IsNullOrEmpty(name))
{
Debug.LogError("name不能为空!");
return;
}
if (SearchNode(name) != null)
{
Debug.LogError("节点已经加入!");
return;
}
var node = root;
node.passCount++;
var pathList = name.Split('|');
foreach (var path in pathList)
{
if (!node.children.ContainsKey(path))
{
node.children.Add(path, new MyRedPointNode(path));
}
node = node.children[path];
node.passCount++;
}
node.endCount++;
}
public MyRedPointNode SearchNode(string name)
{
if (string.IsNullOrEmpty(name))
{
Debug.LogError("name不能为空!");
return null;
}
var node = root;
var pathList = name.Split('|');
foreach (var path in pathList)
{
if (!node.children.ContainsKey(path)) return null;
node = node.children[path];
}
return node.endCount > 0 ? node : null;
}
public void DeleteNode(string name)
{
if (SearchNode(name) == null)
{
return;
}
var node = root;
node.passCount --;
string[] pathList = name.Split('|');
foreach (var path in pathList)
{
var childNode = node.children[path];
childNode.passCount -= 1;
if (childNode.passCount == 0)
{
node.children.Remove(path);
}
node = childNode;
}
node.endCount -= 1;
}
#endregion
public void ChangeRedPointCnt(string name,int delta)
{
var targetNode = SearchNode(name);
if (targetNode == null) return;
if (delta < 0 && delta + targetNode.redPointCount < 0) delta = -targetNode.redPointCount;
var node = root;
var pathList = name.Split('|');
foreach (var path in pathList)
{
var childNode = node.children[path];
childNode.redPointCount += delta;
node = childNode;
foreach (var cb in childNode.updateCB.Values)
{
cb(node.redPointCount);
}
}
}
public void SetCallBack(string name, Action<int> cb)
{
var node = SearchNode(name);
if (node == null) return;
node.updateCB.Add(name,cb);
}
public int GetRedPointCnt(string name)
{
var node = SearchNode(name);
if (node == null) return 0;
return node.redPointCount;
}
}
}
第二部分
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MyRedPoint;
using TMPro;
using UnityEngine.UI;
public class MyRedPointManager : MonoBehaviour
{
public TMP_Text rootText;
public TMP_Text btnAModelText;
public TMP_Text sub1Text;
public TMP_Text sub2Text;
public Button sub1Btn;
public Button sub2Btn;
public Button add1Btn;
private MyRedPointTree redTree;
private void Start()
{
RegisterClickEvents();
CreateTree();
SetCallBack();
AddData();
}
private void AddData()
{
redTree.ChangeRedPointCnt(RedPointsNodeNames.AModelSub1, 2);
redTree.ChangeRedPointCnt(RedPointsNodeNames.AModelSub2, 3);
}
private void SetCallBack()
{
redTree.SetCallBack(RedPointsNodeNames.Root, UpdateRoot);
redTree.SetCallBack(RedPointsNodeNames.AModel, UpdateModelA);
redTree.SetCallBack(RedPointsNodeNames.AModelSub1, UpdateModelA_1);
redTree.SetCallBack(RedPointsNodeNames.AModelSub2, UpdateModelA_2);
}
#region 回调相关
private void UpdateRoot(int redCount)
{
ShowText(rootText, redCount);
}
private void UpdateModelA(int redCount)
{
ShowText(btnAModelText, redCount);
}
private void UpdateModelA_1(int redCount)
{
ShowText(sub1Text, redCount);
}
private void UpdateModelA_2(int redCount)
{
ShowText(sub2Text, redCount);
}
private void ShowText(TMP_Text txt, int count)
{
txt.text = count.ToString();
}
#endregion
private void CreateTree()
{
redTree = new MyRedPointTree();
foreach (var nodeName in RedPointsNodeNames.NodeList)
{
redTree.InsertNode(nodeName);
}
}
private void RegisterClickEvents()
{
sub1Btn.onClick.AddListener(A_1Click);
sub2Btn.onClick.AddListener(A_2Click);
add1Btn.onClick.AddListener(Add1Click);
}
#region 点击事件
private void A_1Click()
{
redTree.ChangeRedPointCnt(RedPointsNodeNames.AModelSub1, -1);
}
private void A_2Click()
{
redTree.ChangeRedPointCnt(RedPointsNodeNames.AModelSub2, -1);
}
private void Add1Click()
{
redTree.ChangeRedPointCnt(RedPointsNodeNames.AModelSub1, 1);
}
#endregion
}
参考资料
【游戏开发实战】手把手教你在Unity中使用lua实现红点系统(前缀树 | 数据结构 | 设计模式 | 算法 | 含工程源码)
对应评论区老哥的项目
后记
有待完善