diff --git a/assets/core/gui/Gui.ts b/assets/core/gui/Gui.ts index 96f64cc..7afdd97 100644 --- a/assets/core/gui/Gui.ts +++ b/assets/core/gui/Gui.ts @@ -17,6 +17,12 @@ export namespace gui { export namespace internal { /** 界面唯一标记变量名 */ export const GUI_KEY = "OOPS_GUI_KEY"; + + /** 获取界面唯一关键字 */ + export function getKey(ctor: any) { + return ctor[GUI_KEY]; + } + /** 获取界面组件配置 */ export function getConfig(key: string) { return configs[key]; diff --git a/assets/core/gui/layer/LayerManager.ts b/assets/core/gui/layer/LayerManager.ts index 3286ac8..6720350 100644 --- a/assets/core/gui/layer/LayerManager.ts +++ b/assets/core/gui/layer/LayerManager.ts @@ -1,5 +1,4 @@ import { Camera, Node, ResolutionPolicy, SafeArea, screen, view, warn } from "cc"; -import { resLoader } from "../../common/loader/ResLoader"; import { oops } from "../../Oops"; import { gui } from "../Gui"; import { LayerDialog } from "./LayerDialog"; @@ -260,6 +259,23 @@ export class LayerManager { } } + /** + * 清理指定界面的缓存 + * @param uiid 窗口唯一标识 + * @example + * oops.gui.removeCache(UIID.Loading); + */ + removeCache(uiid: Uiid) { + let info = this.getInfo(uiid); + let layer = this.uiLayers.get(info.config.layer); + if (layer) { + layer.removeCache(info.config.prefab); + } + else { + console.error(`移除编号为【${uiid}】的界面失败,界面层不存在`); + } + } + /** * 通过界面节点移除 * @param node 窗口节点 diff --git a/assets/core/gui/layer/LayerPopup.ts b/assets/core/gui/layer/LayerPopup.ts index defd693..1b5e12c 100644 --- a/assets/core/gui/layer/LayerPopup.ts +++ b/assets/core/gui/layer/LayerPopup.ts @@ -17,19 +17,13 @@ export class LayerPopUp extends LayerUI { /** 半透明遮罩资源 */ protected mask!: Node; - constructor(name: string) { - super(name); - - this.on(Node.EventType.CHILD_ADDED, this.onChildAdded, this); - this.on(Node.EventType.CHILD_REMOVED, this.onChildRemoved, this); - } - - private onChildAdded(child: Node) { + protected onChildAdded(child: Node) { this.mask.setSiblingIndex(this.children.length - 2); } - private onChildRemoved(child: Node) { + protected onChildRemoved(child: Node) { this.mask.setSiblingIndex(this.children.length - 2); + super.onChildRemoved(child); } protected uiInit(state: UIState): Promise { diff --git a/assets/core/gui/layer/LayerUI.ts b/assets/core/gui/layer/LayerUI.ts index 2c24665..14ee9fe 100644 --- a/assets/core/gui/layer/LayerUI.ts +++ b/assets/core/gui/layer/LayerUI.ts @@ -23,6 +23,20 @@ export class LayerUI extends Node { constructor(name: string) { super(name); LayerHelper.setFullScreen(this); + + this.on(Node.EventType.CHILD_ADDED, this.onChildAdded, this); + this.on(Node.EventType.CHILD_REMOVED, this.onChildRemoved, this); + } + + protected onChildAdded(child: Node) { + + } + + protected onChildRemoved(child: Node) { + const comp = child.getComponent(LayerUIElement); + if (comp) { + this.closeUi(comp.state); + } } /** @@ -74,8 +88,9 @@ export class LayerUI extends Node { protected async load(state: UIState): Promise { return new Promise(async (resolve, reject) => { // 加载界面资源超时提示 - let timerId = setTimeout(this.onLoadingTimeoutGui, oops.config.game.loadingTimeoutGui); if (state.node == null) { + let timerId = setTimeout(this.onLoadingTimeoutGui, oops.config.game.loadingTimeoutGui); + // 优先加载配置的指定资源包中资源,如果没配置则加载默认资源包资源 const res = await resLoader.loadAsync(state.config.bundle!, state.config.prefab, Prefab); if (res) { @@ -92,14 +107,13 @@ export class LayerUI extends Node { console.warn(`路径为【${state.config.prefab}】的预制加载失败`); this.failure(state); } + + // 关闭界面资源超时提示 + oops.gui.waitClose(); + clearTimeout(timerId); } - // 关闭界面资源超时提示 - oops.gui.waitClose(); - clearTimeout(timerId); - await this.uiInit(state); - resolve(state.node); }); } @@ -157,40 +171,30 @@ export class LayerUI extends Node { release = state.config.destroy !== undefined ? state.config.destroy : true; // 不释放界面,缓存起来待下次使用 - if (release === false) { - this.ui_cache.set(state.config.prefab, state); - } + if (release === false) this.ui_cache.set(state.config.prefab, state); - const node = state.node; - const comp = node.getComponent(LayerUIElement)!; + const comp = state.node.getComponent(LayerUIElement)!; comp.remove(release); } - // 清理界面缓存 - const cache = this.ui_cache.get(prefabPath); - if (cache) { - // 验证是否删除后台缓存界面 - if (release) this.removeCache(prefabPath); - } + // 验证是否删除后台缓存界面 + if (release) this.removeCache(prefabPath); } /** 删除缓存的界面,当缓存界面被移除舞台时,可通过此方法删除缓存界面 */ - private removeCache(prefabPath: string) { - let vp = this.ui_cache.get(prefabPath); - if (vp) { - this.closeUi(vp); + removeCache(prefabPath: string) { + const state = this.ui_cache.get(prefabPath); + if (state) { this.ui_cache.delete(prefabPath); - const node = vp.node; - const comp = node.getComponent(LayerUIElement)!; + const comp = state.node.getComponent(LayerUIElement)!; comp.remove(true); - node.destroy(); } } /** 显示界面 */ show(prefabPath: string) { - const vp = this.ui_nodes.get(prefabPath); - if (vp) vp.node.parent = this; + const state = this.ui_nodes.get(prefabPath); + if (state) state.node.parent = this; } /** @@ -198,8 +202,8 @@ export class LayerUI extends Node { * @param prefabPath 预制路径 */ get(prefabPath: string): Node { - const vp = this.ui_nodes.get(prefabPath); - if (vp) return vp.node; + const state = this.ui_nodes.get(prefabPath); + if (state) return state.node; return null!; } diff --git a/assets/core/gui/layer/LayerUIElement.ts b/assets/core/gui/layer/LayerUIElement.ts index 7ad9a76..4fede83 100644 --- a/assets/core/gui/layer/LayerUIElement.ts +++ b/assets/core/gui/layer/LayerUIElement.ts @@ -73,10 +73,6 @@ export class LayerUIElement extends Component { this.state.params.onRemoved(this.node, this.state.params.data); } - // 关闭动画播放完后,界面移除舞台 - //@ts-ignore - this.node.parent.closeUi(this.state); - // 关闭动画播放完后,界面移除舞台事件 this.onClose && this.onClose(); diff --git a/assets/libs/gui/label/LabelTime.ts b/assets/libs/gui/label/LabelTime.ts index 7831f6d..e370c7f 100644 --- a/assets/libs/gui/label/LabelTime.ts +++ b/assets/libs/gui/label/LabelTime.ts @@ -1,32 +1,32 @@ import { Label, _decorator } from "cc"; +import { EDITOR } from "cc/env"; import { oops } from "../../../core/Oops"; import { EventMessage } from "../../../core/common/event/EventMessage"; import { TimeUtil } from "../../../core/utils/TimeUtils"; -import { EDITOR } from "cc/env"; const { ccclass, property, menu } = _decorator; /** 倒计时标签 */ @ccclass("LabelTime") -@menu('OopsFramework/Label/LabelTime (倒计时标签)') +@menu("OopsFramework/Label/LabelTime (倒计时标签)") export default class LabelTime extends Label { @property({ - tooltip: "到计时间总时间(单位秒)" + tooltip: "到计时间总时间(单位秒)", }) countDown: number = 1000; @property({ - tooltip: "天数数据格式化" + tooltip: "天数数据格式化", }) dayFormat: string = "{0} day"; @property({ - tooltip: "时间格式化" + tooltip: "时间格式化", }) timeFormat: string = "{0}:{1}:{2}"; @property({ - tooltip: "是否有00" + tooltip: "是否有00", }) zeroize: boolean = true; @@ -35,9 +35,9 @@ export default class LabelTime extends Label { }) paused: boolean = false; - private backStartTime: number = 0; // 进入后台开始时间 - private dateDisable!: boolean; // 时间能否由天数显示 - private result!: string; // 时间结果字符串 + private backStartTime: number = 0; // 进入后台开始时间 + private dateDisable!: boolean; // 时间能否由天数显示 + private result!: string; // 时间结果字符串 /** 每秒触发事件 */ onSecond: Function = null!; @@ -45,10 +45,9 @@ export default class LabelTime extends Label { onComplete: Function = null!; private replace(value: string, ...args: any): string { - return value.replace(/\{(\d+)\}/g, - function (m, i) { - return args[i]; - }); + return value.replace(/\{(\d+)\}/g, function (m, i) { + return args[i]; + }); } /** 格式化字符串 */ @@ -84,23 +83,15 @@ export default class LabelTime extends Label { if (date < 2) { df = df.replace("days", "day"); } - this.result = this.replace(df, date, hours); // 如果天大于1,则显示 "1 Day..." + this.result = this.replace(df, date, hours); // 如果天大于1,则显示 "1 Day..." } else { hours += date * 24; if (this.zeroize) { - this.result = this.replace( - this.timeFormat, - this.coverString(hours), - this.coverString(minutes), - this.coverString(seconds)); // 否则显示 "01:12:24" + this.result = this.replace(this.timeFormat, this.coverString(hours), this.coverString(minutes), this.coverString(seconds)); // 否则显示 "01:12:24" } else { - this.result = this.replace( - this.timeFormat, - hours, - minutes, - seconds); + this.result = this.replace(this.timeFormat, hours, minutes, seconds); } } this.string = this.result; @@ -108,8 +99,7 @@ export default class LabelTime extends Label { /** 个位数的时间数据将字符串补位 */ private coverString(value: number) { - if (value < 10) - return "0" + value; + if (value < 10) return "0" + value; return value.toString(); } @@ -123,7 +113,7 @@ export default class LabelTime extends Label { * @param second 倒计时时间(单位秒) */ setTime(second: number) { - this.countDown = second; // 倒计时,初始化显示字符串 + this.countDown = second; // 倒计时,初始化显示字符串 this.timing_end(); this.timing_start(); this.format(); @@ -140,15 +130,33 @@ export default class LabelTime extends Label { this.format(); } - start() { + onLoad() { if (!EDITOR) { oops.message.on(EventMessage.GAME_SHOW, this.onGameShow, this); oops.message.on(EventMessage.GAME_HIDE, this.onGameHide, this); } + } + + start() { + if (this.countDown <= 0) return; this.timing_start(); this.format(); } + onEnable() { + super.onEnable(); + if (!EDITOR) { + this.onGameShow(); + } + } + + onDisable() { + super.onDisable(); + if (!EDITOR) { + this.onGameHide(); + } + } + onDestroy() { if (!EDITOR) { oops.message.off(EventMessage.GAME_SHOW, this.onGameShow, this); @@ -175,6 +183,12 @@ export default class LabelTime extends Label { } private onScheduleSecond() { + if (this.countDown == 0) { + this.format(); + this.onScheduleComplete(); + return; + } + this.countDown--; this.format(); if (this.onSecond) this.onSecond(this.node); @@ -187,15 +201,17 @@ export default class LabelTime extends Label { private onScheduleComplete() { this.timing_end(); this.format(); + this.unschedule(this.onScheduleSecond); if (this.onComplete) this.onComplete(this.node); } /** 开始计时 */ - private timing_start() { + timing_start() { this.schedule(this.onScheduleSecond, 1); } - private timing_end() { + /** 关闭计时 */ + timing_end() { this.unscheduleAllCallbacks(); } } diff --git a/assets/module/common/CCEntity.ts b/assets/module/common/CCEntity.ts index d7f3b05..2e9b50c 100644 --- a/assets/module/common/CCEntity.ts +++ b/assets/module/common/CCEntity.ts @@ -35,8 +35,7 @@ export class CCEntity extends ecs.Entity { */ addUi(ctor: ECSCtor, params?: UIParam): Promise { return new Promise(async (resolve, reject) => { - //@ts-ignore - const key = ctor[gui.internal.GUI_KEY]; + const key = gui.internal.getKey(ctor); if (key) { if (params == null) { params = { preload: true }; @@ -62,8 +61,7 @@ export class CCEntity extends ecs.Entity { * @param ctor 界面逻辑组件 */ removeUi(ctor: CompType) { - //@ts-ignore - const key = ctor[gui.internal.GUI_KEY]; + const key = gui.internal.getKey(ctor); if (key) { const node = oops.gui.get(key); if (node == null) { @@ -73,9 +71,8 @@ export class CCEntity extends ecs.Entity { const comp = node.getComponent(LayerUIElement); if (comp) { - comp.onClose = () => { - if (comp.state.config.destroy) this.remove(ctor); - }; + // 处理界面关闭动画播放完成后,移除ECS组件,避免使用到组件实体数据还在动画播放时在使用导致的空对象问题 + comp.onClose = this.remove.bind(this, ctor); oops.gui.remove(key); } }