Unity 一个简单的红点模块

红点模块主要通过树结构完成,先使用一个普通的树结构实现

主要特性

  1. 树形结构管理:支持父子节点的层级关系

  2. 自动传播:子节点变化自动更新父节点状态

  3. 事件通知:红点变化时触发事件,便于UI更新

  4. 多种操作:设置、增加、减少、清空红点数量

  5. 灵活查询:检查节点状态,获取红点数量

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 Dictionary<string, RedDotNode> _children = new Dictionary<string, RedDotNode>(); public IReadOnlyDictionary<string, RedDotNode> Children => _children; // 红点变化事件 public event Action<RedDotNode> 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 List<string>(); 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 Dictionary<string, RedDotNode> _allNodes = new Dictionary<string, 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(); } }

相应参数:

效果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1119641.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024提示工程架构师认证指南:Agentic AI方向的3大权威证书与备考攻略

2024提示工程架构师认证指南&#xff1a;Agentic AI方向的3大权威证书与备考攻略关键词&#xff1a;提示工程、Agentic AI、智能代理、大语言模型、工具调用、认证攻略、LLM应用 摘要&#xff1a;当大语言模型&#xff08;LLM&#xff09;从“对话助手”进化为“自主行动体”&a…

虾皮店如何做爆款商品呢

在竞争激烈的虾皮电商平台上&#xff0c;如何让自己的商品脱颖而出成为爆款&#xff1f;这是每个卖家都在思考的问题。打造爆款不仅是销量和利润的保证&#xff0c;更是店铺流量和品牌认知度的关键驱动力。本文将为您解析虾皮爆款商品的打造流程&#xff0c;从选品策略到推广技…

SOLID原则在Python中的实践:写出可维护的优雅代码

目录 摘要 1 引言&#xff1a;为什么Python开发者更需要SOLID原则 1.1 Python的动态特性是一把双刃剑 1.2 SOLID原则的Python化解读 2 单一职责原则&#xff08;SRP&#xff09;&#xff1a;专注的力量 2.1 SRP核心理解&#xff1a;变更的理由是关键 2.2 实战&#xff1…

学霸同款9个AI论文工具,专科生搞定毕业论文!

学霸同款9个AI论文工具&#xff0c;专科生搞定毕业论文&#xff01; AI 工具如何助力论文写作&#xff1f; 在当前的学术环境中&#xff0c;越来越多的学生开始借助 AI 工具来辅助论文写作。这些工具不仅能够帮助学生高效完成论文初稿&#xff0c;还能在降低 AIGC 率、保持语义…

深度好文:自动化与智能化融合在AI应用架构中的ROI分析,架构师必看!

自动化与智能化融合&#xff1a;AI应用架构中的ROI分析与架构设计指南 元数据框架 标题&#xff1a;自动化与智能化融合&#xff1a;AI应用架构中的ROI分析与架构设计指南关键词&#xff1a;AI应用架构&#xff1b;自动化&#xff1b;智能化&#xff1b;ROI分析&#xff1b;系统…

揭秘提示工程架构师:Agentic AI在环境监测的成功应用

从“问问题”到“解决问题”&#xff1a;提示工程架构师如何用Agentic AI重构环境监测&#xff1f; 关键词 提示工程架构师、Agentic AI、环境监测、智能代理、多模态感知、自动决策、持续学习 摘要 当我们还在讨论“如何让AI答对问题”时&#xff0c;一群“提示工程架构师”已…

2026.1南昌经开区发展规划

重磅!刚刚!南昌北部核心区域规划发展定调! 南昌楼市情报 2026年1月6日 18:04 江西 听全文

Java Agent 技术全解析:从基础框架到落地实践

Java Agent 技术全解析&#xff1a;从基础框架到落地实践 在 Java 开发领域&#xff0c;“无侵入式增强”是很多场景&#xff08;监控、排查、适配&#xff09;的核心需求。而 Java Agent 作为 JVM 层面的字节码增强技术&#xff0c;恰好能满足这一需求——无需修改业务代码&am…

双喜临门!埃文科技荣获“河南省高成长性科技型领军企业”

12月30日下午&#xff0c;河南省高成长性科技型领军企业&#xff08;企业家&#xff09;颁奖大会在郑州黄河迎宾馆举办。本次大会以“豫见新质 科创未来”为主题&#xff0c;旨在发掘表彰科技型标杆企业与领军人才&#xff0c;凝聚创新发展共识&#xff0c;助力河南打造国家创新…

强烈安利8个AI论文网站,本科生毕业论文轻松搞定!

强烈安利8个AI论文网站&#xff0c;本科生毕业论文轻松搞定&#xff01; 论文写作的“救星”来了&#xff0c;AI 工具如何帮你轻松应对毕业难题&#xff1f; 对于很多本科生来说&#xff0c;毕业论文是大学阶段最头疼的任务之一。从选题到开题&#xff0c;从撰写到降重&#x…

标准落地!AI 大模型知识库建设迈入规范化新阶段

人工智能作为战略性核心技术&#xff0c;正加速重塑产业格局&#xff0c;工信部明确提出以标准体系建设破解技术与应用脱节难题&#xff0c;引领大模型产业高质量发展。2025年12月31日&#xff0c;由国家工业信息安全发展研究中心&#xff08;工信部电子一所&#xff09;牵头&a…

【收藏必备】EAG-RAG架构详解:打造闭环自优化的企业级知识问答系统,彻底解决大模型幻觉问题

EAG-RAG是智能体增强检索增强生成技术&#xff0c;通过构建闭环自优化端到端工作流程&#xff0c;解决了传统RAG的知识时效性、幻觉问题和数据访问限制。该技术采用深度知识工程处理数据&#xff0c;通过双重LLM智能体优化查询&#xff0c;结合BM25和向量搜索进行混合检索&…

AI编程:程序员的职业新选择

AI编程:程序员的职业新选择 关键词:AI编程、程序员职业、人工智能、编程技术、职业发展 摘要:本文深入探讨了AI编程作为程序员职业新选择的相关内容。从背景介绍入手,阐述了目的、预期读者、文档结构和术语等。详细解析了AI编程的核心概念与联系,包括原理和架构的示意图及…

校平机:金属板材的“整形医生“

什么是校平机&#xff1f;校平机&#xff08;Leveling Machine&#xff09;是金属加工行业中用于消除板材内部应力、矫正弯曲和波浪形缺陷的专用设备。它通过一系列交错排列的辊轮对金属板材进行反复弯曲&#xff0c;使材料内部纤维组织均匀延伸&#xff0c;最终获得平整、无内…

吾爱原创出品,牛哇~

啰嗦几句 说到文件粉碎&#xff0c;我一直用的是火绒的文件粉碎功能&#xff0c;其集成在右键里&#xff0c;右键选择即可粉碎文件。 而如果不想装火绒的小伙伴&#xff0c;可以用今天推荐的这款非常小巧的文件粉碎软件&#xff0c;非常好用&#xff01; 软件介绍 今天介绍两…

【干货收藏】2026年AI智能体工程:10大维度详解,决定Agent能否规模化落地的生死线

智能体工程是将不确定的LLM系统转化为可靠生产级应用的工程化过程。面对Agent从Demo到产品的五道鸿沟&#xff08;不确定性、上下文管理、环境变化、可观测性不足、安全治理缺失&#xff09;&#xff0c;智能体工程通过四大能力架构层和十大工程维度&#xff0c;实现对Agent的工…

修改adb shell下$前的提示名称

基于android12 修改文件位置&#xff1a;system/core/adb/daemon/shell_service.cpp static std::string GetHostName() {char buf[HOST_NAME_MAX];if (gethostname(buf, sizeof(buf)) ! -1 && strcmp(buf, "localhost") ! 0) return buf;// 在此处return你想…

已授权给****,可永久使用!!

引言 图像格式转换与编辑软件我有段时间没推荐了&#xff0c;今天找来了两款好用的图像格式转换编辑软件&#xff0c;有需要的小伙伴及时收藏&#xff01; 02 软件介绍 第一款&#xff1a;reaConverter reaConverter是一款专业的图片格式转换工具&#xff0c;这款软件是国外…

Agentic-KGR: 利用多智能体协同强化学习提升知识图谱动态演化

Agentic-KGR: Co-evolutionary Knowledge Graph Construction through Multi-Agent Reinforcement Learninghttps://arxiv.org/pdf/2510.091561.静态知识库的“三宗罪” 图 1 典型产品 QA 场景&#xff1a;第一轮问“Pro Max 相机参数”&#xff0c;第二轮追问“256G 版续航”&…

Dify 或 LangChain?高手用原生 API 重构 LLM 开发逻辑

框架的本质&#xff1a;成熟周期的产物&#xff0c;而非创新的催化剂 我们需要先厘清一个软件工程的常识&#xff1a; 框架&#xff08;Framework&#xff09;是为了解决什么问题而诞生的&#xff1f; Spring 诞生是为了解决 Java EE 的繁琐配置&#xff1b;React 诞生是为了解…