UIManager.ts 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. /** @format */
  2. /**
  3. * UIManager界面管理类
  4. *
  5. * 1.打开界面,根据配置自动加载界面、调用初始化、播放打开动画、隐藏其他界面、屏蔽下方界面点击
  6. * 2.关闭界面,根据配置自动关闭界面、播放关闭动画、恢复其他界面
  7. * 3.切换界面,与打开界面类似,但是是将当前栈顶的界面切换成新的界面(先关闭再打开)
  8. * 4.提供界面缓存功能
  9. *
  10. */
  11. import {BaseUI, UIShowTypes} from '../ui/BaseUI'
  12. import {IUIConfig, UICfg} from '../config/UICfg'
  13. import {ccUtils} from '../utils/ccUtils'
  14. import {UI} from '../enums/UI'
  15. import {Data, Mgr} from '../GameControl'
  16. import {IOperateNeed} from '../interface/GlobalInterface'
  17. import {IMessage, IReward, IRewardNty, ItemUICfg} from '../interface/UIInterface'
  18. import {SOUND} from '../enums/Sound'
  19. import {GOODS} from '../enums/Enum'
  20. import {TopCell} from '../cell/TopCell'
  21. import {FootCell} from '../cell/FootCell'
  22. /** UI栈结构体 */
  23. export interface UIInfo {
  24. uiID: number
  25. baseUI: BaseUI
  26. uiArgs: any
  27. uiCfg: IUIConfig
  28. preventNode?: cc.Node
  29. aniNode?: cc.Node
  30. zOrder?: number
  31. isClose?: boolean
  32. hideTime?: number
  33. prefab?: cc.Prefab
  34. }
  35. export interface GroupUIInfo {
  36. uiID: number
  37. uiArgs?: any
  38. }
  39. export class UIManager {
  40. /** 是否正在关闭UI */
  41. private isClosing = false
  42. /** 是否正在打开UI */
  43. private isOpening = false
  44. /** UI界面缓存(key为UIId,value为BaseUI节点)*/
  45. private UICache: {[UIId: number]: UIInfo} = {}
  46. /** UI界面栈({UIID + BaseUI + UIArgs}数组)*/
  47. private UIStack: UIInfo[] = []
  48. /** UI待打开列表 */
  49. private UIOpenQueue: UIInfo[] = []
  50. /** UI待关闭列表 */
  51. private UICloseQueue: number[] = []
  52. /** UI配置 */
  53. private releaseTimeID: any
  54. /** 打开一组UI */
  55. private groupUIs: GroupUIInfo[] = []
  56. private curGroupUI: GroupUIInfo = null
  57. public prefabBundle: cc.AssetManager.Bundle = null
  58. /** UI打开前回调 */
  59. public uiOpenBeforeDelegate: (uiID: number, preUIId: number) => void = null
  60. /** UI打开回调 */
  61. public uiOpenDelegate: (uiID: number, preUIId: number) => void = null
  62. /** UI关闭回调 */
  63. public uiCloseDelegate: (uiID: number) => void = null
  64. private topNode: cc.Node
  65. private footNode: cc.Node
  66. public async init() {
  67. if (this.releaseTimeID) clearInterval(this.releaseTimeID)
  68. this.releaseTimeID = setInterval(this.cleanUI.bind(this), 15000)
  69. Data.main.texBundle = await ccUtils.getBundleAsync('texture')
  70. this.prefabBundle = await ccUtils.getBundleAsync('prefab')
  71. //加载全局通用预制体
  72. let itemsPrefabArr = (await Mgr.res.loadDirSync(this.prefabBundle, 'items', cc.Prefab)) as cc.Prefab[]
  73. for (let j = 0; j < itemsPrefabArr.length; j++) {
  74. Data.main.itemsPrefabMap.set(itemsPrefabArr[j].name, itemsPrefabArr[j])
  75. let pool = Data.main.itemsPoolMap.get(itemsPrefabArr[j].name)
  76. if (!pool) {
  77. pool = new cc.NodePool()
  78. Data.main.itemsPoolMap.set(itemsPrefabArr[j].name, pool)
  79. }
  80. }
  81. //顶部预制体
  82. let topPrefab = (await Mgr.res.loadSync(this.prefabBundle, 'cell/topCell', cc.Prefab)) as cc.Prefab
  83. this.topNode = cc.instantiate(topPrefab)
  84. //底部预制体
  85. let footPrefab = (await Mgr.res.loadSync(this.prefabBundle, 'cell/footCell', cc.Prefab)) as cc.Prefab
  86. this.footNode = cc.instantiate(footPrefab)
  87. return this.prefabBundle != null
  88. }
  89. /****************** 私有方法,UIManager内部的功能和基础规则 *******************/
  90. /**
  91. * 添加防触摸层
  92. * @param zOrder 屏蔽层的层级
  93. */
  94. private preventTouch(zOrder: number) {
  95. let node = new cc.Node()
  96. node.name = 'preventTouch'
  97. node.setContentSize(cc.winSize)
  98. node.on(
  99. cc.Node.EventType.TOUCH_START,
  100. (event: cc.Event.EventCustom) => {
  101. event.stopPropagation()
  102. },
  103. node,
  104. )
  105. let child = cc.director.getScene().getChildByName('Canvas')
  106. child.addChild(node, zOrder)
  107. return node
  108. }
  109. /** 自动执行下一个待关闭或待打开的界面 */
  110. private autoExecNextUI() {
  111. // 逻辑上是先关后开
  112. if (this.UICloseQueue.length > 0) {
  113. let closeUI = this.UICloseQueue[0]
  114. this.UICloseQueue.splice(0, 1)
  115. this.hide(closeUI)
  116. } else if (this.UIOpenQueue.length > 0) {
  117. let uiQueueInfo = this.UIOpenQueue[0]
  118. this.UIOpenQueue.splice(0, 1)
  119. this.show(uiQueueInfo.uiID, uiQueueInfo.uiArgs)
  120. }
  121. }
  122. /**
  123. * 自动检测动画组件以及特定动画,如存在则播放动画,无论动画是否播放,都执行回调
  124. * @param aniName 动画名
  125. * @param aniOverCallback 动画播放完成回调
  126. */
  127. private autoExecAnimation(baseUI: BaseUI, aniName: string, aniOverCallback: () => void) {
  128. // 暂时先省略动画播放的逻辑
  129. let node = baseUI.node
  130. switch (aniName) {
  131. case 'uiOpen':
  132. if (
  133. baseUI.showType >= UIShowTypes.UIAddition &&
  134. ![UI.LoadingUI, UI.TipUI, UI.PowerUpUI, UI.GameLoadingUI].includes(baseUI.uiID)
  135. ) {
  136. Mgr.audio.playSFX(SOUND.uiPop)
  137. node.setScale(0)
  138. node.opacity = 0
  139. cc.tween(node)
  140. .to(0.3, {scale: 1.1}, {easing: 'backOut'}) // 缩放动作,0.3秒内从 0 缩放到 1,使用 backOut 缓动效果
  141. .to(0.3, {opacity: 255}) // 渐显动作,0.1秒内从透明度 0 到 255
  142. .union()
  143. .to(0.1, {scale: 1}) // 缩小一点,形成回弹效果
  144. .call(aniOverCallback)
  145. .start()
  146. } else {
  147. aniOverCallback()
  148. }
  149. break
  150. case 'uiClose':
  151. aniOverCallback()
  152. break
  153. }
  154. }
  155. /**
  156. * 异步加载一个UI的prefab,成功加载了一个prefab之后
  157. * @param uiID 界面id
  158. * @param processCallback 加载进度回调
  159. * @param completeCallback 加载完成回调
  160. * @param uiArgs 初始化参数
  161. */
  162. private getOrCreateUI(
  163. uiID: number,
  164. processCallback: any,
  165. completeCallback: (baseUI: BaseUI, prefab?: cc.Prefab) => void,
  166. uiArgs: any,
  167. ): void {
  168. // 如果找到缓存对象,则直接返回
  169. let baseUI: BaseUI
  170. let cacheUIInfo = this.UICache[uiID]
  171. if (cacheUIInfo) {
  172. completeCallback(cacheUIInfo.baseUI)
  173. return
  174. }
  175. // 找到UI配置
  176. let uiPath = UICfg.get(uiID).url
  177. if (null == uiPath) {
  178. cc.log(`getOrCreateUI ${uiID} faile, prefab conf not found!`)
  179. completeCallback(null)
  180. return
  181. }
  182. this.prefabBundle.load(uiPath, cc.Prefab, processCallback, (err: Error, prefab: cc.Prefab) => {
  183. // 检查加载资源错误
  184. if (err) {
  185. cc.error(`getOrCreateUI loadRes ${uiID} faile, path: ${uiPath} error: ${err}`)
  186. completeCallback(null)
  187. return
  188. }
  189. // 检查实例化错误
  190. let uiNode: cc.Node = cc.instantiate(prefab)
  191. if (null == uiNode) {
  192. cc.error(`getOrCreateUI instantiate ${uiID} faile, path: ${uiPath}`)
  193. completeCallback(null)
  194. cc.assetManager.releaseAsset(prefab)
  195. return
  196. }
  197. // 检查组件获取错误
  198. baseUI = uiNode.getComponent(BaseUI)
  199. if (null == baseUI) {
  200. cc.error(`getOrCreateUI getComponent ${uiID} faile, path: ${uiPath}`)
  201. uiNode.destroy()
  202. completeCallback(null)
  203. cc.assetManager.releaseAsset(prefab)
  204. return
  205. }
  206. baseUI.uiID = uiID
  207. baseUI.init(uiArgs)
  208. completeCallback(baseUI, prefab)
  209. })
  210. }
  211. /**
  212. * UI被打开时回调,对UI进行初始化设置,刷新其他界面的显示,并根据
  213. * @param uiID 哪个界面被打开了
  214. * @param baseUI 界面对象
  215. * @param uiInfo 界面栈对应的信息结构
  216. * @param uiArgs 界面初始化参数
  217. */
  218. private onUIOpen(uiID: number, baseUI: BaseUI, uiInfo: UIInfo, uiArgs: any) {
  219. if (null == baseUI) {
  220. return
  221. }
  222. // 激活界面
  223. uiInfo.baseUI = baseUI
  224. baseUI.node.active = true
  225. let realZIndex = uiInfo.zOrder + baseUI.showType
  226. baseUI.node.zIndex = realZIndex
  227. if (baseUI.prevent) {
  228. let blockInputEvents = baseUI.node.getChildByName('blockInputEvents')
  229. if (!blockInputEvents) {
  230. blockInputEvents = new cc.Node()
  231. blockInputEvents.name = 'blockInputEvents'
  232. blockInputEvents.setContentSize(cc.winSize)
  233. blockInputEvents.addComponent(cc.BlockInputEvents)
  234. baseUI.node.addChild(blockInputEvents, -9999)
  235. }
  236. // 这里直接给node添加BlockInputEvents导致子节点setSwallowTouches无法吞噬,改成上面的实现方式
  237. // baseUI.node.setContentSize(cc.winSize)
  238. // if (!baseUI.node.getComponent(cc.BlockInputEvents)) baseUI.node.addComponent(cc.BlockInputEvents)
  239. }
  240. // 快速关闭界面的设置,绑定界面中的background,实现快速关闭
  241. if (baseUI.quickClose) {
  242. let backGround = baseUI.node.getChildByName('background')
  243. if (!backGround) {
  244. backGround = new cc.Node()
  245. backGround.name = 'background'
  246. backGround.setContentSize(cc.winSize)
  247. baseUI.node.addChild(backGround, -1)
  248. }
  249. backGround.targetOff(cc.Node.EventType.TOUCH_START)
  250. backGround.on(
  251. cc.Node.EventType.TOUCH_START,
  252. (event: cc.Event.EventCustom) => {
  253. event.stopPropagation()
  254. this.hideBaseUI(baseUI)
  255. },
  256. backGround,
  257. )
  258. }
  259. // 添加到场景中
  260. let child = cc.director.getScene().getChildByName('Canvas')
  261. child.addChild(baseUI.node)
  262. // 从那个界面打开的
  263. let fromUIID = 0
  264. if (this.UIStack.length > 1) {
  265. fromUIID = this.UIStack[this.UIStack.length - 2].uiID
  266. }
  267. // 打开界面之前回调
  268. if (this.uiOpenBeforeDelegate) {
  269. this.uiOpenBeforeDelegate(uiID, fromUIID)
  270. }
  271. // 执行onOpen回调 下一帧执行保证onLoad onEnable先执行
  272. baseUI.scheduleOnce(() => {
  273. if (!uiInfo.isClose && baseUI.node.activeInHierarchy) baseUI.onShow(uiArgs, fromUIID)
  274. })
  275. this.autoExecAnimation(baseUI, 'uiOpen', () => {
  276. baseUI.onOpenAniOver()
  277. if (this.uiOpenDelegate) {
  278. this.uiOpenDelegate(uiID, fromUIID)
  279. }
  280. })
  281. }
  282. /** 打开界面并添加到界面栈中 */
  283. public show(uiID: number, uiArgs: any = null, progressCallback: Function = null): void {
  284. let uiInfo: UIInfo = {
  285. uiCfg: UICfg.get(uiID),
  286. uiID: uiID,
  287. uiArgs: uiArgs,
  288. baseUI: null,
  289. }
  290. if (this.isOpening || this.isClosing) {
  291. // 插入待打开队列
  292. let openIndex = this.UIOpenQueue.findIndex(item => item.uiID == uiID)
  293. if (openIndex >= 0) this.UIOpenQueue.splice(openIndex, 1)
  294. this.UIOpenQueue.push(uiInfo)
  295. return
  296. }
  297. let uiInfoTmp = this.getUIInfo(uiID)
  298. if (uiInfoTmp) {
  299. // 重复打开了同一个界面
  300. this.hide(uiID)
  301. this.show(uiID, uiArgs)
  302. return
  303. }
  304. // 设置UI的zOrder
  305. uiInfo.zOrder = this.UIStack.length
  306. this.UIStack.push(uiInfo)
  307. // 先屏蔽点击
  308. uiInfo.preventNode = this.preventTouch(10000)
  309. this.isOpening = true
  310. // 预加载资源,并在资源加载完成后自动打开界面
  311. this.getOrCreateUI(
  312. uiID,
  313. progressCallback,
  314. (baseUI: BaseUI, prefab: cc.Prefab): void => {
  315. if (uiInfo.preventNode) {
  316. uiInfo.preventNode.destroy()
  317. uiInfo.preventNode = null
  318. }
  319. // 如果界面已经被关闭或创建失败
  320. if (uiInfo.isClose || null == baseUI) {
  321. cc.log(`getOrCreateUI ${uiID} faile!
  322. close state : ${uiInfo.isClose} , baseUI : ${baseUI}`)
  323. this.isOpening = false
  324. return
  325. }
  326. if (prefab) uiInfo.prefab = prefab
  327. this.onUIOpen(uiID, baseUI, uiInfo, uiArgs)
  328. this.isOpening = false
  329. this.autoExecNextUI()
  330. },
  331. uiArgs,
  332. )
  333. }
  334. /** 显示一组UI */
  335. showUIs(UIs: GroupUIInfo[]) {
  336. if (!UIs) return
  337. UIs = UIs.filter(value => value)
  338. if (UIs.length <= 0) return
  339. this.groupUIs = UIs
  340. this.curGroupUI = this.groupUIs.splice(0, 1)[0]
  341. this.show(this.curGroupUI.uiID, this.curGroupUI.uiArgs)
  342. }
  343. /** 替换栈顶界面 */
  344. public replace(uiID: number, uiArgs: any = null) {
  345. this.hideBaseUI(this.UIStack[this.UIStack.length - 1].baseUI)
  346. this.show(uiID, uiArgs)
  347. }
  348. /**
  349. * 关闭当前界面
  350. * @param closeUI 要关闭的界面
  351. */
  352. public hideBaseUI(closeUI?: BaseUI) {
  353. let uiCount = this.UIStack.length
  354. if (uiCount < 1 || this.isClosing || this.isOpening) {
  355. if (closeUI) {
  356. // 插入待关闭队列
  357. let closeIndex = this.UICloseQueue.findIndex(v => v == closeUI.uiID)
  358. if (closeIndex >= 0) this.UICloseQueue.splice(closeIndex, 1)
  359. this.UICloseQueue.push(closeUI.uiID)
  360. }
  361. return
  362. }
  363. let uiInfo: UIInfo
  364. if (closeUI) {
  365. let isSplice = false
  366. for (let index = this.UIStack.length - 1; index >= 0; index--) {
  367. let ui = this.UIStack[index]
  368. if (ui.baseUI === closeUI) {
  369. uiInfo = ui
  370. isSplice = true
  371. this.UIStack.splice(index, 1)
  372. break
  373. }
  374. }
  375. if (isSplice) this.resetUIStackZIndex()
  376. // 找不到这个UI
  377. if (uiInfo === undefined) {
  378. let index = this.UIOpenQueue.findIndex(value => value.uiID == closeUI.uiID)
  379. if (index >= 0) {
  380. this.UIOpenQueue.splice(index, 1)
  381. }
  382. this.autoExecNextUI()
  383. return
  384. }
  385. } else {
  386. uiInfo = this.UIStack.pop()
  387. }
  388. // 关闭当前界面
  389. let uiID = uiInfo.uiID
  390. let baseUI = uiInfo.baseUI
  391. uiInfo.isClose = true
  392. // 回收遮罩层
  393. if (uiInfo.preventNode) {
  394. uiInfo.preventNode.destroy()
  395. uiInfo.preventNode = null
  396. }
  397. if (null == baseUI) {
  398. return
  399. }
  400. this.isClosing = true
  401. let preUIInfo = null
  402. for (let i = uiCount - 2; i >= 0; i--) {
  403. if (this.UIStack[i] && this.UIStack[i].baseUI.showType == baseUI.showType) {
  404. preUIInfo = this.UIStack[i]
  405. break
  406. }
  407. }
  408. let close = () => {
  409. this.isClosing = false
  410. // 显示之前的界面
  411. if (preUIInfo && preUIInfo.baseUI && preUIInfo.baseUI.node.activeInHierarchy) {
  412. // 回调onTop onTop只是当前层级生效
  413. preUIInfo.baseUI.onTop(uiID)
  414. }
  415. if (this.uiCloseDelegate) {
  416. this.uiCloseDelegate(uiID)
  417. }
  418. this.releaseUI(uiInfo)
  419. baseUI.onHide()
  420. this.autoExecNextUI()
  421. if (this.groupUIs.length > 0) {
  422. this.curGroupUI = this.groupUIs.splice(0, 1)[0]
  423. this.show(this.curGroupUI.uiID, this.curGroupUI.uiArgs)
  424. }
  425. }
  426. // 执行关闭动画
  427. this.autoExecAnimation(baseUI, 'uiClose', close)
  428. }
  429. /**
  430. * 关闭当前界面
  431. * @param uiID 要关闭的界面
  432. */
  433. public hide(uiID: UI) {
  434. let uiCount = this.UIStack.length
  435. if (uiCount < 1 || this.isClosing || this.isOpening) {
  436. if (uiID) {
  437. // 插入待关闭队列
  438. const openIndex = this.UIOpenQueue.findIndex(v => v.uiID == uiID)
  439. if (openIndex != -1) {
  440. this.UIOpenQueue.splice(openIndex, 1)
  441. } else {
  442. this.UICloseQueue.push(uiID)
  443. }
  444. }
  445. return
  446. }
  447. if (this.getUI(uiID)) this.hideBaseUI(this.getUI(uiID))
  448. }
  449. /** 关闭所有界面 */
  450. public closeAll(remainUIs: UI[] = []) {
  451. // 不播放动画,也不清理缓存
  452. let remainUIInfo = []
  453. for (const uiInfo of this.UIStack) {
  454. if (remainUIs.includes(uiInfo.uiID)) {
  455. remainUIInfo.push(uiInfo)
  456. continue
  457. }
  458. uiInfo.isClose = true
  459. if (uiInfo.preventNode) {
  460. uiInfo.preventNode.destroy()
  461. uiInfo.preventNode = null
  462. }
  463. if (uiInfo.baseUI) {
  464. //uiInfo.baseUI.decAllRef()
  465. this.releaseUI(uiInfo)
  466. uiInfo.baseUI.onHide()
  467. }
  468. }
  469. this.UIOpenQueue = []
  470. this.UICloseQueue = []
  471. this.UIStack = remainUIInfo
  472. this.resetUIStackZIndex()
  473. this.isOpening = false
  474. this.isClosing = false
  475. this.curGroupUI = null
  476. this.groupUIs = []
  477. }
  478. releaseUI(uiInfo: UIInfo) {
  479. let baseUI = uiInfo.baseUI
  480. baseUI.node.parent = null
  481. this.UICache[uiInfo.uiID] = uiInfo
  482. if (!baseUI.cache) {
  483. uiInfo.hideTime = Date.now()
  484. }
  485. }
  486. cleanUI() {
  487. let now = Date.now()
  488. for (let key in this.UICache) {
  489. let uiInfo = this.UICache[key]
  490. if (uiInfo) {
  491. let node = uiInfo.baseUI.node
  492. let hideTime = uiInfo.hideTime
  493. let prefab = uiInfo.prefab
  494. if (!node.parent && hideTime > 0 && now - hideTime > 15000 && !this.isClosing && !this.isOpening) {
  495. console.log('UIMgr releaseUI:', node.name)
  496. uiInfo.baseUI.decAllRef()
  497. node.destroy()
  498. cc.assetManager.releaseAsset(prefab)
  499. delete this.UICache[key]
  500. }
  501. }
  502. }
  503. }
  504. resetUIStackZIndex() {
  505. for (let i = 0; i < this.UIStack.length; i++) {
  506. let uiInfo = this.UIStack[i]
  507. uiInfo.zOrder = i
  508. uiInfo.baseUI.node.zIndex = i + uiInfo.baseUI.showType
  509. }
  510. }
  511. /******************** UI的便捷接口 *******************/
  512. //可能存在当时UI正在加载node,没有baseUI,顶层不是那么精确
  513. public isTopUI(uiID, isCurLayer: boolean = true): boolean {
  514. let curUIs = this.UIStack
  515. let uiInfo = curUIs.find(v => v.uiID == uiID)
  516. if (!uiInfo) return false
  517. if (uiInfo && isCurLayer) {
  518. curUIs = this.UIStack.filter(v => v.baseUI && uiInfo.baseUI && v.baseUI.showType == uiInfo.baseUI.showType)
  519. }
  520. return curUIs[curUIs.length - 1]?.uiID == uiID
  521. }
  522. public getUI(uiID: number): BaseUI {
  523. for (let index = 0; index < this.UIStack.length; index++) {
  524. const element = this.UIStack[index]
  525. if (uiID == element.uiID) {
  526. return element.baseUI
  527. }
  528. }
  529. return null
  530. }
  531. //返回uiID,可能存在当时UI正在加载node,没有baseUI
  532. public getTopUI(): number {
  533. if (this.UIStack.length > 0) {
  534. return this.UIStack[this.UIStack.length - 1].uiID
  535. }
  536. return null
  537. }
  538. public getUIInfo(uiID: number): UIInfo {
  539. for (let index = 0; index < this.UIStack.length; index++) {
  540. let element = this.UIStack[index]
  541. if (uiID == element.uiID) {
  542. return element
  543. }
  544. }
  545. return null
  546. }
  547. public getGroupUIInfo(uiID: number, args?: any): GroupUIInfo {
  548. return {uiID, uiArgs: args}
  549. }
  550. public getRewardGroupUIInfo(rewardNty: IRewardNty): GroupUIInfo | null {
  551. let hasReward = false
  552. for (let key in rewardNty) {
  553. hasReward ||= Array.isArray(rewardNty[key]) && rewardNty[key].length > 0
  554. }
  555. if (hasReward) {
  556. let rewardArgs: IReward = {idNumArr: Mgr.goods.getGoodsListByRewardNty(rewardNty)}
  557. return {uiID: UI.RewardUI, uiArgs: rewardArgs}
  558. } else {
  559. return null
  560. }
  561. }
  562. public callOnShow(uiID: UI, args: any) {
  563. let uiInfo = this.getUIInfo(uiID)
  564. if (uiInfo && uiInfo.baseUI) {
  565. uiInfo.baseUI.onShow(args, null)
  566. }
  567. }
  568. /******************** 通用UI的操作接口 *******************/
  569. public tip(text: string = '') {
  570. if (text == '') {
  571. return
  572. }
  573. this.hide(UI.TipUI)
  574. this.show(UI.TipUI, text)
  575. }
  576. /**
  577. * 显示网络请求等待响应屏蔽触摸的界面
  578. * @param str 显示的文本
  579. * @param isDelay 是否延时显示可见的屏蔽层,延时中为透明的屏蔽层
  580. * @param cutNet 超时后是否重启游戏
  581. */
  582. public showLoading(str: string = '', isDelay: boolean = true, cutNet: boolean = false, timeOut: boolean = false) {
  583. this.show(UI.LoadingUI, {str, isDelay, cutNet, timeOut})
  584. }
  585. /**
  586. * 隐藏网络请求等待响应屏蔽触摸的界面
  587. */
  588. public hideLoading() {
  589. this.hide(UI.LoadingUI)
  590. }
  591. public message(text: string = '', sureFunc: Function, isHideCancel: boolean = false, sureLb: string = '') {
  592. if (text == '') {
  593. return
  594. }
  595. let args: IMessage = {
  596. sureFuc: sureFunc,
  597. tip: text,
  598. isHideCancel,
  599. sureLb,
  600. }
  601. this.show(UI.MessageUI, args)
  602. }
  603. public fadeShow(uiID: number, uiArgs: any = null, inOrOut = true) {
  604. this.show(UI.FadeInOutUI, {
  605. inOrOut,
  606. aniFinishFuc: () => {},
  607. })
  608. if (uiID > 0) this.show(uiID, uiArgs)
  609. }
  610. public showReward(rewardNty: IRewardNty, itemUICfgArr?: ItemUICfg[]) {
  611. if (!rewardNty) return
  612. let hasReward = false
  613. for (let key in rewardNty) {
  614. hasReward ||= Array.isArray(rewardNty[key]) && rewardNty[key].length > 0
  615. }
  616. if (hasReward) {
  617. let rewardArgs: IReward = {idNumArr: Mgr.goods.getGoodsListByRewardNty(rewardNty), itemUICfgArr}
  618. this.show(UI.RewardUI, rewardArgs)
  619. }
  620. }
  621. //传入needNum表示根据needGoods道具ID判断是否显示UI。不传值默认显示UI
  622. public showObtain(needGoods: number | IOperateNeed, needNum: number = 0): boolean {
  623. let operateNeed: IOperateNeed
  624. let showObtain = true
  625. if (typeof needGoods == 'number') {
  626. operateNeed = {
  627. canUp: false,
  628. isMax: false,
  629. need: [
  630. {
  631. id: needGoods,
  632. num: Data.user.goods.get(needGoods) ? Data.user.goods.get(needGoods) : 0,
  633. need: needNum,
  634. },
  635. ],
  636. }
  637. } else {
  638. operateNeed = needGoods
  639. }
  640. if (needNum > 0) {
  641. let hasNum = Data.user.goods.get(operateNeed.need[0]?.id)
  642. showObtain = hasNum < needNum
  643. }
  644. if (showObtain) this.show(UI.ObtainChannelUI, operateNeed)
  645. return showObtain
  646. }
  647. //显示顶部UI
  648. public showTop(baseUI: BaseUI, goodsIDs: GOODS[]) {
  649. this.topNode.parent = baseUI.node
  650. this.topNode.zIndex = 2000
  651. this.topNode.getComponent(TopCell).init(goodsIDs, baseUI)
  652. }
  653. //显示底部UI
  654. public showFoot(baseUI: BaseUI) {
  655. this.footNode.parent = baseUI.node
  656. this.footNode.zIndex = 2000
  657. this.topNode.getComponent(FootCell).init(baseUI)
  658. }
  659. }