Newer
Older
DungeonShooting / DungeonShooting_Godot / src / framework / ui / UiBase.cs
@小李xl 小李xl on 26 Feb 2024 12 KB 制作大厅物体中
  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. //存放的IUiGrid对象
  71. private List<IUiGrid> _uiGrids;
  72.  
  73. public UiBase(string uiName)
  74. {
  75. UiName = uiName;
  76. //记录ui打开
  77. UiManager.RecordUi(this, UiManager.RecordType.Open);
  78. }
  79.  
  80. /// <summary>
  81. /// 创建当前ui时调用
  82. /// </summary>
  83. public virtual void OnCreateUi()
  84. {
  85. }
  86. /// <summary>
  87. /// 用于初始化打开的子Ui, 在 OnCreateUi() 之后调用
  88. /// </summary>
  89. public virtual void OnInitNestedUi()
  90. {
  91. }
  92.  
  93. /// <summary>
  94. /// 当前ui显示时调用
  95. /// </summary>
  96. public virtual void OnShowUi()
  97. {
  98. }
  99.  
  100. /// <summary>
  101. /// 当前ui隐藏时调用
  102. /// </summary>
  103. public virtual void OnHideUi()
  104. {
  105. }
  106.  
  107. /// <summary>
  108. /// 销毁当前ui时调用
  109. /// </summary>
  110. public virtual void OnDestroyUi()
  111. {
  112. }
  113.  
  114. /// <summary>
  115. /// 如果 Ui 处于打开状态, 则每帧调用一次
  116. /// </summary>
  117. public virtual void Process(float delta)
  118. {
  119. }
  120.  
  121. /// <summary>
  122. /// 显示ui
  123. /// </summary>
  124. public void ShowUi()
  125. {
  126. if (IsDestroyed)
  127. {
  128. Debug.LogError($"当前Ui: {UiName}已经被销毁!");
  129. return;
  130. }
  131. Visible = true;
  132. if (IsOpen)
  133. {
  134. return;
  135. }
  136.  
  137. _nestedOpen = true;
  138. IsOpen = true;
  139. OnShowUi();
  140. if (OnShowUiEvent != null)
  141. {
  142. OnShowUiEvent();
  143. }
  144. //子Ui调用显示
  145. if (_nestedUiSet != null)
  146. {
  147. foreach (var uiBase in _nestedUiSet)
  148. {
  149. if (uiBase._nestedOpen || uiBase.Visible)
  150. {
  151. uiBase.ShowUi();
  152. }
  153. }
  154. }
  155. }
  156. /// <summary>
  157. /// 隐藏ui, 不会执行销毁
  158. /// </summary>
  159. public void HideUi()
  160. {
  161. if (IsDestroyed)
  162. {
  163. Debug.LogError($"当前Ui: {UiName}已经被销毁!");
  164. return;
  165. }
  166.  
  167. Visible = false;
  168. if (!IsOpen)
  169. {
  170. return;
  171. }
  172.  
  173. _nestedOpen = false;
  174. IsOpen = false;
  175. OnHideUi();
  176. if (OnHideUiEvent != null)
  177. {
  178. OnHideUiEvent();
  179. }
  180. //子Ui调用隐藏
  181. if (_nestedUiSet != null)
  182. {
  183. foreach (var uiBase in _nestedUiSet)
  184. {
  185. if (uiBase._nestedOpen)
  186. {
  187. uiBase.HideUi();
  188. uiBase._nestedOpen = true;
  189. }
  190. }
  191. }
  192. }
  193.  
  194. /// <summary>
  195. /// 关闭并销毁ui
  196. /// </summary>
  197. public void Destroy()
  198. {
  199. if (IsDestroyed)
  200. {
  201. return;
  202. }
  203. //记录ui关闭
  204. UiManager.RecordUi(this, UiManager.RecordType.Close);
  205. HideUi();
  206. IsDestroyed = true;
  207. OnDestroyUi();
  208. if (OnDestroyUiEvent != null)
  209. {
  210. OnDestroyUiEvent();
  211. }
  212.  
  213. if (_uiGrids != null)
  214. {
  215. foreach (var uiGrid in _uiGrids)
  216. {
  217. uiGrid.Destroy();
  218. }
  219. _uiGrids.Clear();
  220. }
  221. //子Ui调用销毁
  222. if (_nestedUiSet != null)
  223. {
  224. foreach (var uiBase in _nestedUiSet)
  225. {
  226. uiBase.ParentUi = null;
  227. uiBase.Destroy();
  228. }
  229. _nestedUiSet.Clear();
  230. }
  231. //销毁 IUiNodeScript
  232. if (_nodeScripts != null)
  233. {
  234. foreach (var uiNodeScript in _nodeScripts)
  235. {
  236. uiNodeScript.OnDestroy();
  237. }
  238. }
  239.  
  240. //在父Ui中移除当前Ui
  241. if (ParentUi != null)
  242. {
  243. ParentUi.RecordNestedUi(this, null, UiManager.RecordType.Close);
  244. }
  245.  
  246. RemoveAllEventListener();
  247. QueueFree();
  248. }
  249.  
  250. /// <summary>
  251. /// 添加监听事件, 所有事件会在当前 ui 销毁时自动销毁
  252. /// </summary>
  253. /// <param name="eventType">事件类型</param>
  254. /// <param name="callback">回调函数</param>
  255. public void AddEventListener(EventEnum eventType, Action<object> callback)
  256. {
  257. if (_eventFactory == null)
  258. {
  259. _eventFactory = new EventFactory();
  260. }
  261. _eventFactory.AddEventListener(eventType, callback);
  262. }
  263. /// <summary>
  264. /// 移除所有的监听事件
  265. /// </summary>
  266. public void RemoveAllEventListener()
  267. {
  268. if (_eventFactory != null)
  269. {
  270. _eventFactory.RemoveAllEventListener();
  271. }
  272. }
  273.  
  274. /// <summary>
  275. /// 创建一个UiGrid对象, 该Ui对象会在Ui销毁时自动销毁
  276. /// </summary>
  277. /// <param name="template">模板对象</param>
  278. /// <typeparam name="TNode">模板对象类型</typeparam>
  279. /// <typeparam name="TData">存放的数据类型</typeparam>
  280. /// <typeparam name="TCell">Cell处理类</typeparam>
  281. public UiGrid<TNode, TData> CreateUiGrid<TNode, TData, TCell>(TNode template) where TNode : IUiCellNode where TCell : UiCell<TNode, TData>
  282. {
  283. var uiGrid = new UiGrid<TNode, TData>(template, typeof(TCell));
  284. if (_uiGrids == null)
  285. {
  286. _uiGrids = new List<IUiGrid>();
  287. }
  288. _uiGrids.Add(uiGrid);
  289. return uiGrid;
  290. }
  291.  
  292. /// <summary>
  293. /// 创建一个UiGrid对象, 该Ui对象会在Ui销毁时自动销毁
  294. /// </summary>
  295. /// <param name="template">模板对象</param>
  296. /// <param name="parent">父节点</param>
  297. /// <typeparam name="TNode">模板对象类型</typeparam>
  298. /// <typeparam name="TData">存放的数据类型</typeparam>
  299. /// <typeparam name="TCell">Cell处理类</typeparam>
  300. public UiGrid<TNode, TData> CreateUiGrid<TNode, TData, TCell>(TNode template, Node parent) where TNode : IUiCellNode where TCell : UiCell<TNode, TData>
  301. {
  302. var uiGrid = new UiGrid<TNode, TData>(template, parent, typeof(TCell));
  303. if (_uiGrids == null)
  304. {
  305. _uiGrids = new List<IUiGrid>();
  306. }
  307. _uiGrids.Add(uiGrid);
  308. return uiGrid;
  309. }
  310. public sealed override void _Process(double delta)
  311. {
  312. if (!IsOpen)
  313. {
  314. return;
  315. }
  316. var newDelta = (float)delta;
  317. Process(newDelta);
  318. //协程更新
  319. ProxyCoroutineHandler.ProxyUpdateCoroutine(ref _coroutineList, newDelta);
  320. }
  321.  
  322. /// <summary>
  323. /// 嵌套打开子ui
  324. /// </summary>
  325. public UiBase OpenNestedUi(string uiName, UiBase prevUi = null)
  326. {
  327. var packedScene = ResourceManager.Load<PackedScene>("res://" + GameConfig.UiPrefabDir + uiName + ".tscn");
  328. var uiBase = packedScene.Instantiate<UiBase>();
  329. uiBase.Visible = false;
  330. uiBase.PrevUi = prevUi;
  331. AddChild(uiBase);
  332. RecordNestedUi(uiBase, null, UiManager.RecordType.Open);
  333. uiBase.OnCreateUi();
  334. uiBase.OnInitNestedUi();
  335. if (IsOpen)
  336. {
  337. uiBase.ShowUi();
  338. }
  339. return uiBase;
  340. }
  341.  
  342. /// <summary>
  343. /// 嵌套打开子ui
  344. /// </summary>
  345. public T OpenNestedUi<T>(string uiName, UiBase prevUi = null) where T : UiBase
  346. {
  347. return (T)OpenNestedUi(uiName, prevUi);
  348. }
  349.  
  350. /// <summary>
  351. /// 记录嵌套打开/关闭的UI
  352. /// </summary>
  353. public void RecordNestedUi(UiBase uiBase, IUiNode node, UiManager.RecordType type)
  354. {
  355. if (type == UiManager.RecordType.Open)
  356. {
  357. if (uiBase.ParentUi != null && uiBase.ParentUi != this)
  358. {
  359. Debug.LogError($"子Ui:'{uiBase.UiName}'已经被其他Ui:'{uiBase.ParentUi.UiName}'嵌套打开!");
  360. uiBase.ParentUi.RecordNestedUi(uiBase, node, UiManager.RecordType.Close);
  361. }
  362. if (_nestedUiSet == null)
  363. {
  364. _nestedUiSet = new HashSet<UiBase>();
  365. }
  366.  
  367. uiBase.ParentUi = this;
  368. uiBase.ParentNode = node;
  369. _nestedUiSet.Add(uiBase);
  370. }
  371. else
  372. {
  373. if (uiBase.ParentUi == this)
  374. {
  375. uiBase.ParentUi = null;
  376. uiBase.ParentNode = null;
  377. }
  378. else
  379. {
  380. Debug.LogError($"当前Ui:'{UiName}'没有嵌套打开子Ui:'{uiBase.UiName}'!");
  381. return;
  382. }
  383. if (_nestedUiSet == null)
  384. {
  385. return;
  386. }
  387. _nestedUiSet.Remove(uiBase);
  388. }
  389. }
  390.  
  391. /// <summary>
  392. /// 记录当前Ui包含的 IUiNodeScript 接口
  393. /// </summary>
  394. public void RecordUiNodeScript(IUiNodeScript nodeScript)
  395. {
  396. if (_nodeScripts == null)
  397. {
  398. _nodeScripts = new HashSet<IUiNodeScript>();
  399. }
  400. _nodeScripts.Add(nodeScript);
  401. }
  402.  
  403. /// <summary>
  404. /// 打开下一级Ui, 当前Ui会被隐藏
  405. /// </summary>
  406. /// <param name="uiName">下一级Ui的名称</param>
  407. public UiBase OpenNextUi(string uiName)
  408. {
  409. UiBase uiBase;
  410. if (ParentUi != null) //说明当前Ui是嵌套Ui
  411. {
  412. if (ParentNode != null) //子层级打开
  413. {
  414. uiBase = ParentNode.OpenNestedUi(uiName, this);
  415. }
  416. else
  417. {
  418. uiBase = ParentUi.OpenNestedUi(uiName, this);
  419. }
  420. }
  421. else //正常打开
  422. {
  423. uiBase = UiManager.OpenUi(uiName, this);
  424. }
  425. HideUi();
  426. return uiBase;
  427. }
  428. /// <summary>
  429. /// 打开下一级Ui, 当前Ui会被隐藏
  430. /// </summary>
  431. /// <param name="uiName">下一级Ui的名称</param>
  432. public T OpenNextUi<T>(string uiName) where T : UiBase
  433. {
  434. return (T)OpenNextUi(uiName);
  435. }
  436.  
  437. /// <summary>
  438. /// 返回上一级Ui, 当前Ui会被销毁
  439. /// </summary>
  440. public void OpenPrevUi()
  441. {
  442. Destroy();
  443. if (PrevUi == null)
  444. {
  445. Debug.LogError($"Ui: {UiName} 没有记录上一级Ui!");
  446. }
  447. else
  448. {
  449. PrevUi.ShowUi();
  450. }
  451. }
  452.  
  453. public long StartCoroutine(IEnumerator able)
  454. {
  455. return ProxyCoroutineHandler.ProxyStartCoroutine(ref _coroutineList, able);
  456. }
  457. public void StopCoroutine(long coroutineId)
  458. {
  459. ProxyCoroutineHandler.ProxyStopCoroutine(ref _coroutineList, coroutineId);
  460. }
  461. public bool IsCoroutineOver(long coroutineId)
  462. {
  463. return ProxyCoroutineHandler.ProxyIsCoroutineOver(ref _coroutineList, coroutineId);
  464. }
  465. public void StopAllCoroutine()
  466. {
  467. ProxyCoroutineHandler.ProxyStopAllCoroutine(ref _coroutineList);
  468. }
  469. }