Newer
Older
DungeonShooting / DungeonShooting_Godot / src / framework / ui / UiBase.cs
  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. /// 如果 Ui 处于打开状态, 则每帧调用一次
  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 (IsDestroyed)
  104. {
  105. GD.PrintErr($"当前Ui: {UiName}已经被销毁!");
  106. return;
  107. }
  108. if (IsOpen)
  109. {
  110. return;
  111. }
  112.  
  113. IsOpen = true;
  114. Visible = true;
  115. OnShowUi();
  116. //子Ui调用显示
  117. if (_nestedUiSet != null)
  118. {
  119. foreach (var uiBase in _nestedUiSet)
  120. {
  121. uiBase.ShowUi();
  122. }
  123. }
  124. }
  125. /// <summary>
  126. /// 隐藏ui, 不会执行销毁
  127. /// </summary>
  128. public void HideUi()
  129. {
  130. if (IsDestroyed)
  131. {
  132. GD.PrintErr($"当前Ui: {UiName}已经被销毁!");
  133. return;
  134. }
  135. if (!IsOpen)
  136. {
  137. return;
  138. }
  139.  
  140. IsOpen = false;
  141. Visible = false;
  142. OnHideUi();
  143. //子Ui调用隐藏
  144. if (_nestedUiSet != null)
  145. {
  146. foreach (var uiBase in _nestedUiSet)
  147. {
  148. uiBase.HideUi();
  149. }
  150. }
  151. }
  152.  
  153. /// <summary>
  154. /// 关闭并销毁ui
  155. /// </summary>
  156. public void Destroy()
  157. {
  158. if (IsDestroyed)
  159. {
  160. return;
  161. }
  162. //记录ui关闭
  163. UiManager.RecordUi(this, UiManager.RecordType.Close);
  164. HideUi();
  165. IsDestroyed = true;
  166. OnDestroyUi();
  167. //子Ui调用销毁
  168. if (_nestedUiSet != null)
  169. {
  170. foreach (var uiBase in _nestedUiSet)
  171. {
  172. uiBase.ParentUi = null;
  173. uiBase.Destroy();
  174. }
  175. _nestedUiSet.Clear();
  176. }
  177. //销毁 IUiNodeScript
  178. if (_nodeScripts != null)
  179. {
  180. foreach (var uiNodeScript in _nodeScripts)
  181. {
  182. uiNodeScript.OnDestroy();
  183. }
  184. }
  185.  
  186. //在父Ui中移除当前Ui
  187. if (ParentUi != null)
  188. {
  189. ParentUi.RecordNestedUi(this, null, UiManager.RecordType.Close);
  190. }
  191. QueueFree();
  192. }
  193.  
  194. public sealed override void _Process(double delta)
  195. {
  196. if (!IsOpen)
  197. {
  198. return;
  199. }
  200. var newDelta = (float)delta;
  201. Process(newDelta);
  202. //协程更新
  203. if (_coroutineList != null)
  204. {
  205. ProxyCoroutineHandler.ProxyUpdateCoroutine(ref _coroutineList, newDelta);
  206. }
  207. }
  208.  
  209. /// <summary>
  210. /// 嵌套打开子ui
  211. /// </summary>
  212. public UiBase OpenNestedUi(string uiName, UiBase prevUi = null)
  213. {
  214. var packedScene = ResourceManager.Load<PackedScene>("res://" + GameConfig.UiPrefabDir + uiName + ".tscn");
  215. var uiBase = packedScene.Instantiate<UiBase>();
  216. uiBase.PrevUi = prevUi;
  217. AddChild(uiBase);
  218. RecordNestedUi(uiBase, null, UiManager.RecordType.Open);
  219. uiBase.OnCreateUi();
  220. uiBase.OnInitNestedUi();
  221. if (IsOpen)
  222. {
  223. uiBase.ShowUi();
  224. }
  225. return uiBase;
  226. }
  227.  
  228. /// <summary>
  229. /// 嵌套打开子ui
  230. /// </summary>
  231. public T OpenNestedUi<T>(string uiName, UiBase prevUi = null) where T : UiBase
  232. {
  233. return (T)OpenNestedUi(uiName, prevUi);
  234. }
  235.  
  236. /// <summary>
  237. /// 记录嵌套打开/关闭的UI
  238. /// </summary>
  239. public void RecordNestedUi(UiBase uiBase, IUiNode node, UiManager.RecordType type)
  240. {
  241. if (type == UiManager.RecordType.Open)
  242. {
  243. if (uiBase.ParentUi != null && uiBase.ParentUi != this)
  244. {
  245. GD.PrintErr($"子Ui:'{uiBase.UiName}'已经被其他Ui:'{uiBase.ParentUi.UiName}'嵌套打开!");
  246. uiBase.ParentUi.RecordNestedUi(uiBase, node, UiManager.RecordType.Close);
  247. }
  248. if (_nestedUiSet == null)
  249. {
  250. _nestedUiSet = new HashSet<UiBase>();
  251. }
  252.  
  253. uiBase.ParentUi = this;
  254. uiBase.ParentNode = node;
  255. _nestedUiSet.Add(uiBase);
  256. }
  257. else
  258. {
  259. if (uiBase.ParentUi == this)
  260. {
  261. uiBase.ParentUi = null;
  262. uiBase.ParentNode = null;
  263. }
  264. else
  265. {
  266. GD.PrintErr($"当前Ui:'{UiName}'没有嵌套打开子Ui:'{uiBase.UiName}'!");
  267. return;
  268. }
  269. if (_nestedUiSet == null)
  270. {
  271. return;
  272. }
  273. _nestedUiSet.Remove(uiBase);
  274. }
  275. }
  276.  
  277. /// <summary>
  278. /// 记录当前Ui包含的 IUiNodeScript 接口
  279. /// </summary>
  280. public void RecordUiNodeScript(IUiNodeScript nodeScript)
  281. {
  282. if (_nodeScripts == null)
  283. {
  284. _nodeScripts = new HashSet<IUiNodeScript>();
  285. }
  286. _nodeScripts.Add(nodeScript);
  287. }
  288.  
  289. /// <summary>
  290. /// 打开下一级Ui, 当前Ui会被隐藏
  291. /// </summary>
  292. /// <param name="uiName">下一级Ui的名称</param>
  293. public UiBase OpenNextUi(string uiName)
  294. {
  295. UiBase uiBase;
  296. if (ParentUi != null) //说明当前Ui是嵌套Ui
  297. {
  298. if (ParentNode != null) //子层级打开
  299. {
  300. uiBase = ParentNode.OpenNestedUi(uiName, this);
  301. }
  302. else
  303. {
  304. uiBase = ParentUi.OpenNestedUi(uiName, this);
  305. }
  306. }
  307. else //正常打开
  308. {
  309. uiBase = UiManager.OpenUi(uiName, this);
  310. }
  311. HideUi();
  312. return uiBase;
  313. }
  314. /// <summary>
  315. /// 打开下一级Ui, 当前Ui会被隐藏
  316. /// </summary>
  317. /// <param name="uiName">下一级Ui的名称</param>
  318. public T OpenNextUi<T>(string uiName) where T : UiBase
  319. {
  320. return (T)OpenNextUi(uiName);
  321. }
  322.  
  323. /// <summary>
  324. /// 返回上一级Ui, 当前Ui会被销毁
  325. /// </summary>
  326. public void OpenPrevUi()
  327. {
  328. Destroy();
  329. if (PrevUi == null)
  330. {
  331. GD.PrintErr($"Ui: {UiName} 没有记录上一级Ui!");
  332. }
  333. else
  334. {
  335. PrevUi.ShowUi();
  336. }
  337. }
  338.  
  339. public long StartCoroutine(IEnumerator able)
  340. {
  341. return ProxyCoroutineHandler.ProxyStartCoroutine(ref _coroutineList, able);
  342. }
  343. public void StopCoroutine(long coroutineId)
  344. {
  345. ProxyCoroutineHandler.ProxyStopCoroutine(ref _coroutineList, coroutineId);
  346. }
  347. public bool IsCoroutineOver(long coroutineId)
  348. {
  349. return ProxyCoroutineHandler.ProxyIsCoroutineOver(ref _coroutineList, coroutineId);
  350. }
  351. public void StopAllCoroutine()
  352. {
  353. ProxyCoroutineHandler.ProxyStopAllCoroutine(ref _coroutineList);
  354. }
  355. }