文章目錄
- 功能包括
- 如何使用
功能包括
-
紅點數據本地持久化
-
如果子節點有紅點,父節點也要顯示紅點,父節點紅點數為子節點紅點數的和;
-
當子節點紅點更新時,對應的父節點也要更新;
-
當所有子節點都沒有紅點時,父節點才不顯示紅點、
-
紅點的顯示方式分三種:
1.帶數字的,每次經過要減1
2.不帶數字只顯示紅點的
3.不帶數字但是紅點上顯示感嘆號的
如何使用
把這三個腳本復制到項目中
你沒有這個類CryptoPrefs用PlayerPrefs代替即可
RedPointTree
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using Newtonsoft.Json; // 引入Json.NET庫進行序列化和反序列化
/// <summary>
/// 節點名
/// </summary>
public enum ENodeNames
{Shop,Map,User,SongBtn,SongBtn_Event,VipBtn,
}
public class RedPointDataDTO
{public string Name { get; set; }public int PassCnt { get; set; }public int EnCnt { get; set; }public int RedPointCnt { get; set; }// 如果有必要,也可以添加子節點數據,但需確保不包含Unity特有的類型public List<RedPointDataDTO> Children { get; set; } = new List<RedPointDataDTO>();
}
public class RedPointTree : MonoSingleton<RedPointTree>
{/// <summary>/// 字顯示為!/// </summary>public int MaxNum = 999;/// <summary>/// 字不顯示/// </summary>public int NullNum = 998;private RedPointNode root;private const string RED_POINT_PREFS_KEY = "RedPointData";/// <summary>/// 保存紅點數據到CryptoPrefs/// </summary>public void SaveRedPoints(){// 將紅點樹轉換為可序列化的字符串var dto = ConvertToDto(this.root);string jsonData = JsonConvert.SerializeObject(dto, Formatting.None);CryptoPrefs.SetString(RED_POINT_PREFS_KEY, jsonData);CryptoPrefs.Save();}private RedPointDataDTO ConvertToDto(RedPointNode node){var dto = new RedPointDataDTO{Name = node.name,PassCnt = node.passCnt,EnCnt = node.enCnt,RedPointCnt = node.redpoinCnt};foreach (var child in node.children.Values){dto.Children.Add(ConvertToDto(child));}return dto;}/// <summary>/// 從CryptoPrefs加載紅點數據/// </summary>public void LoadRedPoints(){if (CryptoPrefs.HasKey(RED_POINT_PREFS_KEY)){// 從CryptoPrefs加載并反序列化紅點數據string jsonData = CryptoPrefs.GetString(RED_POINT_PREFS_KEY);var dto = JsonConvert.DeserializeObject<RedPointDataDTO>(jsonData);// 清空當前樹結構,避免數據疊加this.root.children.Clear();this.root = ConvertFromDto(dto);}}private RedPointNode ConvertFromDto(RedPointDataDTO dto){var node = new RedPointNode(dto.Name);node.passCnt = dto.PassCnt;node.enCnt = dto.EnCnt;node.redpoinCnt = dto.RedPointCnt;foreach (var childDto in dto.Children){var childNode = ConvertFromDto(childDto);node.children[childDto.Name] = childNode;}return node;}public RedPointTree() {root=new RedPointNode("Root");}/// <summary>/// 初始化/// </summary>public new void Init(){LoadRedPoints(); // 先嘗試加載已有的紅點數據if (this.root == null){//創建根節點this.root = new RedPointNode("Root");}// 構建前綴樹foreach (var name in Enum.GetNames(typeof(ENodeNames))){this.InsterNode(name);}//測試塞入紅點數據// ChangeRedPointCnt(ENodeNames.SongBtn.ToString(), 20);// ChangeRedPointCnt(ENodeNames.SongBtn_Event.ToString(), 1);// ChangeRedPointCnt(ENodeNames.User.ToString(), 999);// ChangeRedPointCnt(ENodeNames.Card.ToString(), 1);// ChangeRedPointCnt(ENodeNames.Shop.ToString(), 1);}/// <summary>/// 插入節點/// </summary>/// <param name="name"></param>public void InsterNode(string name){if (string.IsNullOrEmpty(name)){return;}if (SearchNode(name) != null){//如果已經存在 則不重復插入GameLog.Log("你已經插入了該節點" + name);return;}//node從根節點出發RedPointNode node = root;node.passCnt += 1;//將名字按|符合分割string[] pathList = name.Split('_');foreach (var path in pathList){if(!node.children.ContainsKey(path)){node.children.Add(path, RedPointNode.New(path));}node = node.children[path];node.passCnt = node.passCnt+1;}node.enCnt = node.enCnt + 1;}/// <summary>/// 查詢節點是否在樹中并返回節點/// </summary>/// <param name="name"></param>/// <returns></returns>public RedPointNode SearchNode(string name){if (string.IsNullOrEmpty(name)){return null;}RedPointNode node=this.root;string[] pathList=name.Split('_');foreach (var path in pathList){if(!node.children.ContainsKey(path)){return null;}node = node.children[path];}if (node.enCnt > 0){return node;}return null;}/// <summary>/// 刪除節點/// </summary>/// <param name="name"></param>public void DeleteNode(string name){if (SearchNode(name) == null){return;}RedPointNode node= this.root;node.passCnt = node.passCnt - 1;string[] pathList = name.Split('_');foreach (var path in pathList){RedPointNode childNode = node.children[path];childNode.passCnt = childNode.passCnt - 1;if (childNode.passCnt == 0){//如果該節點沒有任何孩子,則直接刪除node.children.Remove(path);return;}node = childNode;}node.enCnt=node.enCnt - 1;}/// <summary>/// 修改節點的和點數/// </summary>/// <param name="name">紅點名字</param>/// <param name="delta">增量</param>public void ChangeRedPointCnt(string name, int delta){RedPointNode targetNode = SearchNode(name);if (targetNode == null){return;}//如果是減紅點 并且和點數不夠減了 則調整delta 使其不減為0if (delta < 0 && targetNode.redpoinCnt + delta < 0){delta = -targetNode.redpoinCnt;}RedPointNode node=this.root;string[] pathList= name.Split('_');foreach (var path in pathList){RedPointNode childNode = node.children[path];childNode.redpoinCnt = childNode.redpoinCnt + delta;node = childNode;//調用回調函數foreach (var cb in node.updateCb.Values){cb?.Invoke(node.redpoinCnt);}}// 在更新紅點計數后保存數據SaveRedPoints();}/// <summary>/// 直接設置當前紅點的數值/// </summary>/// <param name="name"></param>/// <param name="delta"></param>public void SetRedPointCnt(string name, int delta){RedPointNode targetNode = SearchNode(name);if (targetNode == null){return;}RedPointNode node=this.root;string[] pathList= name.Split('_');foreach (var path in pathList){RedPointNode childNode = node.children[path];childNode.redpoinCnt = delta;node = childNode;//調用回調函數foreach (var cb in node.updateCb.Values){cb?.Invoke(node.redpoinCnt);}}// 在更新紅點計數后保存數據SaveRedPoints();}/// <summary>/// 設置紅點更新回調函數/// </summary>/// <param name="name">節點名</param>/// <param name="key">回調key 自定義字符串</param>/// <param name="cb">回調函數</param>public void SetCallBack(string name, string key, Action<int> cb){RedPointNode node=SearchNode(name);if (node == null){return;}if (!node.updateCb.ContainsKey(key)){node.updateCb.Add(key, cb); }else{node.updateCb[key] = cb;}}/// <summary>/// 查詢節點紅點數/// </summary>/// <param name="name"></param>/// <returns></returns>public int GetRedPointCnt(string name){RedPointNode node=SearchNode(name);if (node == null){return 0;}return node.redpoinCnt;}
}
RedPointNode
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class RedPointNode
{/// <summary>/// 節點名/// </summary>public string name;/// <summary>/// 節點被經過的次數/// </summary>public int passCnt = 0;/// <summary>/// 節點作為尾結點的次數/// </summary>public int enCnt = 0;/// <summary>/// 紅點數/// </summary>public int redpoinCnt = 0;public Dictionary<string, RedPointNode> children ;public Dictionary<string, Action<int>> updateCb ;public RedPointNode(string name){this.name = name;this.passCnt = 0;this.enCnt = 0;this.redpoinCnt = 0;this.children = new Dictionary<string, RedPointNode>();this.updateCb = new Dictionary<string, Action<int>>();}public static RedPointNode New(string name){return new RedPointNode(name);}
}
RedPointMono
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class RedPointMono : MonoBehaviour
{public ENodeNames NodeName;public Text RedPointText;private void Awake(){RedPointTree.Instance.SetCallBack(NodeName.ToString(), this.gameObject.name, (redpointCnt) => { UpdateRedPoint(redpointCnt); });}void OnEnable(){//RedPointText = transform.GetChild(0).transform.GetComponent<Text>();UpdateRedPoint(RedPointTree.Instance.GetRedPointCnt(NodeName.ToString()));}private void OnDestroy(){//注銷紅點回調RedPointTree.Instance.SetCallBack(NodeName.ToString(), this.gameObject.name, null);}//更新紅點private void UpdateRedPoint(int redpointCnt){//throw new NotImplementedException();if (redpointCnt>=RedPointTree.Instance.MaxNum){RedPointText.text = "!";}else if (redpointCnt==RedPointTree.Instance.NullNum){RedPointText.text = "";}else{RedPointText.text = redpointCnt.ToString();}gameObject.SetActive(redpointCnt > 0);}
}
然后紅點結構是這樣的
因為是基于前綴樹的,父子節點關系在這里體現
SongBtn,//父節點
SongBtn_Event,//子節點
這樣當SongBtn_Event有紅點的時候SongBtn也會有
參考鏈接:https://blog.csdn.net/linxinfa/article/details/121899276