RedDotData.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. namespace YIUIFramework
  5. {
  6. /// <summary>
  7. /// 每个具体红点的 运行时数据
  8. /// </summary>
  9. public partial class RedDotData
  10. {
  11. /// <summary>
  12. /// Key
  13. /// </summary>
  14. public int Key => Config.Key;
  15. /// <summary>
  16. /// 配置
  17. /// </summary>
  18. public RedDotConfigData Config { get; private set; }
  19. /// <summary>
  20. /// 操作堆栈
  21. /// </summary>
  22. public List<RedDotStack> StackList { get; private set; }
  23. /// <summary>
  24. /// 我的所有父级
  25. /// </summary>
  26. public HashSet<RedDotData> ParentList { get; private set; }
  27. /// <summary>
  28. /// 我的所有子级
  29. /// </summary>
  30. public HashSet<RedDotData> ChildList { get; private set; }
  31. /// <summary>
  32. /// 红点改变通知
  33. /// </summary>
  34. private Action<int> m_OnChangedAction;
  35. /// <summary>
  36. /// 当前是否提示
  37. /// </summary>
  38. public bool Tips { get; private set; }
  39. /// <summary>
  40. /// 当前红点的真实数量
  41. /// </summary>
  42. public int RealCount { get; private set; }
  43. /// <summary>
  44. /// 当前红点数量 如果关闭了红点永远=0
  45. /// </summary>
  46. public int Count => Tips ? RealCount : 0;
  47. /// <summary>
  48. /// 本地存储的唯一K
  49. /// </summary>
  50. private string m_PlayerTipsKey;
  51. /// <summary>
  52. /// 本地存储的值
  53. /// </summary>
  54. private BoolPrefs m_PlayerTipsKeyBoolPrefs;
  55. private RedDotData()
  56. {
  57. }
  58. internal RedDotData(RedDotConfigData config)
  59. {
  60. Config = config;
  61. ParentList = new HashSet<RedDotData>();
  62. ChildList = new HashSet<RedDotData>();
  63. InitTips();
  64. #if UNITY_EDITOR || YIUIMACRO_REDDOT_STACK
  65. StackList = new List<RedDotStack>();
  66. #endif
  67. }
  68. /// <summary>
  69. /// 初始化 当前红点是否可显示
  70. /// </summary>
  71. private void InitTips()
  72. {
  73. //如果配置档中不允许开关提示 那么这个提示永远 = true 且不可修改
  74. if (!Config.SwitchTips)
  75. {
  76. Tips = true;
  77. return;
  78. }
  79. m_PlayerTipsKey = $"PlayerRedDotTips_{Config.Key}";
  80. m_PlayerTipsKeyBoolPrefs = new BoolPrefs(m_PlayerTipsKey, null, Config.SwitchTips);
  81. Tips = m_PlayerTipsKeyBoolPrefs.Value;
  82. }
  83. internal void DeletePlayerTipsPrefs()
  84. {
  85. m_PlayerTipsKeyBoolPrefs.Delete();
  86. }
  87. /// <summary>
  88. /// 添加父级对象
  89. /// </summary>
  90. internal bool AddParent(RedDotData data)
  91. {
  92. //所有父子关联关系 都在编辑器中检查完成
  93. //要保证不会出现循环引用关系
  94. ParentList.Add(data); //目标设定为我的父级
  95. return data.AddChild(this); //因为他是我的父级所以我为他的子级
  96. }
  97. /// <summary>
  98. /// 添加子对象
  99. /// </summary>
  100. private bool AddChild(RedDotData data)
  101. {
  102. return ChildList.Add(data);
  103. }
  104. /// <summary>
  105. /// 这个是实时同步修改
  106. /// 目前使用脏标修改 防止有人同步过快
  107. /// 没有子级 说明是最后一级 最后一级才可以设置
  108. /// </summary>
  109. internal bool TrySetCount(int count)
  110. {
  111. if (ChildList.Count <= 0)
  112. {
  113. return SetCount(count);
  114. }
  115. Debug.LogError($"{Config.Key} 配置 不是最后一级红点 请不要直接修改");
  116. return false;
  117. }
  118. /// <summary>
  119. /// 设置当前红点数量
  120. /// 返回值 false = 没有变化
  121. /// </summary>
  122. private bool SetCount(int count, RedDotStack stack = null)
  123. {
  124. if (RealCount == count)
  125. {
  126. return false;
  127. }
  128. #if UNITY_EDITOR || YIUIMACRO_REDDOT_STACK
  129. if (stack == null)
  130. {
  131. var firstData = new FirstRedDotChangeData(this, RealCount, count, Tips);
  132. stack = AddNewStack(ERedDotOSType.Count, RealCount, count, Tips, firstData);
  133. }
  134. else
  135. {
  136. AddStack(stack);
  137. }
  138. #endif
  139. RealCount = count;
  140. NotifyChange(stack);
  141. return true;
  142. }
  143. /// <summary>
  144. /// 通知父级 自己变化了
  145. /// 此处会递归 直到没有父级
  146. /// </summary>
  147. private void NotifyChange(RedDotStack stack)
  148. {
  149. InvokeOnChanged();
  150. foreach (var parent in ParentList)
  151. {
  152. parent.ChildChanged(stack);
  153. }
  154. }
  155. /// <summary>
  156. /// 子对象变化了
  157. /// 然后自己变化 然后又通知递归
  158. /// </summary>
  159. private void ChildChanged(RedDotStack stack)
  160. {
  161. var count = 0;
  162. foreach (var child in ChildList)
  163. {
  164. count += child.Count;
  165. }
  166. SetCount(count, stack);
  167. }
  168. /// <summary>
  169. /// 设置当前是否提示
  170. /// </summary>
  171. internal bool SetTips(bool tips, RedDotStack stack = null)
  172. {
  173. if (!Config.SwitchTips)
  174. {
  175. Debug.LogError($"{Config.Key} 配置 关闭了红点的开关提示 目前永久提示 禁止修改 ");
  176. return false;
  177. }
  178. if (Tips == tips)
  179. {
  180. return false;
  181. }
  182. Tips = tips;
  183. #if UNITY_EDITOR || YIUIMACRO_REDDOT_STACK
  184. if (stack == null)
  185. {
  186. var firstData = new FirstRedDotChangeData(this, RealCount, Count, Tips);
  187. stack = AddNewStack(ERedDotOSType.Tips, RealCount, Count, Tips, firstData);
  188. }
  189. else
  190. {
  191. AddStack(stack);
  192. }
  193. #endif
  194. m_PlayerTipsKeyBoolPrefs.Value = tips; //存储到本地
  195. NotifyChange(stack); //提示改变后 通知 监听改变
  196. return true;
  197. }
  198. /// <summary>
  199. /// 添加一个回调
  200. /// int = 当前红点数量 (非真实数量)
  201. /// 并马上设置相关数据
  202. /// (因为界面初始化时都是需要刷新界面的 所以默认调用一次)
  203. /// </summary>
  204. internal void AddOnChanged(Action<int> action)
  205. {
  206. m_OnChangedAction += action;
  207. InvokeOnChanged();
  208. }
  209. /// <summary>
  210. /// 移除回调
  211. /// </summary>
  212. /// <param name="action"></param>
  213. internal void RemoveChanged(Action<int> action)
  214. {
  215. m_OnChangedAction -= action;
  216. }
  217. //try回调
  218. private void InvokeOnChanged()
  219. {
  220. try
  221. {
  222. //回调不会使用真实数量
  223. m_OnChangedAction?.Invoke(Count);
  224. }
  225. catch (Exception e)
  226. {
  227. Debug.LogError($"红点改变回调 try报错 请检查原因 {Key} {e}");
  228. }
  229. }
  230. #if UNITY_EDITOR || YIUIMACRO_REDDOT_STACK
  231. //因为有堆栈操作的需求 所以还是有可能 同一时间刷新2个节点 他们都有相同的路线时 会被刷新2次
  232. //因为需要这2次的堆栈操作信息 如果去掉堆栈 可以合并 TODO
  233. /// <summary>
  234. /// 添加操作堆栈
  235. /// 目前只有在编辑器下执行 以后可能修改成一个可开关的 这样在手机上也可以查看 debug模式
  236. /// </summary>
  237. private RedDotStack AddNewStack(
  238. ERedDotOSType osType,
  239. int originalCount,
  240. int changeCount,
  241. bool changeTips,
  242. FirstRedDotChangeData firstData)
  243. {
  244. var stack = new RedDotStack
  245. {
  246. Id = StackList.Count + 1,
  247. DataTime = DateTime.Now,
  248. StackTrace = new System.Diagnostics.StackTrace(true),
  249. RedDotOSType = osType,
  250. OriginalCount = originalCount,
  251. ChangeCount = changeCount,
  252. ChangeTips = changeTips,
  253. FirstData = firstData,
  254. };
  255. //新的在前 其他地方就不用翻转数据了
  256. StackList.Insert(0, stack);
  257. return stack;
  258. }
  259. private void AddStack(RedDotStack stack)
  260. {
  261. StackList.Insert(0, stack);
  262. }
  263. #endif
  264. }
  265. }