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