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