2026/4/14 5:13:36
网站建设
项目流程
建设领域信用系统网站,宣传片拍摄多少钱,商务网站业务流程,如何做网站红点模块主要通过树结构完成#xff0c;先使用一个普通的树结构实现主要特性树形结构管理#xff1a;支持父子节点的层级关系自动传播#xff1a;子节点变化自动更新父节点状态事件通知#xff1a;红点变化时触发事件#xff0c;便于UI更新多种操作#xff1a;设置、增加…红点模块主要通过树结构完成先使用一个普通的树结构实现主要特性树形结构管理支持父子节点的层级关系自动传播子节点变化自动更新父节点状态事件通知红点变化时触发事件便于UI更新多种操作设置、增加、减少、清空红点数量灵活查询检查节点状态获取红点数量using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; /// summary /// 红点节点 /// /summary public class RedDotNode { // 节点Key public string Key { get; private set; } // 节点显示名称可选 public string Name { get; private set; } // 红点数量 public int Count { get; private set; } // 是否显示红点Count 0 public bool IsActive Count 0; // 父节点 public RedDotNode Parent { get; private set; } // 子节点 private Dictionarystring, RedDotNode _children new Dictionarystring, RedDotNode(); public IReadOnlyDictionarystring, RedDotNode Children _children; // 红点变化事件 public event ActionRedDotNode OnValueChanged; public RedDotNode(string key, string name null) { Key key; Name name ?? key; } /// summary /// 设置红点数量 /// /summary public void SetCount(int count) { if (Count count) return; Count Math.Max(0, count); OnValueChanged?.Invoke(this); // 通知父节点重新计算 Parent?.UpdateFromChildren(); } /// summary /// 增加红点数量 /// /summary public void AddCount(int delta 1) { SetCount(Count delta); } /// summary /// 减少红点数量 /// /summary public void ReduceCount(int delta 1) { SetCount(Math.Max(0, Count - delta)); } /// summary /// 清空红点 /// /summary public void Clear() { SetCount(0); } /// summary /// 添加子节点 /// /summary public void AddChild(RedDotNode child) { if (child null || child this) return; child.Parent?.RemoveChild(child.Key); child.Parent this; _children[child.Key] child; // 监听子节点变化 child.OnValueChanged OnChildValueChanged; // 更新当前节点状态 UpdateFromChildren(); } /// summary /// 移除子节点 /// /summary public bool RemoveChild(string key) { if (_children.TryGetValue(key, out var child)) { child.OnValueChanged - OnChildValueChanged; child.Parent null; _children.Remove(key); UpdateFromChildren(); return true; } return false; } /// summary /// 获取子节点 /// /summary public RedDotNode GetChild(string key) { return _children.TryGetValue(key, out var child) ? child : null; } /// summary /// 根据子节点更新当前节点状态 /// /summary public void UpdateFromChildren() { if (_children.Count 0) return; // 计算策略只要有任意子节点有红点父节点就显示红点 //int newCount _children.Values.Any(child child.IsActive) ? 1 : 0; // 或者计算所有子节点红点总和 int newCount _children.Values.Sum(child child.Count); if (Count ! newCount) { Count newCount; OnValueChanged?.Invoke(this); Parent?.UpdateFromChildren(); } } /// summary /// 子节点值变化回调 /// /summary private void OnChildValueChanged(RedDotNode child) { UpdateFromChildren(); } /// summary /// 获取节点路径 /// /summary public string GetPath() { var path new Liststring(); var node this; while (node ! null) { path.Insert(0, node.Key); node node.Parent; } return string.Join(., path); } /// summary /// 打印节点树结构 /// /summary public void PrintTree(string indent , bool last true) { Debug.Log(indent); if (last) { Debug.Log(└─ ); indent ; } else { Debug.Log(├─ ); indent │ ; } Debug.Log(${Name}{(IsActive ? $ [红点:{Count}] : )}); var childrenList _children.Values.ToList(); for (int i 0; i childrenList.Count; i) { childrenList[i].PrintTree(indent, i childrenList.Count - 1); } } } public class RedDotSystem { public static RedDotSystem Instance { get; } new RedDotSystem(); // 根节点 private RedDotNode _root; // 所有节点字典 private Dictionarystring, RedDotNode _allNodes new Dictionarystring, RedDotNode(); public RedDotNode Root _root; private RedDotSystem(string rootKey Root, string rootName 根节点) { _root new RedDotNode(rootKey, rootName); _allNodes[rootKey] _root; } /// summary /// 注册节点 /// /summary public RedDotNode RegisterNode(string key, string name null, string parentKey null) { if (_allNodes.ContainsKey(key)) { return _allNodes[key]; } var node new RedDotNode(key, name ?? key); _allNodes[key] node; // 连接到父节点 if (!string.IsNullOrEmpty(parentKey)) { if (_allNodes.TryGetValue(parentKey, out var parent)) { parent.AddChild(node); } else { Debug.Log(Parent Null); // 如果父节点不存在先挂到根节点等父节点创建后再调整 _root.AddChild(node); } } else { // 没有指定父节点挂到根节点 _root.AddChild(node); } return node; } /// summary /// 获取节点 /// /summary public RedDotNode GetNode(string key) { return _allNodes.TryGetValue(key, out var node) ? node : null; } /// summary /// 设置节点红点数量 /// /summary public void SetNodeCount(string key, int count) { if (_allNodes.TryGetValue(key, out var node)) { node.SetCount(count); } } /// summary /// 增加节点红点数量 /// /summary public void AddNodeCount(string key, int delta 1) { if (_allNodes.TryGetValue(key, out var node)) { node.AddCount(delta); } } /// summary /// 清空节点红点 /// /summary public void ClearNode(string key) { if (_allNodes.TryGetValue(key, out var node)) { node.Clear(); } } /// summary /// 清空所有红点 /// /summary public void ClearAll() { foreach (var node in _allNodes.Values) { node.Clear(); } } /// summary /// 检查节点是否有红点 /// /summary public bool CheckNodeActive(string key) { return _allNodes.TryGetValue(key, out var node) node.IsActive; } /// summary /// 打印整棵树 /// /summary public void PrintTree() { Debug.Log( 红点树结构 ); _root.PrintTree(); Debug.Log(); } }测试代码public class GameStart : MonoBehaviour { // Start is called before the first frame update void Start() { RedDotSystem.Instance.RegisterNode(A, A); RedDotSystem.Instance.RegisterNode(B, B, A); RedDotSystem.Instance.RegisterNode(C, C, A); } }public class RedNodeCom : MonoBehaviour { public string nodeKey; public string parentKey; RedDotNode RedDot; public TMPro.TextMeshProUGUI redText; public Button button; void Start() { if (string.IsNullOrEmpty(nodeKey)) { return; } RedDot RedDotSystem.Instance.RegisterNode(nodeKey, nodeKey, parentKey); RedDot.OnValueChanged RedDot_OnValueChanged; button.onClick.AddListener(() { RedDot.AddCount(1); }); } private void RedDot_OnValueChanged(RedDotNode node) { Debug.Log($RedDot Node {node.Key} changed to {node.Count}); redText.text node.Key - node.Count.ToString(); } }测试场景中将下方BC按钮关联到A按钮上点击子节点按钮会让父节点自动统计子节点的红点数量相关的数量计算可以在生产环境中自己调整。前缀树在上面的实现方案中查找一个指定的红点需要从一个包含了全部红点的容器中搜索因为是字典结构所以在注册的红点比较少的情况下这个是够用的但一旦数量多了起来这里面的搜索就有些慢了。因此有了通过Key键特殊命名的方式进行导航搜索的方法例如“Mail-User-Self”,Mail-User-Other,Mail-Syetem这样通过前缀进行分类管理起到在查询的时候加速的作用。红点结构不需要改只需要把查询调整一下就行public class RedDotTrieSystem { public static RedDotTrieSystem Instance { get; } new RedDotTrieSystem(); // 根节点 private RedDotNode _root; public RedDotNode Root _root; private RedDotTrieSystem(string rootKey Root, string rootName 根节点) { _root new RedDotNode(rootKey, rootName); } // 插入节点 public RedDotNode RegisterNode(string path) { var segments path.Split(.); var currentNode _root; // 逐层构建树 for (int i 0; i segments.Length; i) { var segment segments[i]; if (!currentNode.Children.ContainsKey(segment)) { var newNode new RedDotNode(segment); currentNode.AddChild(newNode); } currentNode currentNode.Children[segment]; } return currentNode; } /// summary /// 获取节点 /// /summary public RedDotNode GetNode(string key) { var segments key.Split(.); var currentNode _root; // 逐层构建树 for (int i 0; i segments.Length; i) { var segment segments[i]; if (currentNode.Children.ContainsKey(segment)) { currentNode currentNode.Children[segment]; if (i segments.Length - 1) { return currentNode; } } else { return null; } } return null; } /// summary /// 增加节点红点数量 /// /summary public void AddNodeCount(string key, int delta 1) { RedDotNode node GetNode(key); if(node ! null) { node.AddCount(delta); } } }测试代码public class RedDotTrie : MonoBehaviour { public string nodeKey; RedDotNode RedDot; public TMPro.TextMeshProUGUI redText; public Button button; void Start() { if (string.IsNullOrEmpty(nodeKey)) { return; } RedDot RedDotTrieSystem.Instance.RegisterNode(nodeKey); RedDot.OnValueChanged RedDot_OnValueChanged; button.onClick.AddListener(() { RedDot.AddCount(1); }); } private void RedDot_OnValueChanged(RedDotNode node) { Debug.Log($RedDot Node {node.Key} changed to {node.Count}); redText.text node.Key - node.Count.ToString(); } }相应参数效果