diff --git a/assets/core/Root.ts b/assets/core/Root.ts index a0ae880de14fb06da44c2bf19c02fcc34ecafe9f..98e18cb299f8b7fa8a116a1bc2729f51457257e1 100644 --- a/assets/core/Root.ts +++ b/assets/core/Root.ts @@ -4,7 +4,7 @@ * @LastEditors: dgflash * @LastEditTime: 2023-08-28 10:02:57 */ -import { _decorator, Component, director, Game, game, JsonAsset, Node, resources, screen, sys } from "cc"; +import { _decorator, Component, director, Game, game, JsonAsset, Node, profiler, resources, screen, sys } from "cc"; import { GameConfig } from "../module/config/GameConfig"; import { GameQueryConfig } from "../module/config/GameQueryConfig"; import { oops, version } from "./Oops"; @@ -94,6 +94,8 @@ export class Root extends Component { //@ts-ignore oops.gui.initLayer(this.gui, config.json.gui); + // 初始化统计信息 + oops.config.game.stats ? profiler.showStats() : profiler.hideStats(); // 初始化每秒传输帧数 game.frameRate = oops.config.game.frameRate; diff --git a/assets/core/common/event/EventMessage.ts b/assets/core/common/event/EventMessage.ts index 23f53803243e5ce05241108d95f3648af269df89..0a77c9179f40c7f5659a41e68e69e97505f564b1 100644 --- a/assets/core/common/event/EventMessage.ts +++ b/assets/core/common/event/EventMessage.ts @@ -15,13 +15,13 @@ export type ListenerFunc = (event: string, ...args: any) => void /** 框架内部全局事件 */ export enum EventMessage { /** 游戏从后台进入事件 */ - GAME_SHOW = "GAME_ENTER", + GAME_SHOW = "onGameShow", /** 游戏切到后台事件 */ - GAME_HIDE = "GAME_EXIT", + GAME_HIDE = "onGameHide", /** 游戏画笔尺寸变化事件 */ - GAME_RESIZE = "GAME_RESIZE", + GAME_RESIZE = "onGameResize", /** 游戏全屏事件 */ - GAME_FULL_SCREEN = "GAME_FULL_SCREEN", + GAME_FULL_SCREEN = "onGameFullScreen", /** 游戏旋转屏幕事件 */ - GAME_ORIENTATION = "GAME_ORIENTATION" + GAME_ORIENTATION = "onGameOrientation" } diff --git a/assets/core/common/loader/ZipLoader.ts b/assets/core/common/loader/ZipLoader.ts new file mode 100644 index 0000000000000000000000000000000000000000..b13f3a681ea883660b17d825ff96a1aca1ebfb46 --- /dev/null +++ b/assets/core/common/loader/ZipLoader.ts @@ -0,0 +1,87 @@ +import { BufferAsset, SpriteFrame, Texture2D } from "cc"; +import { resLoader } from "./ResLoader"; + +/** + * 加载Zip资源 + * 注: + * 1. 使用此功能需要教程项目中项目资源目录libs/jszip目录拷贝到自己的项目中 + * 2. 选中libs/jszip/jszip文件,属性检查器中勾选导入为插件、允许指点平台加载此库 + * 3. 压缩软件打包的 game.zip 修改为 game.bin 则可在游戏中加载 + */ +export class ZipLoader { + private static zips: Map = new Map(); + + /** + * 加载ZIP资源包 + * @param url + * @returns + */ + static load(url: string): Promise { + return new Promise((resolve, reject) => { + resLoader.load(url, BufferAsset, async (error: Error | null, asset: BufferAsset) => { + if (error) return reject(error); + + var zip = await JSZip.loadAsync(asset.buffer()); + this.zips.set(url, zip); + resolve(zip); + }) + }); + } + + static getJson(zipName: string, path: string): Promise { + return new Promise(async (resolve, reject) => { + var zip = this.zips.get(zipName); + if (zip == null) { + console.error(`名为【${zipName}】的资源包不存在`); + resolve(null); + return; + } + + var file = zip.file(path); + var json = JSON.parse(await file.async("text")); + resolve(json); + }); + } + + static getSpriteFrame(zipName: string, path: string): Promise { + return new Promise(async (resolve, reject) => { + var zip = this.zips.get(zipName); + if (zip == null) { + console.error(`名为【${zipName}】的资源包不存在`); + resolve(null!); + return; + } + + var file = zip.file(path); + var buf = await file.async("base64"); + var img = new Image(); + img.src = 'data:image/png;base64,' + buf; + img.onload = () => { + var texture = new Texture2D(); + texture.reset({ + width: img.width, + height: img.height + }); + texture.uploadData(img, 0, 0); + texture.loaded = true; + + var sf = new SpriteFrame(); + sf.texture = texture; + + resolve(sf); + } + }); + } + + /** 释放Zip资源 */ + static release(url?: string) { + if (url) { + resLoader.release(url); + } + else { + this.zips.forEach((value: JSZip, key: string) => { + resLoader.release(key); + }); + } + } +} \ No newline at end of file diff --git a/assets/module/common/ModuleUtil.ts.meta b/assets/core/common/loader/ZipLoader.ts.meta similarity index 70% rename from assets/module/common/ModuleUtil.ts.meta rename to assets/core/common/loader/ZipLoader.ts.meta index 1a0612544714e02a5fdec06db2594783b20d7375..0b36f42ed73c93f8a8d1a3ae726de14a1590c541 100644 --- a/assets/module/common/ModuleUtil.ts.meta +++ b/assets/core/common/loader/ZipLoader.ts.meta @@ -2,7 +2,7 @@ "ver": "4.0.24", "importer": "typescript", "imported": true, - "uuid": "52a6c740-3b9b-46c5-a784-d53a6b67954a", + "uuid": "2c6ce5e4-08fd-4ff1-b0b6-8c07d7192263", "files": [], "subMetas": {}, "userData": {} diff --git a/assets/core/common/log/Logger.ts b/assets/core/common/log/Logger.ts index 327e2912e6f68848f8b441bd6b6a4a580c9525d7..a12dd341a3a5ed599e5bcbe338e59d1b568036d7 100644 --- a/assets/core/common/log/Logger.ts +++ b/assets/core/common/log/Logger.ts @@ -126,7 +126,7 @@ oops.log.table(object); * 打印标准日志 * @param msg 日志消息 */ - trace(msg: any, color: string = "#000000") { + trace(msg: any, color: string = "#000000ff") { this.print(LogType.Trace, msg, color); } diff --git a/assets/core/common/timer/TimerManager.ts b/assets/core/common/timer/TimerManager.ts index 8db577137ef233aefe23e7c82e061b527144928c..dc89178c80cb9621ef9c9c46e040c7460c8bd5de 100644 --- a/assets/core/common/timer/TimerManager.ts +++ b/assets/core/common/timer/TimerManager.ts @@ -8,10 +8,29 @@ import { Component, game } from "cc"; import { StringUtil } from "../../utils/StringUtil"; import { Timer } from "./Timer"; +interface ITimer { + /** 倒计时编号 */ + id: string; + /** 定时器 */ + timer: Timer; + /** 数据对象 */ + object: any; + /** 修改数据对象的字段 */ + field: string; + /** 事件侦听器的目标和被叫方 */ + target: any; + /** 开始时间 */ + startTime: number; + /** 每秒触发事件 */ + onSeconds: Function[]; + /** 时间完成事件 */ + onCompletes: Function[]; +} + /** 时间管理 */ export class TimerManager extends Component { /** 倒计时数据 */ - private times: any = {}; + private times: { [key: string]: ITimer } = {}; /** 服务器时间 */ private date_s: Date = new Date(); /** 服务器初始时间 */ @@ -25,7 +44,7 @@ export class TimerManager extends Component { protected update(dt: number) { for (let key in this.times) { let data = this.times[key]; - let timer = data.timer as Timer; + let timer = data.timer; if (timer.update(dt)) { if (data.object[data.field] > 0) { data.object[data.field]--; @@ -35,8 +54,8 @@ export class TimerManager extends Component { this.onTimerComplete(data); } // 触发每秒回调事件 - else if (data.onSecond) { - data.onSecond.call(data.object); + else if (data.onSeconds) { + data.onSeconds.forEach(fn => fn.call(data.object)); } } } @@ -44,10 +63,8 @@ export class TimerManager extends Component { } /** 触发倒计时完成事件 */ - private onTimerComplete(data: any) { - if (data.onComplete) data.onComplete.call(data.target, data.object); - if (data.event) this.node.dispatchEvent(data.event); - + private onTimerComplete(data: ITimer) { + if (data.onCompletes) data.onCompletes.forEach(fn => fn.call(data.target, data.object)); delete this.times[data.id]; } @@ -58,14 +75,14 @@ export class TimerManager extends Component { * @param target 触发事件的对象 * @param onSecond 每秒事件 * @param onComplete 倒计时完成事件 - * @returns + * @returns 倒计时编号 * @example export class Test extends Component { private timeId!: string; start() { // 在指定对象上注册一个倒计时的回调管理器 - this.timeId = oops.timer.register(this, "countDown", this.onSecond, this.onComplete); + this.timeId = oops.timer.register(this, "countDown", this, this.onSecond, this.onComplete); } private onSecond() { @@ -77,22 +94,41 @@ export class TimerManager extends Component { } } */ - register(object: any, field: string, target: object, onSecond: Function, onComplete: Function): string { + register(object: any, field: string, target: object, onSecond?: Function, onComplete?: Function): string { const timer = new Timer(); timer.step = 1; - let data: any = {}; - data.id = StringUtil.guid(); - data.timer = timer; - data.object = object; // 管理对象 - data.field = field; // 时间字段 - data.onSecond = onSecond; // 每秒事件 - data.onComplete = onComplete; // 倒计时完成事件 - data.target = target + let data: ITimer = { + id: StringUtil.guid(), + timer: timer, + object: object, + field: field, + onSeconds: [], + onCompletes: [], + target: target, + startTime: this.getTime() + }; + if (onSecond) data.onSeconds.push(onSecond); + if (onComplete) data.onCompletes.push(onComplete); + this.times[data.id] = data; return data.id; } + /** + * 为指定倒计时添加回调事件 + * @param id 倒计时编号 + * @param onSecond 每秒事件 + * @param onComplete 倒计时完成事件 + */ + addCallback(id: string, onSecond?: Function, onComplete?: Function) { + let data = this.times[id]; + if (data) { + if (onSecond) data.onSeconds.push(onSecond); + if (onComplete) data.onCompletes.push(onComplete); + } + } + /** * 在指定对象上注销一个倒计时的回调管理器 * @param id 时间对象唯一表示 @@ -153,23 +189,24 @@ export class TimerManager extends Component { /** 游戏最小化时记录时间数据 */ save(): void { for (let key in this.times) { - this.times[key].startTime = this.getTime(); + let data: ITimer = this.times[key]; + data.startTime = this.getTime(); } } /** 游戏最大化时回复时间数据 */ load(): void { for (let key in this.times) { - let interval = Math.floor((this.getTime() - (this.times[key].startTime || this.getTime())) / 1000); let data = this.times[key]; + let interval = Math.floor((this.getTime() - (data.startTime || this.getTime())) / 1000); data.object[data.field] = data.object[data.field] - interval; if (data.object[data.field] <= 0) { data.object[data.field] = 0; this.onTimerComplete(data); } else { - this.times[key].startTime = null; + data.object[data.field] = 0; } } } -} \ No newline at end of file +} \ No newline at end of file diff --git a/assets/core/game/GameManager.ts b/assets/core/game/GameManager.ts index 2f5d64ce3c25b65a562bac7f83264ccb317de672..678ad230b96c0f7f3f5ef6dadac700a393a45ca1 100644 --- a/assets/core/game/GameManager.ts +++ b/assets/core/game/GameManager.ts @@ -54,7 +54,9 @@ export class GameManager { } // 自定义节点排序索引 - if (params && params.siblingIndex) node.setSiblingIndex(params.siblingIndex); + if (params) { + if (params.siblingIndex) node.setSiblingIndex(params.siblingIndex); + } resolve(node); }); @@ -70,4 +72,4 @@ export class GameManager { //@ts-ignore return director.globalGameTimeScale; } -} +} \ No newline at end of file diff --git a/assets/core/gui/Gui.ts b/assets/core/gui/Gui.ts new file mode 100644 index 0000000000000000000000000000000000000000..7afdd97e18fc323e8c3c7678fa681189aa749007 --- /dev/null +++ b/assets/core/gui/Gui.ts @@ -0,0 +1,49 @@ +import { UIConfigMap } from "./layer/LayerEnum"; +import { UIConfig } from "./layer/UIConfig"; + +var configs: UIConfigMap = {}; + +export namespace gui { + /** 注册界面组件 */ + export function register(key: string, config: UIConfig) { + return function (ctor: any) { + //@ts-ignore + ctor[gui.internal.GUI_KEY] = key; + internal.setConfig(key, config); + }; + } + + /** 框架内部使用方法 */ + 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]; + } + + /** 获取界面组件配置 */ + export function setConfig(key: string, config: UIConfig) { + let c = getConfig(key); + if (c == null) { + configs[key] = config; + } + else { + console.error(`界面${key}重复注册`); + } + } + + /** 初始化界面组件配置 */ + export function initConfigs(uicm: UIConfigMap) { + for (const key in uicm) { + configs[key] = uicm[key]; + } + } + } +} \ No newline at end of file diff --git a/assets/module/common/CCComp.ts.meta b/assets/core/gui/Gui.ts.meta similarity index 70% rename from assets/module/common/CCComp.ts.meta rename to assets/core/gui/Gui.ts.meta index 19c6741c82168d478333951d29ed9d111d2514ab..0dd4546d348b7c71894c66f5e5315941258048aa 100644 --- a/assets/module/common/CCComp.ts.meta +++ b/assets/core/gui/Gui.ts.meta @@ -2,7 +2,7 @@ "ver": "4.0.24", "importer": "typescript", "imported": true, - "uuid": "dd2077e2-c862-4b7f-9afe-6e488c83076d", + "uuid": "51e09b11-d9ab-453b-b637-74d6aa615806", "files": [], "subMetas": {}, "userData": {} diff --git a/assets/core/gui/layer/LayerDialog.ts b/assets/core/gui/layer/LayerDialog.ts index 7f9a1503cd870949d94fe46856664e9d4da2a83e..f9b9ed09cb4d169f84942b564945676811b9f22e 100644 --- a/assets/core/gui/layer/LayerDialog.ts +++ b/assets/core/gui/layer/LayerDialog.ts @@ -4,9 +4,9 @@ * @LastEditors: dgflash * @LastEditTime: 2023-07-24 17:14:57 */ - +import { Node } from "cc"; import { LayerPopUp } from "./LayerPopup"; -import { UICallbacks, UIParams } from "./LayerUIElement"; +import { UIParam, UIState } from "./LayerUIElement"; import { UIConfig } from "./UIConfig"; /** 模式弹窗数据 */ @@ -16,9 +16,7 @@ type DialogParam = { /** 窗口配置 */ config: UIConfig; /** 窗口附加参数 */ - params?: any; - /** 窗口回调 */ - callbacks?: UICallbacks; + params?: UIParam; } /* @@ -27,56 +25,51 @@ type DialogParam = { export class LayerDialog extends LayerPopUp { /** 窗口调用参数队列 */ private params: Array = []; + /** 当前打开的界面 */ + private current: Node = null!; - add(uiid: string, config: UIConfig, params?: any, callbacks?: UICallbacks) { - // 控制同一时间只能显示一个模式窗口 - if (this.ui_nodes.size > 0) { - this.params.push({ - uiid: uiid, - config: config, - params: params, - callbacks: callbacks, - }); - return; - } - - this.show(uiid, config, params, callbacks); + /** + * 添加模式窗口 + * 1. 同时添加多个模式窗口时,第一个之后的窗口会先队列起来,在第一个关闭后在加载与显示第二个;同时方法返回节点保持只返回当前显示的界面节点 + */ + add(uiid: string, config: UIConfig, params?: UIParam): Promise { + return new Promise(async (resolve, reject) => { + // 控制同一时间只能显示一个模式窗口 + if (this.ui_nodes.size > 0) { + this.params.push({ uiid: uiid, config: config, params: params }); + resolve(this.current); + } + else { + this.current = await this.showDialog(uiid, config, params); + resolve(this.current); + } + }); } /** 显示模式弹窗 */ - private show(uiid: string, config: UIConfig, params?: any, callbacks?: UICallbacks) { - let uip = this.ui_cache.get(config.prefab); - if (uip == null) { - uip = new UIParams(); - uip.uiid = uiid; - uip.valid = true; - uip.config = config; - } - - uip.params = params || {}; - uip.callbacks = callbacks ?? {}; - this.ui_nodes.set(uip.config.prefab, uip); - - this.load(uip, config.bundle); + private showDialog(uiid: string, config: UIConfig, param?: UIParam): Promise { + return new Promise(async (resolve, reject) => { + let state = this.initUIConfig(uiid, config, param); + let node = await this.load(state); + resolve(node); + }); } - protected onCloseWindow(uip: UIParams) { - super.onCloseWindow(uip); + protected closeUi(state: UIState) { + super.closeUi(state); setTimeout(this.next.bind(this), 0); } - protected closeUI() { + protected closeBlack() { if (this.params.length == 0) { - this.black.enabled = false; - this.closeVacancyRemove(); - this.closeMask() + super.closeBlack(); } } private next() { if (this.params.length > 0) { let param = this.params.shift()!; - this.show(param.uiid, param.config, param.params, param.callbacks); + this.showDialog(param.uiid, param.config, param.params); } } } \ No newline at end of file diff --git a/assets/core/gui/layer/LayerEnum.ts b/assets/core/gui/layer/LayerEnum.ts index e656455d72b3d155db9389017a9f26d7b1c06bac..9879f98661cdbe7f0c62c6e649a839a72a3e57ac 100644 --- a/assets/core/gui/layer/LayerEnum.ts +++ b/assets/core/gui/layer/LayerEnum.ts @@ -1,7 +1,7 @@ import { UIConfig } from "./UIConfig"; /** 界面编号 */ -export type Uiid = number | string | UIConfig; +export type Uiid = number | string | UIConfig | Function; /** 界面配置集合 */ export type UIConfigMap = { [key: string]: UIConfig } @@ -51,4 +51,4 @@ export enum LayerTypeCls { Game = "Game", /** 自定义节点层 */ Node = "Node" -} +} \ No newline at end of file diff --git a/assets/core/gui/layer/LayerManager.ts b/assets/core/gui/layer/LayerManager.ts index 3d6e7a975f0171ef2873916954c1ac0ad0bd5f75..67203503760f98200d65b295aa99c72f77c69332 100644 --- a/assets/core/gui/layer/LayerManager.ts +++ b/assets/core/gui/layer/LayerManager.ts @@ -1,14 +1,15 @@ -import { Camera, Layers, Node, ResolutionPolicy, SafeArea, Widget, screen, view, warn } from "cc"; -import { resLoader } from "../../common/loader/ResLoader"; +import { Camera, Node, ResolutionPolicy, SafeArea, screen, view, warn } from "cc"; import { oops } from "../../Oops"; +import { gui } from "../Gui"; import { LayerDialog } from "./LayerDialog"; import { LayerCustomType, LayerTypeCls, UIConfigMap, Uiid } from "./LayerEnum"; +import { LayerGame } from "./LayerGame"; +import { LayerHelper } from "./LayerHelper"; import { LayerNotify } from "./LayerNotify"; import { LayerPopUp } from "./LayerPopup"; import { LayerUI } from "./LayerUI"; -import { LayerUIElement, UICallbacks } from "./LayerUIElement"; +import { LayerUIElement, UIParam } from "./LayerUIElement"; import { UIConfig } from "./UIConfig"; -import { LayerGame } from "./LayerGame"; /** 界面层级管理器 */ export class LayerManager { @@ -30,8 +31,6 @@ export class LayerManager { /** 消息提示控制器,请使用show方法来显示 */ private notify!: LayerNotify; - /** UI配置 */ - private configs: UIConfigMap = {}; /** 界面层集合 - 无自定义类型 */ private uiLayers: Map = new Map(); /** 界面层组件集合 */ @@ -137,7 +136,7 @@ export class LayerManager { * @param configs 配置对象 */ init(configs: UIConfigMap): void { - this.configs = configs; + gui.internal.initConfigs(configs); } /** @@ -171,23 +170,30 @@ export class LayerManager { this.notify.waitClose(); } + /** 获取界面信息 */ private getInfo(uiid: Uiid): { key: string; config: UIConfig } { let key = ""; let config: UIConfig = null!; - // 确定 key 和 config + // 界面配置 if (typeof uiid === 'object') { - if (uiid.bundle == null) uiid.bundle = resLoader.defaultBundleName; key = uiid.bundle + "_" + uiid.prefab; - config = this.configs[key]; + config = gui.internal.getConfig(key); if (config == null) { config = uiid; - this.configs[key] = uiid; + gui.internal.setConfig(key, uiid); } } + // 界面对象 - 配合gui.register使用 + else if (uiid instanceof Function) { + //@ts-ignore + key = uiid[gui.internal.GUI_KEY]; + config = gui.internal.getConfig(key); + } + // 界面唯一标记 else { key = uiid.toString(); - config = this.configs[uiid]; + config = gui.internal.getConfig(key); if (config == null) { console.error(`打开编号为【${uiid}】的界面失败,配置信息不存在`); } @@ -198,8 +204,7 @@ export class LayerManager { /** * 同步打开一个窗口 * @param uiid 窗口唯一编号 - * @param uiArgs 窗口参数 - * @param callbacks 回调对象 + * @param param 窗口参数 * @example var uic: UICallbacks = { onAdded: (node: Node, params: any) => { @@ -209,13 +214,28 @@ export class LayerManager { } }; - oops.gui.open(UIID.Loading, null, uic); + oops.gui.open(UIID.Loading); */ - open(uiid: Uiid, uiArgs: any = null, callbacks?: UICallbacks): void { + open(uiid: Uiid, param?: UIParam): Promise { + return new Promise(async (resolve, reject) => { + let info = this.getInfo(uiid); + let layer = this.uiLayers.get(info.config.layer); + if (layer) { + let node = await layer.add(info.key, info.config, param); + resolve(node); + } + else { + console.error(`打开编号为【${uiid}】的界面失败,界面层不存在`); + } + }); + } + + /** 显示指定界面 */ + show(uiid: Uiid) { let info = this.getInfo(uiid); let layer = this.uiLayers.get(info.config.layer); if (layer) { - layer.add(info.key, info.config, uiArgs, callbacks); + layer.show(info.config.prefab); } else { console.error(`打开编号为【${uiid}】的界面失败,界面层不存在`); @@ -223,38 +243,33 @@ export class LayerManager { } /** - * 异步打开一个窗口 - * @param uiid 窗口唯一编号 - * @param uiArgs 窗口参数 - * @example - * var node = await oops.gui.openAsync(UIID.Loading); + * 移除指定标识的窗口 + * @param uiid 窗口唯一标识 + * @example + * oops.gui.remove(UIID.Loading); */ - async openAsync(uiid: Uiid, uiArgs: any = null): Promise { - return new Promise((resolve, reject) => { - const callbacks: UICallbacks = { - onAdded: (node: Node, params: any) => { - resolve(node); - }, - onLoadFailure: () => { - resolve(null); - } - }; - this.open(uiid, uiArgs, callbacks); - }); + remove(uiid: Uiid) { + let info = this.getInfo(uiid); + let layer = this.uiLayers.get(info.config.layer); + if (layer) { + layer.remove(info.config.prefab); + } + else { + console.error(`移除编号为【${uiid}】的界面失败,界面层不存在`); + } } /** - * 移除指定标识的窗口 + * 清理指定界面的缓存 * @param uiid 窗口唯一标识 - * @param isDestroy 移除后是否释放(默认释放内存) * @example - * oops.gui.remove(UIID.Loading); + * oops.gui.removeCache(UIID.Loading); */ - remove(uiid: Uiid, isDestroy: boolean = true) { + removeCache(uiid: Uiid) { let info = this.getInfo(uiid); let layer = this.uiLayers.get(info.config.layer); if (layer) { - layer.remove(info.config.prefab, isDestroy); + layer.removeCache(info.config.prefab); } else { console.error(`移除编号为【${uiid}】的界面失败,界面层不存在`); @@ -264,25 +279,24 @@ export class LayerManager { /** * 通过界面节点移除 * @param node 窗口节点 - * @param isDestroy 移除后是否释放资源(默认释放内存) * @example * oops.gui.removeByNode(cc.Node); */ - removeByNode(node: Node, isDestroy: boolean = true) { + removeByNode(node: Node) { if (node instanceof Node) { let comp = node.getComponent(LayerUIElement); - if (comp && comp.params) { + if (comp && comp.state) { // 释放显示的界面 if (node.parent) { - let uiid = this.configs[comp.params.uiid]; - this.remove(uiid, isDestroy); + let uiid = gui.internal.getConfig(comp.state.uiid); + this.remove(uiid); } // 释放缓存中的界面 - else if (isDestroy) { - let layer = this.uiLayers.get(comp.params.config.layer); + else { + let layer = this.uiLayers.get(comp.state.config.layer); if (layer) { // @ts-ignore 注:不对外使用 - layer.removeCache(comp.params.config.prefab); + layer.removeCache(comp.state.config.prefab); } } } @@ -296,34 +310,14 @@ export class LayerManager { /** * 场景替换 * @param removeUiId 移除场景编号 - * @param openUiId 新打开场景编号 - * @param uiArgs 新打开场景参数 + * @param openUiid 新打开场景编号 + * @param param 新打开场景参数 */ - replace(removeUiId: Uiid, openUiId: Uiid, uiArgs: any = null) { - const callbacks: UICallbacks = { - onAdded: (node: Node, params: any) => { - this.remove(removeUiId); - } - }; - this.open(openUiId, uiArgs, callbacks); - } - - /** - * 异步场景替换 - * @param removeUiId 移除场景编号 - * @param openUiId 新打开场景编号 - * @param uiArgs 新打开场景参数 - */ - replaceAsync(removeUiId: Uiid, openUiId: Uiid, uiArgs: any = null): Promise { - return new Promise(async (resolve, reject) => { - const node = await this.openAsync(openUiId, uiArgs); - if (node) { - this.remove(removeUiId); - resolve(node); - } - else { - resolve(null); - } + replace(removeUiId: Uiid, openUiid: Uiid, param?: UIParam): Promise { + return new Promise(async (resolve, reject) => { + let node = await this.open(openUiid, param); + this.remove(removeUiId); + resolve(node); }); } @@ -380,12 +374,7 @@ export class LayerManager { private create_node(name: string) { const node = new Node(name); - node.layer = Layers.Enum.UI_2D; - const w: Widget = node.addComponent(Widget); - w.isAlignLeft = w.isAlignRight = w.isAlignTop = w.isAlignBottom = true; - w.left = w.right = w.top = w.bottom = 0; - w.alignMode = 2; - w.enabled = true; + LayerHelper.setFullScreen(node); return node; } } \ No newline at end of file diff --git a/assets/core/gui/layer/LayerPopup.ts b/assets/core/gui/layer/LayerPopup.ts index 95d4d639c66bb16c5ce2e5cf32f7b8976b0c90f8..6ac6d15812ec3f85e75f10037734464f63a249d4 100644 --- a/assets/core/gui/layer/LayerPopup.ts +++ b/assets/core/gui/layer/LayerPopup.ts @@ -7,7 +7,7 @@ import { BlockInputEvents, EventTouch, Node } from "cc"; import { ViewUtil } from "../../utils/ViewUtil"; import { PromptResType } from "../GuiEnum"; import { LayerUI } from "./LayerUI"; -import { UIParams } from "./LayerUIElement"; +import { UIState } from "./LayerUIElement"; import { UIConfig } from "./UIConfig"; /* 弹窗层,允许同时弹出多个窗口 */ @@ -17,27 +17,21 @@ 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) { - if (this.mask) this.mask.setSiblingIndex(this.children.length - 2); + protected onChildAdded(child: Node) { + this.mask && this.mask.setSiblingIndex(this.children.length - 2); } - private onChildRemoved(child: Node) { - if (this.mask) this.mask.setSiblingIndex(this.children.length - 2); + protected onChildRemoved(child: Node) { + this.mask && this.mask.setSiblingIndex(this.children.length - 2); + super.onChildRemoved(child); } - protected showUi(uip: UIParams): Promise { + protected uiInit(state: UIState): Promise { return new Promise(async (resolve) => { - const r = await super.showUi(uip); + const r = await super.uiInit(state); if (r) { // 界面加载完成显示时,启动触摸非窗口区域关闭 - this.openVacancyRemove(uip.config); + this.openVacancyRemove(state.config); // 界面加载完成显示时,层级事件阻挡 this.black.enabled = true; @@ -46,15 +40,15 @@ export class LayerPopUp extends LayerUI { }); } - protected onCloseWindow(uip: UIParams) { - super.onCloseWindow(uip); + protected closeUi(state: UIState) { + super.closeUi(state); // 界面关闭后,关闭触摸事件阻挡、关闭触摸非窗口区域关闭、关闭遮罩 - this.closeUI(); + this.closeBlack(); } /** 设置触摸事件阻挡 */ - protected closeUI() { + protected closeBlack() { // 所有弹窗关闭后,关闭事件阻挡功能 if (this.ui_nodes.size == 0) { if (this.black) this.black.enabled = false; @@ -120,13 +114,13 @@ export class LayerPopUp extends LayerUI { if (this.ui_nodes.size > 0) { let vp = this.ui_nodes.array[this.ui_nodes.size - 1]; if (vp.valid && vp.config.vacancy) { - this.remove(vp.config.prefab, vp.config.destroy); + this.remove(vp.config.prefab); } } } clear(isDestroy: boolean) { super.clear(isDestroy) - this.closeUI(); + this.closeBlack(); } } \ No newline at end of file diff --git a/assets/core/gui/layer/LayerUI.ts b/assets/core/gui/layer/LayerUI.ts index ebf9446ed70ae374e5b64644280336576263ad55..c209c13e32f220f89837fa580cda5c4927f5224f 100644 --- a/assets/core/gui/layer/LayerUI.ts +++ b/assets/core/gui/layer/LayerUI.ts @@ -1,19 +1,20 @@ import { instantiate, Node, Prefab, SafeArea } from "cc"; import { Collection } from "db://oops-framework/libs/collection/Collection"; +import { resLoader } from "../../common/loader/ResLoader"; import { oops } from "../../Oops"; import { Uiid } from "./LayerEnum"; import { LayerHelper } from "./LayerHelper"; -import { LayerUIElement, UICallbacks, UIParams } from "./LayerUIElement"; +import { LayerUIElement, UIParam, UIState } from "./LayerUIElement"; import { UIConfig } from "./UIConfig"; /** 界面层对象 */ export class LayerUI extends Node { - /** 全局窗口打开失败 */ + /** 全局窗口打开失败事件 */ onOpenFailure: Function = null!; /** 显示界面节点集合 */ - protected ui_nodes = new Collection(); + protected ui_nodes = new Collection(); /** 被移除的界面缓存数据 */ - protected ui_cache = new Map(); + protected ui_cache = new Map(); /** * UI基础层,允许添加多个预制件节点 @@ -22,77 +23,122 @@ 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); + } } /** * 添加一个预制件节点到层容器中,该方法将返回一个唯一`uuid`来标识该操作节点 + * @param uiid 窗口唯一标识 * @param config 界面配置数据 * @param params 自定义参数 - * @param callbacks 回调函数对象,可选 * @returns ture为成功,false为失败 */ - add(uiid: Uiid, config: UIConfig, params?: any, callbacks?: UICallbacks) { - if (this.ui_nodes.has(config.prefab)) { - console.warn(`路径为【${config.prefab}】的预制重复加载`); - return; - } - - // 检查缓存中是否存界面 - let uip = this.ui_cache.get(config.prefab); - if (uip == null) { - uip = new UIParams(); - uip.uiid = uiid.toString(); - uip.config = config; - } - this.ui_nodes.set(config.prefab, uip); + add(uiid: Uiid, config: UIConfig, params?: UIParam): Promise { + return new Promise(async (resolve, reject) => { + if (this.ui_nodes.has(config.prefab)) { + console.warn(`路径为【${config.prefab}】的预制重复加载`); + return; + } - uip.params = params ?? {}; - uip.callbacks = callbacks ?? {}; - uip.valid = true; + // 检查缓存中是否存界面 + let state = this.initUIConfig(uiid, config, params); + await this.load(state); + resolve(state.node); + }); + } - this.load(uip, config.bundle) + /** 初始化界面配置初始值 */ + protected initUIConfig(uiid: Uiid, config: UIConfig, params?: UIParam) { + let state = this.ui_cache.get(config.prefab); + if (state == null) { + if (config.bundle == null) config.bundle = resLoader.defaultBundleName; + if (config.destroy == null) config.destroy = true; + if (config.vacancy == null) config.vacancy = false; + if (config.mask == null) config.mask = false; + if (config.safeArea == null) config.safeArea = false; + + state = new UIState(); + state.uiid = uiid.toString(); + state.config = config; + } + state.params = params ?? {}; + state.valid = true; + this.ui_nodes.set(config.prefab, state); + return state; } /** * 加载界面资源 - * @param uip 显示参数 + * @param state 显示参数 * @param bundle 远程资源包名,如果为空就是默认本地资源包 */ - protected async load(uip: UIParams, bundle?: string) { - // 加载界面资源超时提示 - const timerId = setTimeout(this.onLoadingTimeoutGui, oops.config.game.loadingTimeoutGui); + protected async load(state: UIState): Promise { + return new Promise(async (resolve, reject) => { + // 加载界面资源超时提示 + 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) { + state.node = instantiate(res); + + // 是否启动真机安全区域显示 + if (state.config.safeArea) state.node.addComponent(SafeArea); + + // 窗口事件委托 + const comp = state.node.addComponent(LayerUIElement); + comp.state = state; + } + else { + console.warn(`路径为【${state.config.prefab}】的预制加载失败`); + this.failure(state); + } + + // 关闭界面资源超时提示 + oops.gui.waitClose(); + clearTimeout(timerId); + } - if (uip && uip.node) { - await this.showUi(uip); - } - else { - // 优先加载配置的指定资源包中资源,如果没配置则加载默认资源包资源 - bundle = bundle || oops.res.defaultBundleName; - const res = await oops.res.loadAsync(bundle, uip.config.prefab, Prefab); - if (res) { - uip.node = instantiate(res); - - // 是否启动真机安全区域显示 - if (uip.config.safeArea) uip.node.addComponent(SafeArea); - - // 窗口事件委托 - const dc = uip.node.addComponent(LayerUIElement); - dc.params = uip; - //@ts-ignore - dc.onCloseWindow = this.onCloseWindow.bind(this); - - // 显示界面 - await this.showUi(uip); + await this.uiInit(state); + resolve(state.node); + }); + } + + /** + * 创建界面节点 + * @param state 视图参数 + */ + protected uiInit(state: UIState): Promise { + return new Promise(async (resolve, reject) => { + const comp = state.node.getComponent(LayerUIElement)!; + const r: boolean = await comp.add(); + if (r) { + state.valid = true; // 标记界面为使用状态 + if (!state.params.preload) { + state.params.preload = false; + state.node.parent = this; + } } else { - console.warn(`路径为【${uip.config.prefab}】的预制加载失败`); - this.failure(uip); + console.warn(`路径为【${state.config.prefab}】的自定义预处理逻辑异常.检查预制上绑定的组件中 onAdded 方法,返回true才能正确完成窗口显示流程`); + this.failure(state); } - } - - // 关闭界面资源超时提示 - oops.gui.waitClose(); - clearTimeout(timerId); + resolve(r); + }); } /** 加载超时事件*/ @@ -101,89 +147,57 @@ export class LayerUI extends Node { } /** 窗口关闭事件 */ - protected onCloseWindow(vp: UIParams) { - this.ui_nodes.delete(vp.config.prefab); - } - - /** - * 创建界面节点 - * @param uip 视图参数 - */ - protected async showUi(uip: UIParams): Promise { - // 触发窗口添加事件 - const comp = uip.node.getComponent(LayerUIElement)!; - const r: boolean = await comp.add(); - if (r) { - uip.node.parent = this; - - // 标记界面为使用状态 - uip.valid = true; - } - else { - console.warn(`路径为【${uip.config.prefab}】的自定义预处理逻辑异常.检查预制上绑定的组件中 onAdded 方法,返回true才能正确完成窗口显示流程`); - this.failure(uip); - } - return r; + protected closeUi(state: UIState) { + this.ui_nodes.delete(state.config.prefab); } /** 打开窗口失败逻辑 */ - protected failure(uip: UIParams) { - this.onCloseWindow(uip); - uip.callbacks && uip.callbacks.onLoadFailure && uip.callbacks.onLoadFailure(); + protected failure(state: UIState) { + this.closeUi(state); this.onOpenFailure && this.onOpenFailure(); } /** * 根据预制件路径删除,预制件如在队列中也会被删除,如果该预制件存在多个也会一起删除 * @param prefabPath 预制路径 - * @param isDestroy 移除后是否释放 */ - remove(prefabPath: string, isDestroy?: boolean): void { - let release: any = undefined; - if (isDestroy !== undefined) release = isDestroy; - - // 界面移出舞台 - const uip = this.ui_nodes.get(prefabPath); - if (uip) { - // 优先使用参数中控制的释放条件,如果未传递参数则用配置中的释放条件,默认不缓存关闭的界面 - if (release === undefined) { - release = uip.config.destroy !== undefined ? uip.config.destroy : true; - } + remove(prefabPath: string): void { + const state = this.ui_nodes.get(prefabPath); + if (state) { + let release: boolean = state.config.destroy!; // 不释放界面,缓存起来待下次使用 - if (release === false) { - this.ui_cache.set(uip.config.prefab, uip); - } + if (release === false) this.ui_cache.set(state.config.prefab, state); - const node = uip.node; - const comp = node.getComponent(LayerUIElement)!; + // 界面移出舞台 + const comp = state.node.getComponent(LayerUIElement)!; comp.remove(release); } - - // 验证是否删除后台缓存界面 - if (release === true) this.removeCache(prefabPath); } - /** 删除缓存的界面,当缓存界面被移除舞台时,可通过此方法删除缓存界面 */ - private removeCache(prefabPath: string) { - let vp = this.ui_cache.get(prefabPath); - if (vp) { - this.onCloseWindow(vp); + /** 删除缓存的界面,当调用 remove 移除舞台时,可通过此方法删除缓存界面 */ + 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 state = this.ui_nodes.get(prefabPath); + if (state) state.node.parent = this; + } + /** * 根据预制路径获取已打开界面的节点对象 * @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!; } @@ -204,13 +218,13 @@ export class LayerUI extends Node { const length = this.ui_nodes.array.length - 1; for (let i = length; i >= 0; i--) { const uip = this.ui_nodes.array[i]; - this.remove(uip.config.prefab, isDestroy); + this.remove(uip.config.prefab); } this.ui_nodes.clear(); // 清除缓存中的界面 if (isDestroy) { - this.ui_cache.forEach((value: UIParams, prefabPath: string) => { + this.ui_cache.forEach((value: UIState, prefabPath: string) => { this.removeCache(prefabPath); }); } diff --git a/assets/core/gui/layer/LayerUIElement.ts b/assets/core/gui/layer/LayerUIElement.ts index 6c9c8c941a34cc46bdb33bcfe749315408bfb588..4fede830851316f401e90b43867340006def5d40 100644 --- a/assets/core/gui/layer/LayerUIElement.ts +++ b/assets/core/gui/layer/LayerUIElement.ts @@ -18,13 +18,11 @@ const { ccclass } = _decorator; @ccclass('LayerUIElement') export class LayerUIElement extends Component { /** 视图参数 */ - params: UIParams = null!; + state: UIState = null!; /** 关闭窗口之前 */ - onCloseWindowBefore: Function = null!; - /** 界面关闭回调 - 包括关闭动画播放完(辅助框架内存业务流程使用) */ - private onCloseWindow: Function = null!; + onClose: Function = null!; - /** 窗口添加 */ + /** 添加界面且界面设置到父节点之前 */ add(): Promise { return new Promise(async (resolve, reject) => { // 触发窗口组件上添加到父节点后的事件 @@ -32,7 +30,7 @@ export class LayerUIElement extends Component { const component: any = this.node.components[i]; const func = component[EventOnAdded]; if (func) { - if (await func.call(component, this.params.params) == false) { + if (await func.call(component, this.state.params.data) == false) { resolve(false); return; } @@ -40,8 +38,8 @@ export class LayerUIElement extends Component { } // 触发外部窗口显示前的事件(辅助实现自定义动画逻辑) - if (typeof this.params.callbacks.onAdded === "function") { - this.params.callbacks.onAdded(this.node, this.params.params); + if (typeof this.state.params.onAdded === "function") { + this.state.params.onAdded(this.node, this.state.params.data); } resolve(true); @@ -49,14 +47,14 @@ export class LayerUIElement extends Component { } /** 删除节点,该方法只能调用一次,将会触发onBeforeRemoved回调 */ - remove(isDestroy?: boolean) { - if (this.params.valid) { + remove(isDestroy: boolean) { + if (this.state.valid) { // 触发窗口移除舞台之前事件 - this.applyComponentsFunction(this.node, EventOnBeforeRemove, this.params.params); + this.applyComponentsFunction(this.node, EventOnBeforeRemove, this.state.params.data); // 通知外部对象窗口组件上移除之前的事件(关闭窗口前的关闭动画处理) - if (typeof this.params.callbacks.onBeforeRemove === "function") { - this.params.callbacks.onBeforeRemove(this.node, this.onBeforeRemoveNext.bind(this, isDestroy)); + if (typeof this.state.params.onBeforeRemove === "function") { + this.state.params.onBeforeRemove(this.node, this.onBeforeRemoveNext.bind(this, isDestroy)); } else { this.onBeforeRemoveNext(isDestroy); @@ -68,37 +66,31 @@ export class LayerUIElement extends Component { } /** 窗口关闭前动画处理完后的回调方法,主要用于释放资源 */ - private onBeforeRemoveNext(isDestroy?: boolean) { - this.onCloseWindowBefore && this.onCloseWindowBefore(); - this.removed(this.params, isDestroy); - } - - /** 窗口组件中触发移除事件与释放窗口对象 */ - private removed(uip: UIParams, isDestroy?: boolean) { - uip.valid = false; + private onBeforeRemoveNext(isDestroy: boolean) { + this.state.valid = false; - if (uip.callbacks && typeof uip.callbacks.onRemoved === "function") { - uip.callbacks.onRemoved(this.node, uip.params); + if (this.state.params && typeof this.state.params.onRemoved === "function") { + this.state.params.onRemoved(this.node, this.state.params.data); } - // 界面移除舞台事件 - this.onCloseWindow && this.onCloseWindow(uip); + // 关闭动画播放完后,界面移除舞台事件 + this.onClose && this.onClose(); if (isDestroy) { // 释放界面显示对象 this.node.destroy(); // 释放界面相关资源 - oops.res.release(uip.config.prefab, uip.config.bundle); + oops.res.release(this.state.config.prefab, this.state.config.bundle); - oops.log.logView(`【界面管理】释放【${uip.config.prefab}】界面资源`); + // oops.log.logView(`【界面管理】释放【${uip.config.prefab}】界面资源`); } else { this.node.removeFromParent(); } // 触发窗口组件上窗口移除之后的事件 - this.applyComponentsFunction(this.node, EventOnRemoved, this.params.params); + this.applyComponentsFunction(this.node, EventOnRemoved, this.state.params.data); } private applyComponentsFunction(node: Node, funName: string, params: any) { @@ -112,30 +104,33 @@ export class LayerUIElement extends Component { } onDestroy() { - this.params = null!; - this.onCloseWindowBefore = null!; - this.onCloseWindow = null!; + this.state = null!; + this.onClose = null!; } } /** 本类型仅供gui模块内部使用,请勿在功能逻辑中使用 */ -export class UIParams { +export class UIState { /** 界面唯一编号 */ uiid: string = null!; /** 界面配置 */ config: UIConfig = null!; - /** 传递给打开界面的参数 */ - params: any = null!; /** 窗口事件 */ - callbacks: UICallbacks = null!; + params: UIParam = null!; /** 是否在使用状态 */ valid: boolean = true; /** 界面根节点 */ node: Node = null!; } -/*** 界面回调参数对象定义 */ -export interface UICallbacks { +/*** 界面打开参数 */ +export interface UIParam { + /** 自定义传递参数 */ + data?: any; + + /** 是否开启预加载(默认不开启 - 开启后加载完不显示界面) */ + preload?: boolean; + /** * 节点添加到层级以后的回调 * @param node 当前界面节点 @@ -143,13 +138,6 @@ export interface UICallbacks { */ onAdded?: (node: Node, params: any) => void, - /** - * 窗口节点 destroy 之后回调 - * @param node 当前界面节点 - * @param params 外部传递参数 - */ - onRemoved?: (node: Node | null, params: any) => void, - /** * 如果指定onBeforeRemoved,则next必须调用,否则节点不会被正常删除。 * @@ -159,6 +147,10 @@ export interface UICallbacks { */ onBeforeRemove?: (node: Node, next: Function) => void, - /** 网络异常时,窗口加载失败回调 */ - onLoadFailure?: () => void; + /** + * 窗口节点 destroy 之后回调 + * @param node 当前界面节点 + * @param params 外部传递参数 + */ + onRemoved?: (node: Node, params: any) => void } \ No newline at end of file diff --git a/assets/core/gui/layer/UIConfig.ts b/assets/core/gui/layer/UIConfig.ts index 344c4d7581e21e3b87d5f00a9e071331145f9b3d..7babb7552a870f224ef72c818fac7f89e3ca2ef9 100644 --- a/assets/core/gui/layer/UIConfig.ts +++ b/assets/core/gui/layer/UIConfig.ts @@ -31,7 +31,7 @@ export interface UIConfig { layer: string; /** 预制资源相对路径 */ prefab: string; - /** 是否自动施放(默认不自动释放) */ + /** 是否自动施放(默认自动释放) */ destroy?: boolean; /** -----弹窗属性----- */ @@ -39,7 +39,7 @@ export interface UIConfig { vacancy?: boolean, /** 是否打开窗口后显示背景遮罩(默认关闭) */ mask?: boolean; - /** 是否启动真机安全区域显示 */ + /** 是否启动真机安全区域显示(默认关闭) */ safeArea?: boolean; /** 界面弹出时的节点排序索引 */ siblingIndex?: number; diff --git a/assets/core/utils/DeviceUtil.ts b/assets/core/utils/DeviceUtil.ts index 823b687bc1968a2a0e97545a445c1a01b2948552..02d48262e94386eb7b3c3f8ec8f51d7411bee56f 100644 --- a/assets/core/utils/DeviceUtil.ts +++ b/assets/core/utils/DeviceUtil.ts @@ -40,9 +40,6 @@ export class DeviceUtil { /** 是否为字节小游戏 */ static get isByteDance() { return sys.platform === sys.Platform.BYTEDANCE_MINI_GAME; } - /** 是否为百度小游戏 */ - static get isBaidu() { return sys.platform === sys.Platform.BAIDU_MINI_GAME; } - /** 是否为 vivo 小游戏 */ static get isVivo() { return sys.platform === sys.Platform.VIVO_MINI_GAME; } diff --git a/assets/core/utils/JsonUtil.ts b/assets/core/utils/JsonUtil.ts index 03778f99fd47aa5744dd99f8a2d666766652e44d..2a1f1c811861c48002785d6ec6f9538988861929 100644 --- a/assets/core/utils/JsonUtil.ts +++ b/assets/core/utils/JsonUtil.ts @@ -6,94 +6,94 @@ */ import { JsonAsset } from "cc"; +import { ZipLoader } from "db://oops-framework/core/common/loader/ZipLoader"; import { resLoader } from "../common/loader/ResLoader"; /** 资源路径 */ -const path: string = "config/game/"; +const pathJson: string = "config/game/"; +/** 压缩包资源路径 */ +const pathZip: string = "config/game/game"; /** 数据缓存 */ const data: Map = new Map(); /** JSON数据表工具 */ export class JsonUtil { + /** 是否使用压缩包加载配置表 */ + static zip: boolean = false; + /** * 通知资源名从缓存中获取一个Json数据表 * @param name 资源名 */ static get(name: string): any { - if (data.has(name)) - return data.get(name); + if (data.has(name)) return data.get(name); } /** - * 通知资源名加载Json数据表 - * @param name 资源名 - * @param callback 资源加载完成回调 + * 异步加载Json数据表 + * @param name 资源名 */ - static load(name: string, callback: Function): void { - if (data.has(name)) - callback(data.get(name)); - else { - const url = path + name; - resLoader.load(url, JsonAsset, (err: Error | null, content: JsonAsset) => { - if (err) { - console.warn(err.message); - callback(null); + static load(name: string): Promise { + return new Promise(async (resolve, reject) => { + let content: any = null; + if (data.has(name)) { + resolve(data.get(name)); + } + else { + const url = pathJson + name; + if (this.zip) { + content = await ZipLoader.getJson(pathZip, `${name}.json`); } else { + content = await resLoader.loadAsync(url, JsonAsset); + } + + if (content) { data.set(name, content.json); resLoader.release(url); - callback(content.json); + resolve(content.json); } - }); - } + else { + resolve(null); + } + } + }); } /** - * 异步加载Json数据表 - * @param name 资源名 + * 加载所有配置表数据到缓存中 + * @param isZip 是否为压缩包 + * @param zipNames 压缩包内的资源名列表 */ - static loadAsync(name: string): Promise { - return new Promise((resolve, reject) => { - if (data.has(name)) { - resolve(data.get(name)) + static loadDir(zipNames?: string[]): Promise { + return new Promise(async (resolve, reject) => { + if (this.zip && zipNames) { + await ZipLoader.load(pathZip); + zipNames.forEach(name => { + data.set(name, ZipLoader.getJson(pathZip, `${name}.json`)); + }); + ZipLoader.release(pathZip); + resolve(); } else { - const url = path + name; - resLoader.load(url, JsonAsset, (err: Error | null, content: JsonAsset) => { + resLoader.loadDir(pathJson, (err: Error | null, assets: JsonAsset[]) => { if (err) { - console.warn(err.message); - resolve(null); + console.error(err.message); + resolve(); } else { - data.set(name, content.json); - resLoader.release(url); - resolve(content.json); + assets.forEach(asset => { + data.set(asset.name, asset.json); + }); + resLoader.releaseDir(pathJson); + resolve(); } }); } }); } - /** 加载所有配置表数据到缓存中 */ - static loadDirAsync(): Promise { - return new Promise((resolve, reject) => { - resLoader.loadDir(path, (err: Error | null, assets: JsonAsset[]) => { - if (err) { - console.warn(err.message); - resolve(false); - } - else { - assets.forEach(asset => { - data.set(asset.name, asset.json); - }); - resLoader.releaseDir(path); - resolve(true); - } - }); - }); - } - /** * 通过指定资源名释放资源内存 * @param name 资源名 diff --git a/assets/core/utils/TimeUtils.ts b/assets/core/utils/TimeUtils.ts index 11a287380f4cffe9905fad061e5bd8e6d5cd734b..dfd2161c4071f5e82921d30a49ba49aed4a072a2 100644 --- a/assets/core/utils/TimeUtils.ts +++ b/assets/core/utils/TimeUtils.ts @@ -6,7 +6,7 @@ export class TimeUtil { * @param time2 结束时间 * @returns */ - public static daysBetween(time1: number | string | Date, time2: number | string | Date): number { + static daysBetween(time1: number | string | Date, time2: number | string | Date): number { if (time2 == undefined) { time2 = +new Date(); } @@ -18,7 +18,7 @@ export class TimeUtil { } /** 间隔秒数,时间顺序无要求,最后会获取绝对值 */ - public static secsBetween(time1: number, time2: number) { + static secsBetween(time1: number, time2: number) { let dates = Math.abs((time2 - time1)) / (1000); dates = Math.floor(dates) + 1; return dates; @@ -28,7 +28,7 @@ export class TimeUtil { * 代码休眠时间 * @param ms 毫秒 */ - public static async sleep(ms: number) { + static async sleep(ms: number) { return new Promise((resolve) => { setTimeout(() => { resolve(); diff --git a/assets/libs/behavior-tree/BTreeNode.ts b/assets/libs/behavior-tree/BTreeNode.ts index debef83a30c0530c96f369121ddf2e721b78ff7b..0932554d95dd211b06ee5f8db0b3c3d665a93849 100644 --- a/assets/libs/behavior-tree/BTreeNode.ts +++ b/assets/libs/behavior-tree/BTreeNode.ts @@ -10,35 +10,35 @@ import { IControl } from './IControl'; export abstract class BTreeNode implements IControl { protected _control!: IControl; - public title: string; + title: string; - public constructor() { + constructor() { this.title = this.constructor.name; } - public start(blackboard?: any) { + start(blackboard?: any) { } - public end(blackboard?: any) { + end(blackboard?: any) { } - public abstract run(blackboard?: any): void; + abstract run(blackboard?: any): void; - public setControl(control: IControl) { + setControl(control: IControl) { this._control = control; } - public running(blackboard?: any) { + running(blackboard?: any) { this._control.running(this); } - public success() { + success() { this._control.success(); } - public fail() { + fail() { this._control.fail(); } } \ No newline at end of file diff --git a/assets/libs/behavior-tree/BehaviorTree.ts b/assets/libs/behavior-tree/BehaviorTree.ts index 55f78dc66f3f4cd4ea77fb2211bc5da8b7437776..d7beb52a240c4b65b0d5582ca3c4fca3112ce79e 100644 --- a/assets/libs/behavior-tree/BehaviorTree.ts +++ b/assets/libs/behavior-tree/BehaviorTree.ts @@ -17,7 +17,7 @@ export class BehaviorTree implements IControl { private _blackboard: any; /** 是否已开始执行 */ - public get started(): boolean { + get started(): boolean { return this._started; } @@ -26,7 +26,7 @@ export class BehaviorTree implements IControl { * @param node 根节点 * @param blackboard 外部参数对象 */ - public constructor(node: BTreeNode, blackboard?: any) { + constructor(node: BTreeNode, blackboard?: any) { countUnnamed += 1; this.title = node.constructor.name + '(btree_' + (countUnnamed) + ')'; this._root = node; @@ -34,12 +34,12 @@ export class BehaviorTree implements IControl { } /** 设置行为逻辑中的共享数据 */ - public setObject(blackboard: any) { + setObject(blackboard: any) { this._blackboard = blackboard; } /** 执行行为树逻辑 */ - public run() { + run() { if (this._started) { console.error(`行为树【${this.title}】未调用步骤,在最后一次调用步骤时有一个任务未完成`); } @@ -52,16 +52,16 @@ export class BehaviorTree implements IControl { node.run(this._blackboard); } - public running(node: BTreeNode) { + running(node: BTreeNode) { this._started = false; } - public success() { + success() { this._current.end(this._blackboard); this._started = false; } - public fail() { + fail() { this._current.end(this._blackboard); this._started = false; } diff --git a/assets/libs/behavior-tree/BranchNode.ts b/assets/libs/behavior-tree/BranchNode.ts index 8da38642b05bd7a0d706ecc0260ebf1a387e2eb2..58c39bfde5a59b966c1d7b8789345d4713f79780 100644 --- a/assets/libs/behavior-tree/BranchNode.ts +++ b/assets/libs/behavior-tree/BranchNode.ts @@ -10,7 +10,7 @@ import { BTreeNode } from './BTreeNode'; /** 复合节点 */ export abstract class BranchNode extends BTreeNode { /** 子节点数组 */ - public children: Array; + children: Array; /** 当前任务索引 */ protected _actualTask!: number; /** 正在运行的节点 */ @@ -19,17 +19,17 @@ export abstract class BranchNode extends BTreeNode { /** 外部参数对象 */ protected _blackboard: any; - public constructor(nodes: Array) { + constructor(nodes: Array) { super(); this.children = nodes || []; } - public start() { + start() { this._actualTask = 0; super.start(); } - public run(blackboard?: any) { + run(blackboard?: any) { if (this.children.length == 0) { // 没有子任务直接视为执行失败 this._control.fail(); } @@ -53,17 +53,17 @@ export abstract class BranchNode extends BTreeNode { node.run(this._blackboard); } - public running(node: BTreeNode) { + running(node: BTreeNode) { this._nodeRunning = node; this._control.running(node); } - public success() { + success() { this._nodeRunning = null; this._runningNode.end(this._blackboard); } - public fail() { + fail() { this._nodeRunning = null; this._runningNode.end(this._blackboard); } diff --git a/assets/libs/behavior-tree/Decorator.ts b/assets/libs/behavior-tree/Decorator.ts index b903c915e002857c31fc60c4dc7ba6fb4dc17641..4e8074444902612455161d66d20ab67f2d972edc 100644 --- a/assets/libs/behavior-tree/Decorator.ts +++ b/assets/libs/behavior-tree/Decorator.ts @@ -12,7 +12,7 @@ import { BTreeNode } from './BTreeNode'; * 如果装饰器是true 它所在的子树会被执行,如果是false 所在的子树不会被执行 */ export class Decorator extends BTreeNode { - public node!: BTreeNode; + node!: BTreeNode; constructor(node?: string | BTreeNode) { super() @@ -25,17 +25,17 @@ export class Decorator extends BTreeNode { this.node = BehaviorTree.getNode(node); } - public start() { + start() { this.node.setControl(this); this.node.start(); super.start(); } - public end() { + end() { this.node.end(); } - public run(blackboard: any) { + run(blackboard: any) { this.node.run(blackboard); } } diff --git a/assets/libs/behavior-tree/Selector.ts b/assets/libs/behavior-tree/Selector.ts index 836e4e8d726a79023fbbdda4b802c064c2ecde33..b375609580460cffa92c263b48b7c4f52caf308f 100644 --- a/assets/libs/behavior-tree/Selector.ts +++ b/assets/libs/behavior-tree/Selector.ts @@ -11,12 +11,12 @@ import { BranchNode } from './BranchNode'; * 只要子节点有一个返回true,则停止执行其它子节点,并且Selector返回true。如果所有子节点都返回false,则Selector返回false。 */ export class Selector extends BranchNode { - public success() { + success() { super.success() this._control.success(); } - public fail() { + fail() { super.fail() this._actualTask += 1; diff --git a/assets/libs/behavior-tree/Sequence.ts b/assets/libs/behavior-tree/Sequence.ts index f989babc2002ef293c53f9fe9e78fb7d02553f86..05f33d91dc7fed61683f7588437ac651d8176c3a 100644 --- a/assets/libs/behavior-tree/Sequence.ts +++ b/assets/libs/behavior-tree/Sequence.ts @@ -16,7 +16,7 @@ export class Sequence extends BranchNode { super(nodes); } - public success() { + success() { super.success(); this._actualTask += 1; @@ -28,7 +28,7 @@ export class Sequence extends BranchNode { } } - public fail() { + fail() { super.fail(); this._control.fail(); } diff --git a/assets/libs/ecs/ECS.ts b/assets/libs/ecs/ECS.ts index 19b120d4192c59b9791d71a40bcc56e10c8ded46..fbc243122a48987528c6124edec3ac6738c1793f 100644 --- a/assets/libs/ecs/ECS.ts +++ b/assets/libs/ecs/ECS.ts @@ -42,6 +42,7 @@ export namespace ecs { export interface IComp { canRecycle: boolean; ent: Entity; + tid: number; reset(): void; } @@ -145,17 +146,14 @@ export namespace ecs { if (ctor.tid === -1) { ctor.tid = ECSModel.compTid++; ctor.compName = name; + ECSModel.compCtors.push(ctor); // 注册不同类型的组件 if (canNew) { - ECSModel.compCtors.push(ctor); // 注册不同类型的组件 ECSModel.compPools.set(ctor.tid, []); } - else { - ECSModel.compCtors.push(null!); - } ECSModel.compAddOrRemove.set(ctor.tid, []); } else { - throw new Error(`重复注册组件: ${name}.`); + throw new Error(`ECS 组件重复注册: ${name}.`); } } } diff --git a/assets/libs/ecs/ECSComp.ts b/assets/libs/ecs/ECSComp.ts index ad0f9f8cd445b318ac9a44d576687b9c2f1d1f2d..41ead311dfc87de486b02f131085f92e15bb211f 100644 --- a/assets/libs/ecs/ECSComp.ts +++ b/assets/libs/ecs/ECSComp.ts @@ -14,18 +14,18 @@ import { ECSEntity } from "./ECSEntity"; export abstract class ECSComp implements ecs.IComp { /** 组件的类型编号,-1表示未给该组件分配编号 */ static tid: number = -1; - /** 组件名 */ static compName: string; - /** 拥有该组件的实体 */ - ent!: ECSEntity; - /** * 是否可回收组件对象,默认情况下都是可回收的 * 注:如果该组件对象是由ecs系统外部创建的,则不可回收,需要用户自己手动进行回收 */ canRecycle: boolean = true; + /** 拥有该组件的实体 */ + ent!: ECSEntity; + /** 组件的类型编号 */ + tid: number = -1; /** * 组件被回收时会调用这个接口。可以在这里重置数据,或者解除引用 diff --git a/assets/libs/ecs/ECSEntity.ts b/assets/libs/ecs/ECSEntity.ts index a790a90b7a8d77102e4b56c37ce8b5e082b993e0..e6c2d4f78895540ac8df17737424259470dff4aa 100644 --- a/assets/libs/ecs/ECSEntity.ts +++ b/assets/libs/ecs/ECSEntity.ts @@ -81,22 +81,30 @@ export class ECSEntity { this._parent = value; } - private _children: Map | null = null; - /** 子实体集合 */ - get children(): Map { - if (this._children == null) { - this._children = new Map(); - } - return this._children; + /** 子实体 */ + private childs: Map = null!; + + /** 获取子实体 */ + getChild(eid: number) { + return this.childs.get(eid) as T; } /** * 添加子实体 * @param entity 被添加的实体对象 + * @returns 子实体的唯一编号, -1表示添加失败 */ - addChild(entity: ECSEntity) { + addChild(entity: ECSEntity): number { + if (this.childs == null) this.childs = new Map(); + + if (this.childs.has(entity.eid)) { + console.warn(`子实体${entity.name}已存在`); + return -1; + } + entity._parent = this; - this.children.set(entity.eid, entity); + this.childs.set(entity.eid, entity); + return entity.eid; } /** @@ -106,10 +114,10 @@ export class ECSEntity { * @returns */ removeChild(entity: ECSEntity, isDestroy = true) { - if (this.children == null) return; + if (this.childs == null) return; entity.parent = null; - this.children.delete(entity.eid); + this.childs.delete(entity.eid); if (isDestroy) entity.destroy(); } @@ -154,6 +162,7 @@ export class ECSEntity { // @ts-ignore this[ctor.compName] = comp; this.compTid2Ctor.set(compTid, ctor); + comp.tid = compTid; comp.ent = this; // 广播实体添加组件的消息 broadcastCompAddOrRemove(this, compTid); @@ -173,9 +182,11 @@ export class ECSEntity { this[tmpCtor.compName] = ctor; this.compTid2Ctor.set(compTid, tmpCtor); //@ts-ignore - ctor.ent = this; + ctor.tid = compTid; //@ts-ignore ctor.canRecycle = false; + //@ts-ignore + ctor.ent = this; broadcastCompAddOrRemove(this, compTid); return this; @@ -272,11 +283,11 @@ export class ECSEntity { } // 移除模块上所有子模块 - if (this._children) { - this._children.forEach(e => { + if (this.childs) { + this.childs.forEach(e => { this.removeChild(e); }); - this._children = null; + this.childs = null!; } // 移除实体上所有组件 diff --git a/assets/libs/extension/DateExt.ts b/assets/libs/extension/DateExt.ts index 4120fec7afad06488843436f56a30cd54728491f..36a44a51d789a91f89b6aa72f7091126aa56d1f4 100644 --- a/assets/libs/extension/DateExt.ts +++ b/assets/libs/extension/DateExt.ts @@ -1,10 +1,26 @@ declare global { interface Date { + /** + * 时间格式化 + * @param format 时间格式,例如:yy-mm-dd hh:mm:ss + */ format(format: string): string; + + /** + * 时间加法 + * @param addMillis 时间加法,单位毫秒 + */ + addTime(addMillis: number): Date; + + /** + * 验证时间是否在指定范围 + * @param t1 范围开始时间 + * @param t2 范围结束时间 + */ + range(t1: number | Date, t2: number | Date): boolean; } } -/** 格式化时间字符串 */ Date.prototype.format = function (format: string): string { const year: number = this.getFullYear(); const month: number = this.getMonth() + 1; @@ -35,4 +51,27 @@ Date.prototype.format = function (format: string): string { return r; }; -export { }; +Date.prototype.addTime = function (addMillis: number): Date { + return new Date(this.getTime() + addMillis); +} + +Date.prototype.range = function (d1: number | Date, d2: number | Date): boolean { + let t1: number = -1; + let t2: number = -1; + if (d1 instanceof Date) + t1 = d1.getTime(); + else + t1 = d1; + if (d2 instanceof Date) + t2 = d2.getTime(); + else + t2 = d2; + + let now = this.getTime(); + if (now >= t1 && now < t2) { + return true; + } + return false; +} + +export { }; \ No newline at end of file diff --git a/assets/libs/gui/button/ButtonSimple.ts b/assets/libs/gui/button/ButtonSimple.ts index b66216ff51272d30c92459bf6779efc1d57f46fe..a96b0b9f8d56d7e28bbe5c225712522904507af5 100644 --- a/assets/libs/gui/button/ButtonSimple.ts +++ b/assets/libs/gui/button/ButtonSimple.ts @@ -59,7 +59,7 @@ export default class ButtonSimple extends Component { } /** 短按触摸音效 */ - protected async playEffect() { + protected playEffect() { if (ButtonSimple.effectPath) { oops.audio.playEffect(ButtonSimple.effectPath); } diff --git a/assets/libs/gui/label/LabelTime.ts b/assets/libs/gui/label/LabelTime.ts index 7831f6d684405319bc479e4074cfacfe9e907590..d8b9b07bafbb488cfb2a66100dcee6361e731818 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"; + dayFormat: string = "{0}天{1}小时"; @property({ - tooltip: "时间格式化" + tooltip: "时间格式化", }) timeFormat: string = "{0}:{1}:{2}"; @property({ - tooltip: "是否有00" + tooltip: "时间是否有固定二位数据", }) 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]; + }); } /** 格式化字符串 */ @@ -75,7 +74,7 @@ export default class LabelTime extends Label { let dataFormat = this.dayFormat; let index = dataFormat.indexOf("{1}"); if (hours == 0 && index > -1) { - dataFormat = dataFormat.substring(0, index - 1); + dataFormat = dataFormat.substring(0, index); } let df = dataFormat; if (date > 1 && dataFormat.indexOf("days") < 0) { @@ -84,23 +83,21 @@ export default class LabelTime extends Label { if (date < 2) { df = df.replace("days", "day"); } - this.result = this.replace(df, date, hours); // 如果天大于1,则显示 "1 Day..." + + if (this.zeroize) { + this.result = this.replace(df, date, this.coverString(hours)); + } + else { + 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 +105,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 +119,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 +136,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 +189,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 +207,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(); } -} +} \ No newline at end of file diff --git a/assets/libs/gui/language/Language.ts b/assets/libs/gui/language/Language.ts index 020efea68612a52c8fbe50448567132d048c53b6..88fea18f4dd6d57705a80a6e26d48154f450b7d1 100644 --- a/assets/libs/gui/language/Language.ts +++ b/assets/libs/gui/language/Language.ts @@ -53,7 +53,7 @@ export class LanguageManager { * @param language 语言名 * @param callback 多语言资源数据加载完成回调 */ - setLanguage(language: string, callback?: (success: boolean) => void) { + setLanguage(language: string, callback?: Function) { if (language == null || language == "") { language = this._defaultLanguage; } @@ -68,7 +68,7 @@ export class LanguageManager { } if (language === LanguageData.current) { - callback && callback(false); + callback && callback(); return; } @@ -78,7 +78,7 @@ export class LanguageManager { LanguageData.current = language; this._languagePack.updateLanguage(language); this._languagePack.releaseLanguageAssets(oldLanguage); - callback && callback(true); + callback && callback(); }); } diff --git a/assets/libs/gui/language/LanguagePack.ts b/assets/libs/gui/language/LanguagePack.ts index 53bfadffad03fe039a697d8e22e28caf2b015336..1d227266fb0a02f5239cdb4e7abdbf09acf14f2e 100644 --- a/assets/libs/gui/language/LanguagePack.ts +++ b/assets/libs/gui/language/LanguagePack.ts @@ -44,7 +44,7 @@ export class LanguagePack { /** 多语言Excel配置表数据 */ private loadTable(lang: string): Promise { return new Promise(async (resolve, reject) => { - let json = await JsonUtil.loadAsync("Language"); + let json = await JsonUtil.load("Language"); if (json) { LanguageData.language.set(LanguageDataType.Excel, json); Logger.instance.logConfig("config/game/Language", "下载语言包 table 资源"); diff --git a/assets/libs/model-view/StringFormat.ts b/assets/libs/model-view/StringFormat.ts index 89e3b527e4f867f464df9b25d8bb172a6997e12d..24b8d8e22b9222645a94b596900cbe339c6fa311 100644 --- a/assets/libs/model-view/StringFormat.ts +++ b/assets/libs/model-view/StringFormat.ts @@ -1,3 +1,5 @@ +import { LanguageData } from "../gui/language/LanguageData"; + /** * 数值格式化函数, 通过语义解析自动设置值的范围 * //整数 @@ -93,19 +95,20 @@ class StringFormat { } /** 将数字缩短显示为KMBT单位 大写,目前只支持英文 */ - private kmbt(value: number, lang: string = 'en') { + private kmbt(value: number) { //10^4=万, 10^8=亿,10^12=兆,10^16=京, - let counts = [1000, 1000000, 1000000000, 1000000000000]; - let units = ['', 'K', 'M', 'B', 'T']; + let counts: number[] = null!; + let units: string[] = null!; - switch (lang) { + switch (LanguageData.current) { case 'zh': //10^4=万, 10^8=亿,10^12=兆,10^16=京, counts = [10000, 100000000, 1000000000000, 10000000000000000]; units = ['', '万', '亿', '兆', '京']; break; - default: + counts = [1000, 1000000, 1000000000, 1000000000000]; + units = ['', 'K', 'M', 'B', 'T']; break; } diff --git a/assets/module/common/CCBusiness.ts b/assets/module/common/CCBusiness.ts new file mode 100644 index 0000000000000000000000000000000000000000..e8a68368439cc3bb470a91322d070c07c76aa980 --- /dev/null +++ b/assets/module/common/CCBusiness.ts @@ -0,0 +1,85 @@ +/* + * @Author: dgflash + * @Date: 2025-09-18 10:20:51 + * @LastEditors: dgflash + * @LastEditTime: 2025-09-18 17:20:51 + */ + +import { EventDispatcher } from "../../core/common/event/EventDispatcher"; +import { ListenerFunc } from "../../core/common/event/EventMessage"; +import { CCEntity } from "./CCEntity"; + +/** 业务逻辑 */ +export class CCBusiness { + ent!: T; + + /** 业务逻辑初始化 */ + protected init() { + + } + + destroy() { + // 释放消息对象 + if (this._event) { + this._event.destroy(); + this._event = null; + } + } + + //#region 全局事件管理 + + private _event: EventDispatcher | null = null; + /** 全局事件管理器 */ + private get event(): EventDispatcher { + if (this._event == null) this._event = new EventDispatcher(); + return this._event; + } + + /** + * 注册全局事件 + * @param event 事件名 + * @param listener 处理事件的侦听器函数 + * @param object 侦听函数绑定的this对象 + */ + on(event: string, listener: ListenerFunc, object: any) { + this.event.on(event, listener, object); + } + + /** + * 移除全局事件 + * @param event 事件名 + */ + off(event: string) { + this.event.off(event); + } + + /** + * 触发全局事件 + * @param event 事件名 + * @param args 事件参数 + */ + dispatchEvent(event: string, ...args: any) { + this.event.dispatchEvent(event, ...args); + } + + /** + * 批量设置全局事件 + * @example + * this.setEvent("onGlobal"); + * this.dispatchEvent("onGlobal", "全局事件"); + * + * onGlobal(event: string, args: any) { console.log(args) }; + */ + protected setEvent(...args: string[]) { + const self: any = this; + for (const name of args) { + const func = self[name]; + if (func) + this.on(name, func, this); + else + console.error(`名为【${name}】的全局事方法不存在`); + } + } + + //#endregion +} \ No newline at end of file diff --git a/assets/module/common/CCVMParentComp.ts.meta b/assets/module/common/CCBusiness.ts.meta similarity index 70% rename from assets/module/common/CCVMParentComp.ts.meta rename to assets/module/common/CCBusiness.ts.meta index 922f5b25eb8ef8cb489bda189ee7dfb7ede62b70..fcbff47f1c1c9e55e32e69e612e9eb4083de8b7f 100644 --- a/assets/module/common/CCVMParentComp.ts.meta +++ b/assets/module/common/CCBusiness.ts.meta @@ -2,7 +2,7 @@ "ver": "4.0.24", "importer": "typescript", "imported": true, - "uuid": "5908a4e6-3359-48b6-95e0-a3b44ea50790", + "uuid": "4a753a57-2867-478e-b770-c05d96535c4e", "files": [], "subMetas": {}, "userData": {} diff --git a/assets/module/common/CCEntity.ts b/assets/module/common/CCEntity.ts new file mode 100644 index 0000000000000000000000000000000000000000..3f4dc74c0377cbdba4607ff2052845157ec5389a --- /dev/null +++ b/assets/module/common/CCEntity.ts @@ -0,0 +1,186 @@ +import { __private, Node } from "cc"; +import { resLoader } from "../../core/common/loader/ResLoader"; +import { gui } from "../../core/gui/Gui"; +import { LayerUIElement, UIParam } from "../../core/gui/layer/LayerUIElement"; +import { oops } from "../../core/Oops"; +import { ViewUtil } from "../../core/utils/ViewUtil"; +import { ecs } from "../../libs/ecs/ECS"; +import { ECSEntity } from "../../libs/ecs/ECSEntity"; +import { CompType } from "../../libs/ecs/ECSModel"; +import { CCBusiness } from "./CCBusiness"; +import { CCView } from "./CCView"; +import { CCViewVM } from "./CCViewVM"; + +export type ECSCtor = __private.__types_globals__Constructor | __private.__types_globals__AbstractedConstructor; +export type ECSView = CCViewVM | CCView; + +/** ECS 游戏模块实体 */ +export abstract class CCEntity extends ecs.Entity { + //#region 子模块管理 + /** 单例子实体 */ + private singletons: Map = null!; + + /** 添加单例子实体 */ + addChildSingleton(cls: any): T { + if (this.singletons == null) this.singletons = new Map(); + if (this.singletons.has(cls)) { + console.error(`${cls.name} 单例子实体已存在`); + return null!; + } + let entity = cls.create(); + this.singletons.set(cls, entity); + this.addChild(entity); + return entity as T; + } + + /** 获取单例子实体 */ + getChildSingleton(cls: any): T { + return this.singletons.get(cls) as T; + } + + /** 移除单例子实体 */ + removeChildSingleton(cls: any) { + let entity = this.singletons.get(cls); + if (entity) { + this.singletons.delete(cls); + this.removeChild(entity); + } + } + //#endregion + + //#region 游戏视图层管理 + /** + * 通过资源内存中获取预制上的组件添加到ECS实体中 + * @param ctor 界面逻辑组件 + * @param parent 显示对象父级 + * @param path 显示资源地址 + * @param bundleName 资源包名称 + */ + addPrefab(ctor: ECSCtor, parent: Node, path: string, bundleName: string = resLoader.defaultBundleName) { + const node = ViewUtil.createPrefabNode(path, bundleName); + const comp = node.getComponent(ctor)!; + this.add(comp); + node.parent = parent; + } + + /** + * 添加视图层组件 + * @param ctor 界面逻辑组件 + * @param params 界面参数 + * @returns 界面节点 + */ + addUi(ctor: ECSCtor, params?: UIParam): Promise { + return new Promise(async (resolve, reject) => { + const key = gui.internal.getKey(ctor); + if (key) { + if (params == null) { + params = { preload: true }; + } + else { + params.preload = true; + } + + const node = await oops.gui.open(key, params); + const comp = node.getComponent(ctor) as ecs.Comp; + this.add(comp); + oops.gui.show(key); + resolve(node); + } + else { + console.error(`${key} 界面组件未使用 gui.register 注册`); + } + }); + } + + /** + * 移除视图层组件 + * @param ctor 界面逻辑组件 + */ + removeUi(ctor: CompType) { + const key = gui.internal.getKey(ctor); + if (key) { + const node = oops.gui.get(key); + if (node == null) { + console.error(`${key} 界面重复关闭`); + return; + } + + const comp = node.getComponent(LayerUIElement); + if (comp) { + // 处理界面关闭动画播放完成后,移除ECS组件,避免使用到组件实体数据还在动画播放时在使用导致的空对象问题 + comp.onClose = this.remove.bind(this, ctor); + oops.gui.remove(key); + } + } + else { + this.remove(ctor); + } + } + //#endregion + + //#region 游戏业务层管理 + /** 模块业务逻辑组件 */ + private businesss: Map> = null!; + + /** + * 批量添加组件 + * @param ctors 组件类 + * @returns + */ + addBusinesss>(...clss: any[]) { + for (let ctor of clss) { + this.addBusiness(ctor); + } + } + + /** + * 添加业务逻辑组件 + * @param cls 业务逻辑组件类 + * @returns 业务逻辑组件实例 + */ + addBusiness>(cls: any): T { + if (this.businesss == null) this.businesss = new Map(); + if (this.businesss.has(cls)) { + console.error(`${cls.name} 业务逻辑组件已存在`); + return null!; + } + let business = new cls(); + business.ent = this; + business.init(); + this.businesss.set(cls, business); + return business as T; + } + + /** + * 获取业务逻辑组件 + * @param cls 业务逻辑组件类 + * @returns 业务逻辑组件实例 + */ + getBusiness>(cls: any): T { + return this.businesss.get(cls) as T; + } + + /** + * 移除业务逻辑组件 + * @param cls 业务逻辑组件类 + */ + removeBusiness(cls: any) { + let business = this.businesss.get(cls); + if (business) this.businesss.delete(cls); + } + //#endregion + + destroy(): void { + if (this.singletons) { + this.singletons.clear(); + this.singletons = null!; + } + + if (this.businesss) { + this.businesss.forEach(business => business.destroy()); + this.businesss.clear(); + this.businesss = null!; + } + super.destroy(); + } +} \ No newline at end of file diff --git a/assets/module/common/CCEntity.ts.meta b/assets/module/common/CCEntity.ts.meta new file mode 100644 index 0000000000000000000000000000000000000000..7040995c5d7398f9e2e2d2fcdc66fcedba217328 --- /dev/null +++ b/assets/module/common/CCEntity.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "59805570-9a5d-4894-aac0-14a60c895c74", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/module/common/CCComp.ts b/assets/module/common/CCView.ts similarity index 61% rename from assets/module/common/CCComp.ts rename to assets/module/common/CCView.ts index 814d300fadfb0bf48535917eb30a16c752eaf249..01ccbe56674d7e6f152d7eca52e2e84c18fb8825 100644 --- a/assets/module/common/CCComp.ts +++ b/assets/module/common/CCView.ts @@ -1,46 +1,56 @@ -/* - * @Author: dgflash - * @Date: 2021-11-11 19:05:32 - * @LastEditors: dgflash - * @LastEditTime: 2022-09-06 17:20:51 - */ - -import { _decorator } from 'cc'; -import { GameComponent } from './GameComponent'; -import { ecs } from '../../libs/ecs/ECS'; - -const { ccclass, property } = _decorator; - -/** - * 游戏显示对象组件 - * - * 功能介绍: - * 1. 对象拥有 cc.Component 组件功能与 ecs.Comp 组件功能 - * 2. 对象自带全局事件监听、释放、发送全局消息功能 - * 3. 对象管理的所有节点摊平,直接通过节点名获取cc.Node对象 - * - * 应用场景 - * 1. 网络游戏,优先有数据对象,然后创建视图对象,当释放视图组件时,部分场景不希望释放数据对象 - * - * @example -@ccclass('RoleViewComp') -@ecs.register('RoleView', false) -export class RoleViewComp extends CCComp { - @property({ type: sp.Skeleton, tooltip: '角色动画' }) - spine: sp.Skeleton = null!; - - onLoad(){ - - } -} - */ -@ccclass('CCComp') -export abstract class CCComp extends GameComponent implements ecs.IComp { - static tid: number = -1; - static compName: string; - - canRecycle!: boolean; - ent!: ecs.Entity; - - abstract reset(): void; +/* + * @Author: dgflash + * @Date: 2021-11-11 19:05:32 + * @LastEditors: dgflash + * @LastEditTime: 2022-09-06 17:20:51 + */ + +import { ecs } from '../../libs/ecs/ECS'; +import { ECSModel } from '../../libs/ecs/ECSModel'; +import { CCEntity } from './CCEntity'; +import { GameComponent } from './GameComponent'; + +/** + * ECS 游戏显示对象组件 + * + * 功能介绍: + * 1. 对象拥有 cc.Component 组件功能与 ecs.Comp 组件功能 + * 2. 对象自带全局事件监听、释放、发送全局消息功能 + * 3. 对象管理的所有节点摊平,直接通过节点名获取cc.Node对象 + * + * 应用场景 + * 1. 网络游戏,优先有数据对象,然后创建视图对象,当释放视图组件时,部分场景不希望释放数据对象 + * + * @example +@ccclass('RoleViewComp') +@ecs.register('RoleView', false) +export class RoleViewComp extends CCView { + @property({ type: sp.Skeleton, tooltip: '角色动画' }) + spine: sp.Skeleton = null!; + + onLoad(){ + + } +} + */ +export abstract class CCView extends GameComponent implements ecs.IComp { + static tid: number = -1; + static compName: string; + + canRecycle!: boolean; + ent!: T; + tid: number = -1; + + /** 从父节点移除自己 */ + remove() { + const cct = ECSModel.compCtors[this.tid]; + if (this.ent) { + this.ent.removeUi(cct); + } + else { + console.error(`组件 ${this.name} 移除失败,组件未注册`); + } + } + + abstract reset(): void; } \ No newline at end of file diff --git a/assets/module/common/CCView.ts.meta b/assets/module/common/CCView.ts.meta new file mode 100644 index 0000000000000000000000000000000000000000..d44693aad15f5459ee80ea9675046709f08d16b8 --- /dev/null +++ b/assets/module/common/CCView.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "7f4397f9-04cd-42e3-a323-d468c93b19c0", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/module/common/CCVMParentComp.ts b/assets/module/common/CCViewVM.ts similarity index 67% rename from assets/module/common/CCVMParentComp.ts rename to assets/module/common/CCViewVM.ts index 55b70460e5e3912d3ac231691583f9be402d3cd4..6f1219cd0eaab63b02fe981d8d4228284c3b8e9d 100644 --- a/assets/module/common/CCVMParentComp.ts +++ b/assets/module/common/CCViewVM.ts @@ -1,58 +1,68 @@ -/* - * @Author: dgflash - * @Date: 2021-11-11 19:05:32 - * @LastEditors: dgflash - * @LastEditTime: 2022-09-06 17:22:05 - */ - -import { _decorator } from 'cc'; -import { ecs } from '../../libs/ecs/ECS'; -import VMParent from '../../libs/model-view/VMParent'; - -const { ccclass, property } = _decorator; - -/** - * 支持 MVVM 功能的游戏显示对象组件 - * - * 使用方法: - * 1. 对象拥有 cc.Component 组件功能与 ecs.Comp 组件功能 - * 2. 对象自带全局事件监听、释放、发送全局消息功能 - * 3. 对象管理的所有节点摊平,直接通过节点名获取cc.Node对象(节点名不能有重名) - * 4. 对象支持 VMParent 所有功能 - * - * 应用场景 - * 1. 网络游戏,优先有数据对象,然后创建视图对象,当释放视图组件时,部分场景不希望释放数据对象 - * - * @example -@ccclass('LoadingViewComp') -@ecs.register('LoadingView', false) -export class LoadingViewComp extends CCVMParentComp { - // VM 组件绑定数据 - data: any = { - // 加载资源当前进度 - finished: 0, - // 加载资源最大进度 - total: 0, - // 加载资源进度比例值 - progress: "0", - // 加载流程中提示文本 - prompt: "" - }; - - private progress: number = 0; - - reset(): void { - - } -} - */ -@ccclass('CCVMParentComp') -export abstract class CCVMParentComp extends VMParent implements ecs.IComp { - static tid: number = -1; - static compName: string; - - canRecycle!: boolean; - ent!: ecs.Entity; - - abstract reset(): void; +/* + * @Author: dgflash + * @Date: 2021-11-11 19:05:32 + * @LastEditors: dgflash + * @LastEditTime: 2022-09-06 17:22:05 + */ + +import { ecs } from '../../libs/ecs/ECS'; +import { ECSModel } from '../../libs/ecs/ECSModel'; +import VMParent from '../../libs/model-view/VMParent'; +import { CCEntity } from './CCEntity'; + +/** + * 支持 MVVM 功能的 ECS 游戏显示对象组件 + * + * 使用方法: + * 1. 对象拥有 cc.Component 组件功能与 ecs.Comp 组件功能 + * 2. 对象自带全局事件监听、释放、发送全局消息功能 + * 3. 对象管理的所有节点摊平,直接通过节点名获取cc.Node对象(节点名不能有重名) + * 4. 对象支持 VMParent 所有功能 + * + * 应用场景 + * 1. 网络游戏,优先有数据对象,然后创建视图对象,当释放视图组件时,部分场景不希望释放数据对象 + * + * @example +@ccclass('LoadingViewComp') +@ecs.register('LoadingView', false) +export class LoadingViewComp extends CCViewVM { + // VM 组件绑定数据 + data: any = { + // 加载资源当前进度 + finished: 0, + // 加载资源最大进度 + total: 0, + // 加载资源进度比例值 + progress: "0", + // 加载流程中提示文本 + prompt: "" + }; + + private progress: number = 0; + + reset(): void { + + } +} + */ +export abstract class CCViewVM extends VMParent implements ecs.IComp { + static tid: number = -1; + static compName: string; + + canRecycle!: boolean; + ent!: T; + tid: number = -1; + + /** 从父节点移除自己 */ + remove() { + const cct = ECSModel.compCtors[this.tid]; + if (this.ent) { + this.ent.removeUi(cct); + } + else { + console.error(`组件 ${this.name} 移除失败,组件未注册`); + } + } + + abstract reset(): void; } \ No newline at end of file diff --git a/assets/module/common/CCViewVM.ts.meta b/assets/module/common/CCViewVM.ts.meta new file mode 100644 index 0000000000000000000000000000000000000000..2536e00450d297ab23e61d4f67ff56d9e01858dd --- /dev/null +++ b/assets/module/common/CCViewVM.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "33d31b2d-c771-4759-9fc6-96bbd5bcfa15", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/module/common/GameComponent.ts b/assets/module/common/GameComponent.ts index bc29552665df6959a26f9346717aa9ae1bc14cb0..031ce005d28da93445b1fbfe5f5618ba2272847d 100644 --- a/assets/module/common/GameComponent.ts +++ b/assets/module/common/GameComponent.ts @@ -480,6 +480,12 @@ export class GameComponent extends Component { /** 游戏旋转屏幕事件回调 */ protected onGameOrientation(): void { } //#endregion + + /** 移除自己 */ + remove() { + oops.gui.removeByNode(this.node); + } + protected onDestroy() { // 释放消息对象 if (this._event) { diff --git a/assets/module/common/ModuleUtil.ts b/assets/module/common/ModuleUtil.ts deleted file mode 100644 index 7814aa181fd7935b5665b66ad1f7dedaae556b11..0000000000000000000000000000000000000000 --- a/assets/module/common/ModuleUtil.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { Node, __private } from "cc"; -import { oops } from "../../core/Oops"; -import { resLoader } from "../../core/common/loader/ResLoader"; -import { Uiid } from "../../core/gui/layer/LayerEnum"; -import { LayerUIElement, UICallbacks } from "../../core/gui/layer/LayerUIElement"; -import { ViewUtil } from "../../core/utils/ViewUtil"; -import { ecs } from "../../libs/ecs/ECS"; -import { CompType } from "../../libs/ecs/ECSModel"; -import { CCComp } from "./CCComp"; -import { CCVMParentComp } from "./CCVMParentComp"; - -export type ECSCtor = __private.__types_globals__Constructor | __private.__types_globals__AbstractedConstructor; - -export class ModuleUtil { - /** - * 添加界面组件 - * @param ent 模块实体 - * @param ctor 界面逻辑组件 - * @param uiId 界面资源编号 - * @param uiArgs 界面参数 - */ - static addViewUi(ent: ecs.Entity, ctor: ECSCtor, uiId: Uiid, uiArgs: any = null) { - const uic: UICallbacks = { - onAdded: (node: Node, params: any) => { - const comp = node.getComponent(ctor) as ecs.Comp; - //@ts-ignore - if (!ent.has(ctor)) ent.add(comp); - } - }; - oops.gui.open(uiId, uiArgs, uic); - } - - /** - * 异步添加视图层组件 - * @param ent 模块实体 - * @param ctor 界面逻辑组件 - * @param uiId 界面资源编号 - * @param uiArgs 界面参数 - * @returns 界面节点 - */ - static addViewUiAsync(ent: ecs.Entity, ctor: ECSCtor, uiId: Uiid, uiArgs: any = null): Promise { - return new Promise((resolve, reject) => { - const uic: UICallbacks = { - onAdded: (node: Node, params: any) => { - const comp = node.getComponent(ctor) as ecs.Comp; - ent.add(comp); - resolve(node); - }, - onLoadFailure: () => { - resolve(null); - } - }; - oops.gui.open(uiId, uiArgs, uic); - }); - } - - /** - * 通过资源内存中获取预制上的组件添加到ECS实体中 - * @param ent 模块实体 - * @param ctor 界面逻辑组件 - * @param parent 显示对象父级 - * @param url 显示资源地址 - * @param bundleName 资源包名称 - */ - static addView(ent: ecs.Entity, ctor: ECSCtor, parent: Node, url: string, bundleName: string = resLoader.defaultBundleName) { - const node = ViewUtil.createPrefabNode(url, bundleName); - const comp = node.getComponent(ctor)!; - ent.add(comp); - node.parent = parent; - } - - /** - * 业务实体上移除界面组件 - * @param ent 模块实体 - * @param ctor 界面逻辑组件 - * @param uiId 界面资源编号 - * @param isDestroy 是否释放界面缓存(默认为释放界面缓存) - * @param onRemoved 窗口关闭完成事件 - */ - static removeViewUi(ent: ecs.Entity, ctor: CompType, uiId: Uiid, isDestroy: boolean = true, onRemoved?: Function) { - const node = oops.gui.get(uiId); - if (!node) { - if (onRemoved) onRemoved(); - return; - } - - const comp = node.getComponent(LayerUIElement); - if (comp) { - comp.onCloseWindowBefore = () => { - // 移除ECS显示组件 - if (isDestroy) ent.remove(ctor, isDestroy); - if (onRemoved) onRemoved(); - }; - oops.gui.remove(uiId, isDestroy); - } - } -} \ No newline at end of file diff --git a/assets/module/config/BuildTimeConstants.ts b/assets/module/config/BuildTimeConstants.ts index 89cfa749913f812683fc1e0920a5bf8864d7c3ec..81fc32a47f90ed787bd242c46899920cfc77223a 100644 --- a/assets/module/config/BuildTimeConstants.ts +++ b/assets/module/config/BuildTimeConstants.ts @@ -10,7 +10,7 @@ const keys = (Object.keys(buildTimeConstants) as (keyof typeof buildTimeConstant /* 游戏运行环境 */ export class BuildTimeConstants { - constructor() { + toString() { const keyNameMaxLen = keys.reduce((len, key) => Math.max(len, key.length), 0); const enviroment = `${keys.map((key) => { const value = buildTimeConstants[key]; diff --git a/assets/module/config/Config.ts b/assets/module/config/Config.ts index e3fece872f525828ff97302388380e5d983e6c0b..1a1b8301330ec04b988b61ce4a983f3951ed2d00 100644 --- a/assets/module/config/Config.ts +++ b/assets/module/config/Config.ts @@ -5,13 +5,14 @@ * @LastEditTime: 2022-11-01 15:47:16 */ +import { BuildTimeConstants } from "./BuildTimeConstants"; import { GameConfig } from "./GameConfig"; import { GameQueryConfig } from "./GameQueryConfig"; /** 游戏配置静态访问类 */ export class Config { /** 环境常量 */ - // btc!: BuildTimeConstants; + btc: BuildTimeConstants = new BuildTimeConstants(); /** 游戏配置数据,版本号、支持语种等数据 */ game!: GameConfig; diff --git a/assets/module/config/GameConfig.ts b/assets/module/config/GameConfig.ts index af0a99ca7ef933a5017ecea0d14fa8e4329f447b..2f0ac6cf37c017310c81c9374e9d9521bc840451 100644 --- a/assets/module/config/GameConfig.ts +++ b/assets/module/config/GameConfig.ts @@ -4,38 +4,71 @@ * @LastEditors: dgflash * @LastEditTime: 2023-02-14 14:27:22 */ - import { oops } from "../../core/Oops"; +/** 游戏自定义参数分组类型 */ +export enum GameConfigCustomType { + /** 开发环境 */ + Dev = "dev", + /** 测试环境 */ + Test = "test", + /** 生产环境 */ + Prod = "prod", +} + /* 游戏配置解析,对应 resources/config/config.json 配置 */ export class GameConfig { /** 客户端版本号配置 */ get version(): string { - return this._data["config"]["version"]; - } - /** 包名 */ - get package(): string { - return this._data["config"]["package"]; - } - /** 游戏每秒传输帧数 */ - get frameRate(): number { - return this._data.config.frameRate; + return this.data.version; } /** 本地存储内容加密 key */ get localDataKey(): string { - return this._data.config.localDataKey; + return this.data.localDataKey; } /** 本地存储内容加密 iv */ get localDataIv(): string { - return this._data.config.localDataIv; + return this.data.localDataIv; + } + /** 游戏每秒传输帧数 */ + get frameRate(): number { + return this.data.frameRate; + } + /** 是否开启移动设备安全区域适配 */ + get mobileSafeArea(): boolean { + return this.data.mobileSafeArea || false; + } + /** 加载界面资源超时提示 */ + get loadingTimeoutGui(): number { + return this.data.loadingTimeoutGui || 1000; + } + /** 是否显示统计信息 */ + get stats(): number { + return this.data.stats; } /** Http 服务器地址 */ get httpServer(): string { - return this._data.config.httpServer; + return this.data.httpServer; } /** Http 请求超时时间 */ get httpTimeout(): number { - return this._data.config.httpTimeout; + return this.data.httpTimeout; + } + /** WebSocket 服务器地址 */ + get webSocketServer(): string { + return this.data.webSocketServer; + } + /** WebSocket 心跳间隔时间(毫秒) */ + get webSocketHeartTime(): number { + return this.data.webSocketHeartTime; + } + /** WebSocket 指定时间没收到消息就断开连接(毫秒) */ + get webSocketReceiveTime(): number { + return this.data.webSocketReceiveTime; + } + /** WebSocket 重连间隔时间(毫秒) */ + get webSocketReconnetTimeOut(): number { + return this.data.webSocketReconnetTimeOut; } /** 获取当前客户端支持的语言类型 */ @@ -55,42 +88,31 @@ export class GameConfig { return this._data.language.default || "zh"; } - /** 是否启用远程资源 */ - get bundleEnable(): string { - return this._data.bundle.enable; - } - /** 远程资源服务器地址 */ - get bundleServer(): string { - return this._data.bundle.server; - } /** 远程资源名 */ get bundleDefault(): string { return this._data.bundle.default; } - /** 远程所有资源包配置 */ - get bundlePackages(): string { - return this._data.bundle.packages; - } - - /** 加载界面资源超时提示 */ - get loadingTimeoutGui(): number { - return this._data.config.loadingTimeoutGui || 1000; - } - /** 是否开启移动设备安全区域适配 */ - get mobileSafeArea(): boolean { - return this._data.config.mobileSafeArea || false; - } - - private readonly _data: any = null; + private _data: any = null; /** 游戏配置数据 */ get data(): any { - return this._data; + return this._data.config[this._configType]; } + /** 当前游戏配置分组类型 */ + private _configType: GameConfigCustomType = GameConfigCustomType.Prod; + constructor(config: any) { this._data = Object.freeze(config.json); - + this.setConfigType(this._data.type); oops.log.logConfig(this._data, "游戏配置"); } + + /** + * 设置游戏参数类型 + * @param type + */ + setConfigType(type: GameConfigCustomType) { + this._configType = type; + } } \ No newline at end of file