Newer
Older
DungeonShooting / DungeonShooting_Godot / src / framework / ui / UiBase.cs
@小李xl 小李xl on 22 Aug 2023 8 KB 完成保存与未保存状态
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using Godot;
  4.  
  5. /// <summary>
  6. /// Ui 基类
  7. /// </summary>
  8. public abstract partial class UiBase : Control, IDestroy, ICoroutine
  9. {
  10. /// <summary>
  11. /// 当前 UI 所属层级
  12. /// </summary>
  13. [Export]
  14. public UiLayer Layer = UiLayer.Middle;
  15.  
  16. /// <summary>
  17. /// ui名称
  18. /// </summary>
  19. public string UiName { get; }
  20. /// <summary>
  21. /// 是否已经打开ui
  22. /// </summary>
  23. public bool IsOpen { get; private set; } = false;
  24.  
  25. public bool IsDestroyed { get; private set; }
  26.  
  27. /// <summary>
  28. /// 负责记录上一个Ui
  29. /// </summary>
  30. public UiBase PrevUi { get; set; }
  31.  
  32. /// <summary>
  33. /// 所属父级Ui, 仅当通过 UiNode.OpenNestedUi() 打开时才会赋值<br/>
  34. /// 注意: 如果是在预制体中放置的子 Ui, 那么子 Ui 的该属性会在 父 Ui 的 OnCreateUi() 之后赋值
  35. /// </summary>
  36. public UiBase ParentUi { get; private set; }
  37. /// <summary>
  38. /// 所属父级节点, 仅当通过 UiNode.OpenNestedUi() 打开时才会赋值<br/>
  39. /// 注意: 如果是在预制体中放置的子 Ui, 那么子 Ui 的该属性会在 父 Ui 的 OnCreateUi() 之后赋值
  40. /// </summary>
  41. public IUiNode ParentNode { get; private set; }
  42.  
  43. //开启的协程
  44. private List<CoroutineData> _coroutineList;
  45. //嵌套打开的Ui列表
  46. private HashSet<UiBase> _nestedUiSet;
  47. //当前Ui包含的 IUiNodeScript 接口, 关闭Ui是需要调用 IUiNodeScript.OnDestroy()
  48. private HashSet<IUiNodeScript> _nodeScripts;
  49.  
  50. public UiBase(string uiName)
  51. {
  52. UiName = uiName;
  53. //记录ui打开
  54. UiManager.RecordUi(this, UiManager.RecordType.Open);
  55. }
  56.  
  57. /// <summary>
  58. /// 创建当前ui时调用
  59. /// </summary>
  60. public virtual void OnCreateUi()
  61. {
  62. }
  63. /// <summary>
  64. /// 用于初始化打开的子Ui, 在 OnCreateUi() 之后调用
  65. /// </summary>
  66. public virtual void OnInitNestedUi()
  67. {
  68. }
  69.  
  70. /// <summary>
  71. /// 当前ui显示时调用
  72. /// </summary>
  73. public virtual void OnShowUi()
  74. {
  75. }
  76.  
  77. /// <summary>
  78. /// 当前ui隐藏时调用
  79. /// </summary>
  80. public virtual void OnHideUi()
  81. {
  82. }
  83.  
  84. /// <summary>
  85. /// 销毁当前ui时调用
  86. /// </summary>
  87. public virtual void OnDestroyUi()
  88. {
  89. }
  90.  
  91. /// <summary>
  92. /// 每帧调用一次
  93. /// </summary>
  94. public virtual void Process(float delta)
  95. {
  96. }
  97.  
  98. /// <summary>
  99. /// 显示ui
  100. /// </summary>
  101. public void ShowUi()
  102. {
  103. if (IsOpen)
  104. {
  105. return;
  106. }
  107.  
  108. IsOpen = true;
  109. Visible = true;
  110. OnShowUi();
  111. //子Ui调用显示
  112. if (_nestedUiSet != null)
  113. {
  114. foreach (var uiBase in _nestedUiSet)
  115. {
  116. uiBase.ShowUi();
  117. }
  118. }
  119. }
  120. /// <summary>
  121. /// 隐藏ui, 不会执行销毁
  122. /// </summary>
  123. public void HideUi()
  124. {
  125. if (!IsOpen)
  126. {
  127. return;
  128. }
  129.  
  130. IsOpen = false;
  131. Visible = false;
  132. OnHideUi();
  133. //子Ui调用隐藏
  134. if (_nestedUiSet != null)
  135. {
  136. foreach (var uiBase in _nestedUiSet)
  137. {
  138. uiBase.HideUi();
  139. }
  140. }
  141. }
  142.  
  143. /// <summary>
  144. /// 关闭并销毁ui
  145. /// </summary>
  146. public void Destroy()
  147. {
  148. if (IsDestroyed)
  149. {
  150. return;
  151. }
  152. //记录ui关闭
  153. UiManager.RecordUi(this, UiManager.RecordType.Close);
  154. HideUi();
  155. IsDestroyed = true;
  156. OnDestroyUi();
  157. //子Ui调用销毁
  158. if (_nestedUiSet != null)
  159. {
  160. foreach (var uiBase in _nestedUiSet)
  161. {
  162. uiBase.ParentUi = null;
  163. uiBase.Destroy();
  164. }
  165. _nestedUiSet.Clear();
  166. }
  167. //销毁 IUiNodeScript
  168. if (_nodeScripts != null)
  169. {
  170. foreach (var uiNodeScript in _nodeScripts)
  171. {
  172. uiNodeScript.OnDestroy();
  173. }
  174. }
  175.  
  176. //在父Ui中移除当前Ui
  177. if (ParentUi != null)
  178. {
  179. ParentUi.RecordNestedUi(this, null, UiManager.RecordType.Close);
  180. }
  181. QueueFree();
  182. }
  183.  
  184. public sealed override void _Process(double delta)
  185. {
  186. if (!IsOpen)
  187. {
  188. return;
  189. }
  190. var newDelta = (float)delta;
  191. Process(newDelta);
  192. //协程更新
  193. if (_coroutineList != null)
  194. {
  195. ProxyCoroutineHandler.ProxyUpdateCoroutine(ref _coroutineList, newDelta);
  196. }
  197. }
  198.  
  199. /// <summary>
  200. /// 嵌套打开子ui
  201. /// </summary>
  202. public UiBase OpenNestedUi(string uiName, UiBase prevUi = null)
  203. {
  204. var packedScene = ResourceManager.Load<PackedScene>("res://" + GameConfig.UiPrefabDir + uiName + ".tscn");
  205. var uiBase = packedScene.Instantiate<UiBase>();
  206. uiBase.PrevUi = prevUi;
  207. AddChild(uiBase);
  208. RecordNestedUi(uiBase, null, UiManager.RecordType.Open);
  209. uiBase.OnCreateUi();
  210. uiBase.OnInitNestedUi();
  211. if (IsOpen)
  212. {
  213. uiBase.ShowUi();
  214. }
  215. return uiBase;
  216. }
  217.  
  218. /// <summary>
  219. /// 嵌套打开子ui
  220. /// </summary>
  221. public T OpenNestedUi<T>(string uiName, UiBase prevUi = null) where T : UiBase
  222. {
  223. return (T)OpenNestedUi(uiName, prevUi);
  224. }
  225.  
  226. /// <summary>
  227. /// 记录嵌套打开/关闭的UI
  228. /// </summary>
  229. public void RecordNestedUi(UiBase uiBase, IUiNode node, UiManager.RecordType type)
  230. {
  231. if (type == UiManager.RecordType.Open)
  232. {
  233. if (uiBase.ParentUi != null && uiBase.ParentUi != this)
  234. {
  235. GD.PrintErr($"子Ui:'{uiBase.UiName}'已经被其他Ui:'{uiBase.ParentUi.UiName}'嵌套打开!");
  236. uiBase.ParentUi.RecordNestedUi(uiBase, node, UiManager.RecordType.Close);
  237. }
  238. if (_nestedUiSet == null)
  239. {
  240. _nestedUiSet = new HashSet<UiBase>();
  241. }
  242.  
  243. uiBase.ParentUi = this;
  244. uiBase.ParentNode = node;
  245. _nestedUiSet.Add(uiBase);
  246. }
  247. else
  248. {
  249. if (uiBase.ParentUi == this)
  250. {
  251. uiBase.ParentUi = null;
  252. uiBase.ParentNode = null;
  253. }
  254. else
  255. {
  256. GD.PrintErr($"当前Ui:'{UiName}'没有嵌套打开子Ui:'{uiBase.UiName}'!");
  257. return;
  258. }
  259. if (_nestedUiSet == null)
  260. {
  261. return;
  262. }
  263. _nestedUiSet.Remove(uiBase);
  264. }
  265. }
  266.  
  267. /// <summary>
  268. /// 记录当前Ui包含的 IUiNodeScript 接口
  269. /// </summary>
  270. public void RecordUiNodeScript(IUiNodeScript nodeScript)
  271. {
  272. if (_nodeScripts == null)
  273. {
  274. _nodeScripts = new HashSet<IUiNodeScript>();
  275. }
  276. _nodeScripts.Add(nodeScript);
  277. }
  278.  
  279. /// <summary>
  280. /// 打开下一级Ui, 当前Ui会被隐藏
  281. /// </summary>
  282. /// <param name="uiName">下一级Ui的名称</param>
  283. public UiBase OpenNextUi(string uiName)
  284. {
  285. UiBase uiBase;
  286. if (ParentUi != null) //说明当前Ui是嵌套Ui
  287. {
  288. if (ParentNode != null) //子层级打开
  289. {
  290. uiBase = ParentNode.OpenNestedUi(uiName, this);
  291. }
  292. else
  293. {
  294. uiBase = ParentUi.OpenNestedUi(uiName, this);
  295. }
  296. }
  297. else //正常打开
  298. {
  299. uiBase = UiManager.OpenUi(uiName, this);
  300. }
  301. HideUi();
  302. return uiBase;
  303. }
  304. /// <summary>
  305. /// 打开下一级Ui, 当前Ui会被隐藏
  306. /// </summary>
  307. /// <param name="uiName">下一级Ui的名称</param>
  308. public T OpenNextUi<T>(string uiName) where T : UiBase
  309. {
  310. return (T)OpenNextUi(uiName);
  311. }
  312.  
  313. /// <summary>
  314. /// 返回上一级Ui, 当前Ui会被销毁
  315. /// </summary>
  316. public void OpenPrevUi()
  317. {
  318. Destroy();
  319. if (PrevUi == null)
  320. {
  321. GD.PrintErr($"Ui: {UiName} 没有记录上一级Ui!");
  322. }
  323. else
  324. {
  325. PrevUi.ShowUi();
  326. }
  327. }
  328.  
  329. public long StartCoroutine(IEnumerator able)
  330. {
  331. return ProxyCoroutineHandler.ProxyStartCoroutine(ref _coroutineList, able);
  332. }
  333. public void StopCoroutine(long coroutineId)
  334. {
  335. ProxyCoroutineHandler.ProxyStopCoroutine(ref _coroutineList, coroutineId);
  336. }
  337. public void StopAllCoroutine()
  338. {
  339. ProxyCoroutineHandler.ProxyStopAllCoroutine(ref _coroutineList);
  340. }
  341. }