From 5fd4a83224f1f4c58fb345192340a5180930e8bb Mon Sep 17 00:00:00 2001 From: dgflash Date: Tue, 19 Aug 2025 16:59:15 +0800 Subject: [PATCH 01/20] =?UTF-8?q?=E4=BC=98=E5=8C=96LayerUIElement=E3=80=81?= =?UTF-8?q?ModuleUtil=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/core/gui/layer/LayerUI.ts | 1 + assets/core/gui/layer/LayerUIElement.ts | 15 +++++-------- assets/module/common/ModuleUtil.ts | 28 ++++++------------------- 3 files changed, 12 insertions(+), 32 deletions(-) diff --git a/assets/core/gui/layer/LayerUI.ts b/assets/core/gui/layer/LayerUI.ts index 432a1c0..87f3dde 100644 --- a/assets/core/gui/layer/LayerUI.ts +++ b/assets/core/gui/layer/LayerUI.ts @@ -81,6 +81,7 @@ export class LayerUI extends Node { // 窗口事件委托 const dc = vp.node.addComponent(LayerUIElement); dc.params = vp; + //@ts-ignore dc.onCloseWindow = this.onCloseWindow.bind(this); // 显示界面 diff --git a/assets/core/gui/layer/LayerUIElement.ts b/assets/core/gui/layer/LayerUIElement.ts index 142a5d4..8c8c6b5 100644 --- a/assets/core/gui/layer/LayerUIElement.ts +++ b/assets/core/gui/layer/LayerUIElement.ts @@ -22,7 +22,7 @@ export class LayerUIElement extends Component { /** 关闭窗口之前 */ onCloseWindowBefore: Function = null!; /** 界面关闭回调 - 包括关闭动画播放完(辅助框架内存业务流程使用) */ - onCloseWindow: Function = null!; + private onCloseWindow: Function = null!; /** 窗口添加 */ add(): Promise { @@ -56,25 +56,20 @@ export class LayerUIElement extends Component { // 通知外部对象窗口组件上移除之前的事件(关闭窗口前的关闭动画处理) if (typeof this.params.callbacks.onBeforeRemove === "function") { - this.params.callbacks.onBeforeRemove( - this.node, - this.onBeforeRemoveNext.bind(this, isDestroy)); + this.params.callbacks.onBeforeRemove(this.node, this.onBeforeRemoveNext.bind(this, isDestroy)); } else { - this.removed(this.params, isDestroy); + this.onBeforeRemoveNext(isDestroy); } } else { - this.removed(this.params, isDestroy); + this.onBeforeRemoveNext(isDestroy); } } /** 窗口关闭前动画处理完后的回调方法,主要用于释放资源 */ private onBeforeRemoveNext(isDestroy?: boolean) { - if (this.onCloseWindowBefore) { - this.onCloseWindowBefore(); - this.onCloseWindowBefore = null!; - } + this.onCloseWindowBefore && this.onCloseWindowBefore(); this.removed(this.params, isDestroy); } diff --git a/assets/module/common/ModuleUtil.ts b/assets/module/common/ModuleUtil.ts index 5682187..d97867f 100644 --- a/assets/module/common/ModuleUtil.ts +++ b/assets/module/common/ModuleUtil.ts @@ -1,8 +1,8 @@ import { Node, __private } from "cc"; import { oops } from "../../core/Oops"; import { resLoader } from "../../core/common/loader/ResLoader"; -import { LayerUIElement, UICallbacks } from "../../core/gui/layer/LayerUIElement"; import { Uiid } from "../../core/gui/layer/LayerEnum"; +import { LayerUIElement, UICallbacks } from "../../core/gui/layer/LayerUIElement"; import { UIConfig } from "../../core/gui/layer/UIConfig"; import { ViewUtil } from "../../core/utils/ViewUtil"; import { ecs } from "../../libs/ecs/ECS"; @@ -98,28 +98,12 @@ export class ModuleUtil { const comp = node.getComponent(LayerUIElement); if (comp) { - if (comp.params.callbacks.onBeforeRemove) { - comp.onCloseWindowBefore = () => { - ent.remove(ctor, isDestroy); - if (onRemoved) onRemoved(); - }; - } - else if (comp.params.callbacks.onRemoved) { - comp.onCloseWindow = () => { - ent.remove(ctor, isDestroy); - if (onRemoved) onRemoved(); - }; - } - else { - ent.remove(ctor, isDestroy); + comp.onCloseWindowBefore = () => { + // 移除ECS显示组件 + if (isDestroy) ent.remove(ctor, isDestroy); if (onRemoved) onRemoved(); - } - } - else { - ent.remove(ctor, isDestroy); - if (onRemoved) onRemoved(); + }; + oops.gui.remove(uiId, isDestroy); } - - oops.gui.remove(uiId, isDestroy); } } \ No newline at end of file -- Gitee From 876e58096a8bb87797c7518ceac7e36fd26611d7 Mon Sep 17 00:00:00 2001 From: dgflash Date: Tue, 19 Aug 2025 18:30:34 +0800 Subject: [PATCH 02/20] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/core/gui/layer/LayerUI.ts | 25 +++++++++++++------------ assets/module/common/ModuleUtil.ts | 22 +++++----------------- 2 files changed, 18 insertions(+), 29 deletions(-) diff --git a/assets/core/gui/layer/LayerUI.ts b/assets/core/gui/layer/LayerUI.ts index 87f3dde..fc5fb57 100644 --- a/assets/core/gui/layer/LayerUI.ts +++ b/assets/core/gui/layer/LayerUI.ts @@ -59,37 +59,38 @@ export class LayerUI extends Node { /** * 加载界面资源 - * @param vp 显示参数 + * @param uip 显示参数 * @param bundle 远程资源包名,如果为空就是默认本地资源包 */ - protected async load(vp: UIParams, bundle?: string) { + protected async load(uip: UIParams, bundle?: string) { // 加载界面资源超时提示 const timerId = setTimeout(this.onLoadingTimeoutGui, oops.config.game.loadingTimeoutGui); - if (vp && vp.node) { - await this.showUi(vp); + if (uip && uip.node) { + await this.showUi(uip); } else { // 优先加载配置的指定资源包中资源,如果没配置则加载默认资源包资源 bundle = bundle || oops.res.defaultBundleName; - const res = await oops.res.loadAsync(bundle, vp.config.prefab, Prefab); + const res = await oops.res.loadAsync(bundle, uip.config.prefab, Prefab); if (res) { - vp.node = instantiate(res); + uip.node = instantiate(res); + // 是否启动真机安全区域显示 - if (vp.config.safeArea) vp.node.addComponent(SafeArea); + if (uip.config.safeArea) uip.node.addComponent(SafeArea); // 窗口事件委托 - const dc = vp.node.addComponent(LayerUIElement); - dc.params = vp; + const dc = uip.node.addComponent(LayerUIElement); + dc.params = uip; //@ts-ignore dc.onCloseWindow = this.onCloseWindow.bind(this); // 显示界面 - await this.showUi(vp); + await this.showUi(uip); } else { - console.warn(`路径为【${vp.config.prefab}】的预制加载失败`); - this.failure(vp); + console.warn(`路径为【${uip.config.prefab}】的预制加载失败`); + this.failure(uip); } } diff --git a/assets/module/common/ModuleUtil.ts b/assets/module/common/ModuleUtil.ts index d97867f..7814aa1 100644 --- a/assets/module/common/ModuleUtil.ts +++ b/assets/module/common/ModuleUtil.ts @@ -3,13 +3,14 @@ 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 { UIConfig } from "../../core/gui/layer/UIConfig"; 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 { /** * 添加界面组件 @@ -18,11 +19,7 @@ export class ModuleUtil { * @param uiId 界面资源编号 * @param uiArgs 界面参数 */ - static addViewUi( - ent: ecs.Entity, - ctor: __private.__types_globals__Constructor | __private.__types_globals__AbstractedConstructor, - uiId: number, - uiArgs: any = null) { + 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; @@ -41,11 +38,7 @@ export class ModuleUtil { * @param uiArgs 界面参数 * @returns 界面节点 */ - static addViewUiAsync( - ent: ecs.Entity, - ctor: __private.__types_globals__Constructor | __private.__types_globals__AbstractedConstructor, - uiId: number | UIConfig, - uiArgs: any = null): Promise { + 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) => { @@ -69,12 +62,7 @@ export class ModuleUtil { * @param url 显示资源地址 * @param bundleName 资源包名称 */ - static addView( - ent: ecs.Entity, - ctor: __private.__types_globals__Constructor | __private.__types_globals__AbstractedConstructor, - parent: Node, - url: string, - bundleName: string = resLoader.defaultBundleName) { + 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); -- Gitee From 6936e09f89ba858e8375553186daeca226e1edf5 Mon Sep 17 00:00:00 2001 From: dgflash Date: Wed, 20 Aug 2025 09:26:11 +0800 Subject: [PATCH 03/20] =?UTF-8?q?=E4=BC=98=E5=8C=96GUI=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/core/gui/layer/LayerGame.ts | 12 ++----- assets/core/gui/layer/LayerHelper.ts | 20 +++++++++++ assets/core/gui/layer/LayerHelper.ts.meta | 9 +++++ assets/core/gui/layer/LayerManager.ts | 6 ++-- assets/core/gui/layer/LayerNotify.ts | 15 +++----- assets/core/gui/layer/LayerPopup.ts | 6 ++-- assets/core/gui/layer/LayerUI.ts | 44 ++++++++++------------- assets/core/gui/layer/LayerUIElement.ts | 4 +-- 8 files changed, 62 insertions(+), 54 deletions(-) create mode 100644 assets/core/gui/layer/LayerHelper.ts create mode 100644 assets/core/gui/layer/LayerHelper.ts.meta diff --git a/assets/core/gui/layer/LayerGame.ts b/assets/core/gui/layer/LayerGame.ts index c16ef28..0299ba4 100644 --- a/assets/core/gui/layer/LayerGame.ts +++ b/assets/core/gui/layer/LayerGame.ts @@ -4,11 +4,12 @@ * @LastEditors: dgflash * @LastEditTime: 2025-08-15 10:06:47 */ -import { Layers, Node, NodePool, Prefab, Vec3, warn, Widget } from "cc"; +import { Node, NodePool, Prefab, Vec3, warn } from "cc"; import { resLoader } from "../../common/loader/ResLoader"; import { ViewUtil } from "../../utils/ViewUtil"; import { LayerCustomType } from "./LayerEnum"; import { GameElementParams, LayerGameElement } from "./LayerGameElement"; +import { LayerHelper } from "./LayerHelper"; import { GameElementConfig } from "./UIConfig"; /* 二维游戏层 */ @@ -18,14 +19,7 @@ export class LayerGame extends Node { constructor() { super(LayerCustomType.Game); - - const widget: Widget = this.addComponent(Widget); - widget.isAlignLeft = widget.isAlignRight = widget.isAlignTop = widget.isAlignBottom = true; - widget.left = widget.right = widget.top = widget.bottom = 0; - widget.alignMode = 2; - widget.enabled = true; - - this.layer = Layers.Enum.UI_2D; + LayerHelper.setFullScreen(this); } /** diff --git a/assets/core/gui/layer/LayerHelper.ts b/assets/core/gui/layer/LayerHelper.ts new file mode 100644 index 0000000..05c71b5 --- /dev/null +++ b/assets/core/gui/layer/LayerHelper.ts @@ -0,0 +1,20 @@ +import { Layers } from "cc"; +import { Node, Widget } from "cc"; + +/** 界面层辅助工具 */ +export class LayerHelper { + /** + * 界面层全屏布局 + * @param node 全屏布局的节点 + */ + static setFullScreen(node: Node) { + const widget: Widget = node.addComponent(Widget); + widget.isAlignLeft = widget.isAlignRight = widget.isAlignTop = widget.isAlignBottom = true; + widget.left = widget.right = widget.top = widget.bottom = 0; + widget.alignMode = 2; + widget.enabled = true; + + node.layer = Layers.Enum.UI_2D; + return widget; + } +} \ No newline at end of file diff --git a/assets/core/gui/layer/LayerHelper.ts.meta b/assets/core/gui/layer/LayerHelper.ts.meta new file mode 100644 index 0000000..3f8102d --- /dev/null +++ b/assets/core/gui/layer/LayerHelper.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "2e65136e-fc22-466b-a076-16dfbb12b505", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/core/gui/layer/LayerManager.ts b/assets/core/gui/layer/LayerManager.ts index c3666ab..3d6e7a9 100644 --- a/assets/core/gui/layer/LayerManager.ts +++ b/assets/core/gui/layer/LayerManager.ts @@ -1,14 +1,14 @@ import { Camera, Layers, Node, ResolutionPolicy, SafeArea, Widget, screen, view, warn } from "cc"; import { resLoader } from "../../common/loader/ResLoader"; import { oops } from "../../Oops"; -import { LayerUIElement, UICallbacks } from "./LayerUIElement"; import { LayerDialog } from "./LayerDialog"; import { LayerCustomType, LayerTypeCls, UIConfigMap, Uiid } from "./LayerEnum"; -import { LayerGame } from "./LayerGame"; import { LayerNotify } from "./LayerNotify"; import { LayerPopUp } from "./LayerPopup"; import { LayerUI } from "./LayerUI"; +import { LayerUIElement, UICallbacks } from "./LayerUIElement"; import { UIConfig } from "./UIConfig"; +import { LayerGame } from "./LayerGame"; /** 界面层级管理器 */ export class LayerManager { @@ -372,7 +372,7 @@ export class LayerManager { * @example * oops.gui.clear(); */ - clear(isDestroy: boolean = false) { + clear(isDestroy: boolean = true) { this.uiLayers.forEach((layer: LayerUI) => { layer.clear(isDestroy); }) diff --git a/assets/core/gui/layer/LayerNotify.ts b/assets/core/gui/layer/LayerNotify.ts index e57c7e2..3058597 100644 --- a/assets/core/gui/layer/LayerNotify.ts +++ b/assets/core/gui/layer/LayerNotify.ts @@ -4,15 +4,14 @@ * @LastEditors: dgflash * @LastEditTime: 2022-09-02 13:44:12 */ -import { BlockInputEvents, Layers, Node, Widget, instantiate } from "cc"; +import { BlockInputEvents, Node, instantiate } from "cc"; import { EDITOR } from "cc/env"; import { ViewUtil } from "../../utils/ViewUtil"; import { PromptResType } from "../GuiEnum"; import { Notify } from "../prompt/Notify"; +import { LayerHelper } from "./LayerHelper"; -/* - * 滚动消息提示层 - */ +/* 滚动消息提示层 */ export class LayerNotify extends Node { private black!: BlockInputEvents; /** 等待提示资源 */ @@ -24,14 +23,8 @@ export class LayerNotify extends Node { constructor(name: string) { super(name); + LayerHelper.setFullScreen(this); - const widget: Widget = this.addComponent(Widget); - widget.isAlignLeft = widget.isAlignRight = widget.isAlignTop = widget.isAlignBottom = true; - widget.left = widget.right = widget.top = widget.bottom = 0; - widget.alignMode = 2; - widget.enabled = true; - - this.layer = Layers.Enum.UI_2D; this.black = this.addComponent(BlockInputEvents); this.black.enabled = false; } diff --git a/assets/core/gui/layer/LayerPopup.ts b/assets/core/gui/layer/LayerPopup.ts index aaa7ab4..c950327 100644 --- a/assets/core/gui/layer/LayerPopup.ts +++ b/assets/core/gui/layer/LayerPopup.ts @@ -3,13 +3,12 @@ * @LastEditors: dgflash * @LastEditTime: 2022-09-02 13:44:28 */ - -import { BlockInputEvents, EventTouch, Layers, Node } from "cc"; +import { BlockInputEvents, EventTouch, Node } from "cc"; import { ViewUtil } from "../../utils/ViewUtil"; import { PromptResType } from "../GuiEnum"; import { LayerUI } from "./LayerUI"; -import { UIConfig } from "./UIConfig"; import { UIParams } from "./LayerUIElement"; +import { UIConfig } from "./UIConfig"; /* 弹窗层,允许同时弹出多个窗口 */ export class LayerPopUp extends LayerUI { @@ -21,7 +20,6 @@ export class LayerPopUp extends LayerUI { constructor(name: string) { super(name); - this.layer = Layers.Enum.UI_2D; this.on(Node.EventType.CHILD_ADDED, this.onChildAdded, this); this.on(Node.EventType.CHILD_REMOVED, this.onChildRemoved, this); } diff --git a/assets/core/gui/layer/LayerUI.ts b/assets/core/gui/layer/LayerUI.ts index fc5fb57..ebf9446 100644 --- a/assets/core/gui/layer/LayerUI.ts +++ b/assets/core/gui/layer/LayerUI.ts @@ -1,7 +1,8 @@ -import { instantiate, Node, Prefab, SafeArea, Widget } from "cc"; +import { instantiate, Node, Prefab, SafeArea } from "cc"; import { Collection } from "db://oops-framework/libs/collection/Collection"; import { oops } from "../../Oops"; import { Uiid } from "./LayerEnum"; +import { LayerHelper } from "./LayerHelper"; import { LayerUIElement, UICallbacks, UIParams } from "./LayerUIElement"; import { UIConfig } from "./UIConfig"; @@ -20,12 +21,7 @@ export class LayerUI extends Node { */ constructor(name: string) { super(name); - - const widget: Widget = this.addComponent(Widget); - widget.isAlignLeft = widget.isAlignRight = widget.isAlignTop = widget.isAlignBottom = true; - widget.left = widget.right = widget.top = widget.bottom = 0; - widget.alignMode = 2; - widget.enabled = true; + LayerHelper.setFullScreen(this); } /** @@ -147,20 +143,20 @@ export class LayerUI extends Node { if (isDestroy !== undefined) release = isDestroy; // 界面移出舞台 - const vp = this.ui_nodes.get(prefabPath); - if (vp) { + const uip = this.ui_nodes.get(prefabPath); + if (uip) { // 优先使用参数中控制的释放条件,如果未传递参数则用配置中的释放条件,默认不缓存关闭的界面 if (release === undefined) { - release = vp.config.destroy !== undefined ? vp.config.destroy : true; + release = uip.config.destroy !== undefined ? uip.config.destroy : true; } // 不释放界面,缓存起来待下次使用 if (release === false) { - this.ui_cache.set(vp.config.prefab, vp); + this.ui_cache.set(uip.config.prefab, uip); } - const childNode = vp.node; - const comp = childNode.getComponent(LayerUIElement)!; + const node = uip.node; + const comp = node.getComponent(LayerUIElement)!; comp.remove(release); } @@ -174,12 +170,10 @@ export class LayerUI extends Node { if (vp) { this.onCloseWindow(vp); this.ui_cache.delete(prefabPath); - const childNode = vp.node; - const comp = childNode.getComponent(LayerUIElement)!; - if (comp) { - comp.remove(true); - } - childNode.destroy(); + const node = vp.node; + const comp = node.getComponent(LayerUIElement)!; + comp.remove(true); + node.destroy(); } } @@ -189,8 +183,7 @@ export class LayerUI extends Node { */ get(prefabPath: string): Node { const vp = this.ui_nodes.get(prefabPath); - if (vp) - return vp.node; + if (vp) return vp.node; return null!; } @@ -208,10 +201,11 @@ export class LayerUI extends Node { */ clear(isDestroy: boolean): void { // 清除所有显示的界面 - this.ui_nodes.forEach((value: UIParams, key: string) => { - this.remove(value.config.prefab, isDestroy); - value.valid = false; - }); + 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.ui_nodes.clear(); // 清除缓存中的界面 diff --git a/assets/core/gui/layer/LayerUIElement.ts b/assets/core/gui/layer/LayerUIElement.ts index 8c8c6b5..6c9c8c9 100644 --- a/assets/core/gui/layer/LayerUIElement.ts +++ b/assets/core/gui/layer/LayerUIElement.ts @@ -8,12 +8,12 @@ import { Component, Node, _decorator } from "cc"; import { oops } from "../../Oops"; import { UIConfig } from "./UIConfig"; -const { ccclass } = _decorator; - const EventOnAdded: string = "onAdded"; const EventOnBeforeRemove: string = "onBeforeRemove"; const EventOnRemoved: string = "onRemoved"; +const { ccclass } = _decorator; + /** 窗口元素组件 */ @ccclass('LayerUIElement') export class LayerUIElement extends Component { -- Gitee From 32562ae2d46228a3335ac35faf74e82ea5d4c2f1 Mon Sep 17 00:00:00 2001 From: dgflash Date: Wed, 20 Aug 2025 17:35:20 +0800 Subject: [PATCH 04/20] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=9F=B3=E6=95=88?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=E6=B1=A0=E5=91=BD=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/core/common/audio/AudioEffect.ts | 12 ++- assets/core/common/audio/AudioEffectPool.ts | 92 ++++++++++++--------- 2 files changed, 64 insertions(+), 40 deletions(-) diff --git a/assets/core/common/audio/AudioEffect.ts b/assets/core/common/audio/AudioEffect.ts index 1dfadbf..d7522e1 100644 --- a/assets/core/common/audio/AudioEffect.ts +++ b/assets/core/common/audio/AudioEffect.ts @@ -4,12 +4,22 @@ * @LastEditors: dgflash * @LastEditTime: 2022-09-02 10:22:36 */ +import { AudioClip } from 'cc'; import { AudioSource, _decorator } from 'cc'; +import { IAudioParams } from './IAudio'; const { ccclass } = _decorator; /** 游戏音效 */ @ccclass('AudioEffect') export class AudioEffect extends AudioSource { + /** 唯一编号 */ + key: string = null!; + /** 音效编号 */ + aeid: number = -1; + /** 音效果资源路径 */ + path: string | AudioClip = null! + /** 音效参数 */ + params?: IAudioParams; /** 背景音乐播放完成回调 */ onComplete: Function | null = null; @@ -18,6 +28,6 @@ export class AudioEffect extends AudioSource { } private onAudioEnded() { - this.onComplete && this.onComplete(); + this.onComplete && this.onComplete(this); } } \ No newline at end of file diff --git a/assets/core/common/audio/AudioEffectPool.ts b/assets/core/common/audio/AudioEffectPool.ts index 34e1a32..aea0fbc 100644 --- a/assets/core/common/audio/AudioEffectPool.ts +++ b/assets/core/common/audio/AudioEffectPool.ts @@ -48,46 +48,51 @@ export class AudioEffectPool { /** * 加载与播放音效 - * @param url 音效资源地址与音效资源 - * @param bundleName 资源包名 - * @param onPlayComplete 播放完成回调 + * @param path 音效资源地址与音效资源 + * @param params 音效附加参数 * @returns */ - async loadAndPlay(url: string | AudioClip, params?: IAudioParams): Promise { + async loadAndPlay(path: string | AudioClip, params?: IAudioParams): Promise { return new Promise(async (resolve, reject) => { if (!this.switch) return resolve(-1); - let bundleName = resLoader.defaultBundleName; - let loop = false; - let volume = this.volume; - let onPlayComplete: Function = null!; - if (params) { - if (params.bundle != null) bundleName = params.bundle; - if (params.loop != null) loop = params.loop; - if (params.volume != null) volume = params.volume; - if (params.onPlayComplete != null) onPlayComplete = params.onPlayComplete; + if (params == null) { + params = { + bundle: resLoader.defaultBundleName, + volume: this.volume, + loop: false, + } + } + else { + if (params.bundle == null) params.bundle = resLoader.defaultBundleName; + if (params.volume == null) params.volume = this.volume; + if (params.loop == null) params.loop = false; } + let bundle = params.bundle!; + let volume = params.volume!; + let loop = params.loop!; + // 创建音效资源 let clip: AudioClip; - if (url instanceof AudioClip) { - clip = url; + if (path instanceof AudioClip) { + clip = path; } else { - clip = resLoader.get(url, AudioClip, bundleName)!; + clip = resLoader.get(path, AudioClip, bundle)!; // 加载音效资源 if (clip == null) { - let urls = this.res.get(bundleName); + let urls = this.res.get(bundle); if (urls == null) { urls = []; - this.res.set(bundleName, urls); - urls.push(url); + this.res.set(bundle, urls); + urls.push(path); } - else if (urls.indexOf(url) == -1) { - urls.push(url); + else if (urls.indexOf(path) == -1) { + urls.push(path); } - clip = await resLoader.loadAsync(bundleName, url, AudioClip); + clip = await resLoader.loadAsync(bundle, path, AudioClip); } } @@ -97,35 +102,38 @@ export class AudioEffectPool { return; } - let aeid = this.getAeId(); - let key: string; - if (url instanceof AudioClip) { - key = url.uuid; - } - else { - key = `${bundleName}_${url}`; - } - key += "_" + aeid; - // 获取音效果播放器播放音乐 + let aeid: number = -1; + let key: string = null!; let ae: AudioEffect; let node: Node = null!; if (this.pool.size() == 0) { + aeid = this.getAeId(); + if (path instanceof AudioClip) { + key = path.uuid; + } + else { + key = `${bundle}_${path}`; + } + key += "_" + aeid; + node = new Node(); node.name = "AudioEffect"; node.parent = oops.audio.node; ae = node.addComponent(AudioEffect)!; - ae.onComplete = () => { - this.put(aeid, url, bundleName); // 播放完回收对象 - onPlayComplete && onPlayComplete(aeid, url, bundleName); - // console.log(`【音效】回收,池中剩余音效播放器【${this.pool.size()}】`); - }; + ae.key = key; + ae.aeid = aeid; + ae.path = path; + ae.params = params; + ae.onComplete = this.onAudioEffectPlayComplete.bind(this); } else { node = this.pool.get()!; + node.parent = oops.audio.node; ae = node.getComponent(AudioEffect)!; + aeid = ae.aeid; + key = ae.key; } - // 记录正在播放的音效播放器 this.effects.set(key, ae); @@ -134,11 +142,17 @@ export class AudioEffectPool { ae.volume = volume; ae.currentTime = 0; ae.play(); - resolve(aeid); }); } + private onAudioEffectPlayComplete(ae: AudioEffect) { + const bundle = ae.params!.bundle!; + this.put(ae.aeid, ae.path, bundle); // 播放完回收对象 + ae.params && ae.params.onPlayComplete && ae.params.onPlayComplete(ae.aeid, ae.path, bundle); + // console.log(`【音效】回收,池中剩余音效播放器【${this.pool.size()}】`); + } + /** * 回收音效播放器 * @param aeid 播放器编号 -- Gitee From 6f237104c162549d2b9cbcce021e5bb4b306dd62 Mon Sep 17 00:00:00 2001 From: dgflash Date: Wed, 20 Aug 2025 18:11:58 +0800 Subject: [PATCH 05/20] =?UTF-8?q?=E6=B7=BB=E5=8A=A0ButtonSimple=E3=80=81UI?= =?UTF-8?q?Button=E6=89=B9=E9=87=8F=E8=AE=BE=E7=BD=AE=E8=A7=A6=E6=91=B8?= =?UTF-8?q?=E9=9F=B3=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/libs/gui/button/ButtonSimple.ts | 23 ++++++++++------------- assets/libs/gui/button/UIButton.ts | 11 ++++++++++- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/assets/libs/gui/button/ButtonSimple.ts b/assets/libs/gui/button/ButtonSimple.ts index 6710454..b66216f 100644 --- a/assets/libs/gui/button/ButtonSimple.ts +++ b/assets/libs/gui/button/ButtonSimple.ts @@ -1,6 +1,5 @@ import { AudioClip, Component, EventTouch, Node, _decorator, game } from "cc"; import { oops } from "../../../core/Oops"; -import { resLoader } from "../../../core/common/loader/ResLoader"; const { ccclass, property, menu } = _decorator; @@ -23,10 +22,15 @@ export default class ButtonSimple extends Component { type: AudioClip }) private effect: AudioClip = null!; - // private effectIds: number[] = []; private touchCount = 0; private touchtEndTime = 0; + private static effectPath: string = null!; + /** 批量设置触摸音效 */ + static setBatchEffect(path: string) { + this.effectPath = path; + } + onLoad() { this.node.on(Node.EventType.TOUCH_END, this.onTouchEnd, this); this.node.on(Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this); @@ -56,23 +60,16 @@ export default class ButtonSimple extends Component { /** 短按触摸音效 */ protected async playEffect() { - if (this.effect) { + if (ButtonSimple.effectPath) { + oops.audio.playEffect(ButtonSimple.effectPath); + } + else if (this.effect) { oops.audio.playEffect(this.effect); - // const effectId = await oops.audio.playEffect(this.effect, resLoader.defaultBundleName, () => { - // this.effectIds.remove(effectId); - // }); - // if (effectId > 0) this.effectIds.push(effectId); } } onDestroy() { this.node.off(Node.EventType.TOUCH_END, this.onTouchEnd, this); this.node.off(Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this); - - // if (this.effect) { - // this.effectIds.forEach(effectId => { - // oops.audio.putEffect(effectId, this.effect); - // }); - // } } } diff --git a/assets/libs/gui/button/UIButton.ts b/assets/libs/gui/button/UIButton.ts index 00180da..9ec26c5 100644 --- a/assets/libs/gui/button/UIButton.ts +++ b/assets/libs/gui/button/UIButton.ts @@ -32,6 +32,12 @@ export default class UIButton extends Button { /** 触摸结束时间 */ private _touchEndTime = 0; + private static effectPath: string = null!; + /** 批量设置触摸音效 */ + static setBatchEffect(path: string) { + this.effectPath = path; + } + /** 触摸结束 */ protected _onTouchEnded(event: EventTouch) { if (!this._interactable || !this.enabledInHierarchy) { @@ -74,7 +80,10 @@ export default class UIButton extends Button { /** 短按触摸音效 */ protected playEffect() { - if (this.effect) { + if (UIButton.effectPath) { + oops.audio.playEffect(UIButton.effectPath); + } + else if (this.effect) { oops.audio.playEffect(this.effect); } } -- Gitee From c8573f730ab11f8749e853706cbf327c37528447 Mon Sep 17 00:00:00 2001 From: dgflash Date: Wed, 20 Aug 2025 18:29:11 +0800 Subject: [PATCH 06/20] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/core/common/audio/AudioEffectPool.ts | 28 +++++++-------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/assets/core/common/audio/AudioEffectPool.ts b/assets/core/common/audio/AudioEffectPool.ts index aea0fbc..d78b5bf 100644 --- a/assets/core/common/audio/AudioEffectPool.ts +++ b/assets/core/common/audio/AudioEffectPool.ts @@ -117,14 +117,15 @@ export class AudioEffectPool { } key += "_" + aeid; - node = new Node(); - node.name = "AudioEffect"; + node = new Node("AudioEffect"); node.parent = oops.audio.node; - ae = node.addComponent(AudioEffect)!; + + ae = node.addComponent(AudioEffect); ae.key = key; ae.aeid = aeid; ae.path = path; ae.params = params; + ae.clip = clip; ae.onComplete = this.onAudioEffectPlayComplete.bind(this); } else { @@ -138,9 +139,7 @@ export class AudioEffectPool { this.effects.set(key, ae); ae.loop = loop; - ae.clip = clip; ae.volume = volume; - ae.currentTime = 0; ae.play(); resolve(aeid); }); @@ -150,7 +149,7 @@ export class AudioEffectPool { const bundle = ae.params!.bundle!; this.put(ae.aeid, ae.path, bundle); // 播放完回收对象 ae.params && ae.params.onPlayComplete && ae.params.onPlayComplete(ae.aeid, ae.path, bundle); - // console.log(`【音效】回收,池中剩余音效播放器【${this.pool.size()}】`); + console.log(`【音效】回收,池中剩余音效播放器【${this.pool.size()}】`); } /** @@ -179,35 +178,26 @@ export class AudioEffectPool { /** 停止播放所有音效 */ stop() { - this.effects.forEach(ae => { - ae.stop(); - }); + this.effects.forEach(ae => ae.stop()); } /** 恢复所有音效 */ play() { if (!this.switch) return; - - this.effects.forEach(ae => { - ae.play(); - }); + this.effects.forEach(ae => ae.play()); } /** 暂停所有音效 */ pause() { if (!this.switch) return; - this.effects.forEach(ae => { - ae.pause(); - }); + this.effects.forEach(ae => ae.pause()); } /** 释放所有音效资源与对象池中播放器 */ release() { // 释放正在播放的音效 - this.effects.forEach(ae => { - ae.node.destroy(); - }); + this.effects.forEach(ae => ae.node.destroy()); this.effects.clear(); // 释放音效资源 -- Gitee From b0f5252b507d8aafda71cb9889bc9a6b722d1b91 Mon Sep 17 00:00:00 2001 From: dgflash Date: Thu, 21 Aug 2025 10:55:54 +0800 Subject: [PATCH 07/20] =?UTF-8?q?oops.res.loadBundle=E6=B7=BB=E5=8A=A0opti?= =?UTF-8?q?ons=E8=B5=84=E6=BA=90=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/core/common/loader/ResLoader.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/assets/core/common/loader/ResLoader.ts b/assets/core/common/loader/ResLoader.ts index 85098b1..7c6e8db 100644 --- a/assets/core/common/loader/ResLoader.ts +++ b/assets/core/common/loader/ResLoader.ts @@ -117,12 +117,13 @@ oops.res.loadRemote(this.url, opt, onComplete); /** * 加载资源包 * @param name 资源地址 + * @param options 资源参数,例:{ version: "74fbe" } * @example - await oops.res.loadBundle(name); + await oops.res.loadBundle(name, options); */ - loadBundle(name: string): Promise { + loadBundle(name: string, options: { [k: string]: any; version?: string; } | null = null): Promise { return new Promise((resolve, reject) => { - assetManager.loadBundle(name, (err, bundle: AssetManager.Bundle) => { + assetManager.loadBundle(name, options, (err, bundle: AssetManager.Bundle) => { if (err) { resolve(null!); return; -- Gitee From 31604de25c669ee312f3ecc288e1103a6f72dfc5 Mon Sep 17 00:00:00 2001 From: dgflash Date: Fri, 22 Aug 2025 13:53:41 +0800 Subject: [PATCH 08/20] =?UTF-8?q?=E9=9F=B3=E4=B9=90=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=A4=96=E7=BD=91=E8=BF=9C=E7=A8=8B=E9=9F=B3?= =?UTF-8?q?=E4=B9=90=E6=92=AD=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/core/common/audio/AudioEffectPool.ts | 95 ++++++++++++------- assets/core/common/audio/AudioMusic.ts | 65 +++++++------ assets/core/common/loader/ResLoader.ts | 47 +++++---- .../libs/animator-effect/EffectSingleCase.ts | 6 +- 4 files changed, 124 insertions(+), 89 deletions(-) diff --git a/assets/core/common/audio/AudioEffectPool.ts b/assets/core/common/audio/AudioEffectPool.ts index d78b5bf..e27d897 100644 --- a/assets/core/common/audio/AudioEffectPool.ts +++ b/assets/core/common/audio/AudioEffectPool.ts @@ -4,6 +4,7 @@ import { resLoader } from "../loader/ResLoader"; import { AudioEffect } from "./AudioEffect"; import { IAudioParams } from "./IAudio"; +/** 音乐效缓冲编号最大值 */ const AE_ID_MAX = 30000; /** 音效池 */ @@ -31,12 +32,14 @@ export class AudioEffectPool { }); } - /** 音效播放器对象池 */ + /** 音效播放器节点对象池 */ private pool: NodePool = new NodePool(); /** 对象池集合 */ private effects: Map = new Map(); /** 用过的音效资源记录 */ private res: Map = new Map(); + /** 外网远程资源记录(地址、音效对象) */ + private res_remote: Map = new Map(); private _aeId: number = 0; /** 获取请求唯一编号 */ @@ -70,15 +73,27 @@ export class AudioEffectPool { } let bundle = params.bundle!; - let volume = params.volume!; - let loop = params.loop!; - // 创建音效资源 - let clip: AudioClip; + let key: string = null!; + let clip: AudioClip | undefined; + // 通过预制自动加载的音效资源(音效内存跟随预制体的内存一并释放) if (path instanceof AudioClip) { clip = path; + key = path.uuid; } + // 非引擎管理的远程资源加载 + else if (path.indexOf("http") == 0) { + key = path; + clip = this.res_remote.get(path); + if (clip == null) { + const extension = path.split('.').pop(); + clip = await resLoader.loadRemote(path, { ext: `.${extension}` }); + this.res_remote.set(path, clip); + } + } + // 资源加载 else { + key = `${bundle}_${path}`; clip = resLoader.get(path, AudioClip, bundle)!; // 加载音效资源 @@ -104,42 +119,35 @@ export class AudioEffectPool { // 获取音效果播放器播放音乐 let aeid: number = -1; - let key: string = null!; let ae: AudioEffect; let node: Node = null!; if (this.pool.size() == 0) { aeid = this.getAeId(); - if (path instanceof AudioClip) { - key = path.uuid; - } - else { - key = `${bundle}_${path}`; - } key += "_" + aeid; node = new Node("AudioEffect"); - node.parent = oops.audio.node; ae = node.addComponent(AudioEffect); ae.key = key; ae.aeid = aeid; ae.path = path; ae.params = params; - ae.clip = clip; ae.onComplete = this.onAudioEffectPlayComplete.bind(this); } else { node = this.pool.get()!; - node.parent = oops.audio.node; ae = node.getComponent(AudioEffect)!; - aeid = ae.aeid; - key = ae.key; + ae.key = ae.key; + ae.aeid = ae.aeid; } + // 记录正在播放的音效播放器 this.effects.set(key, ae); - ae.loop = loop; - ae.volume = volume; + node.parent = oops.audio.node; + ae.loop = params.loop!; + ae.volume = params.volume!; + ae.clip = clip; ae.play(); resolve(aeid); }); @@ -149,22 +157,25 @@ export class AudioEffectPool { const bundle = ae.params!.bundle!; this.put(ae.aeid, ae.path, bundle); // 播放完回收对象 ae.params && ae.params.onPlayComplete && ae.params.onPlayComplete(ae.aeid, ae.path, bundle); - console.log(`【音效】回收,池中剩余音效播放器【${this.pool.size()}】`); + // console.log(`【音效】回收,池中剩余音效播放器【${this.pool.size()}】`); } /** * 回收音效播放器 * @param aeid 播放器编号 - * @param url 音效路径 + * @param path 音效路径 * @param bundleName 资源包名 */ - put(aeid: number, url: string | AudioClip, bundleName: string = resLoader.defaultBundleName) { + put(aeid: number, path: string | AudioClip, bundleName: string = resLoader.defaultBundleName) { let key: string; - if (url instanceof AudioClip) { - key = url.uuid; + if (path instanceof AudioClip) { + key = path.uuid; + } + else if (path.indexOf("http") == 0) { + key = path; } else { - key = `${bundleName}_${url}`; + key = `${bundleName}_${path}`; } key += "_" + aeid; @@ -190,22 +201,42 @@ export class AudioEffectPool { /** 暂停所有音效 */ pause() { if (!this.switch) return; - this.effects.forEach(ae => ae.pause()); } /** 释放所有音效资源与对象池中播放器 */ release() { - // 释放正在播放的音效 + // 释放池中音乐播放器 + this.releasePool(); + + // 释放各个资源包中的音效资源 + this.releaseRes(); + + // 释放外网远程音效资源 + this.releaseResRemote(); + } + + /** 释放池中音乐播放器 */ + releasePool() { + this.pool.clear(); + + // 释放正在播放的音效对象 this.effects.forEach(ae => ae.node.destroy()); this.effects.clear(); + } - // 释放音效资源 - this.res.forEach((urls: string[], bundleName: string) => { - urls.forEach(url => resLoader.release(url, bundleName)); + /** 释放各个资源包中的音效资源 */ + releaseRes() { + this.res.forEach((paths: string[], bundleName: string) => { + paths.forEach(path => resLoader.release(path, bundleName)); }); + } - // 释放池中播放器 - this.pool.clear(); + /** 释放外网远程音效资源 */ + releaseResRemote() { + this.res_remote.forEach((clip: AudioClip, path: string) => { + clip.decRef(); + }); + this.res_remote.clear(); } } \ No newline at end of file diff --git a/assets/core/common/audio/AudioMusic.ts b/assets/core/common/audio/AudioMusic.ts index 0f30441..a0ed7e0 100644 --- a/assets/core/common/audio/AudioMusic.ts +++ b/assets/core/common/audio/AudioMusic.ts @@ -62,15 +62,15 @@ export class AudioMusic extends AudioSource { /** * 加载音乐并播放 - * @param url 音乐资源地址 + * @param path 音乐资源地址 * @param params 背景音乐资源播放参数 */ - async loadAndPlay(url: string, params?: IAudioParams) { + async loadAndPlay(path: string, params?: IAudioParams) { if (!this.switch) return; // 禁止播放音乐 // 下一个加载的背景音乐资源 if (this._isLoading) { - this._nextUrl = url; + this._nextUrl = path; this._nextParams = params!; return; } @@ -88,33 +88,40 @@ export class AudioMusic extends AudioSource { }; this._isLoading = true; - var clip: AudioClip = await resLoader.loadAsync(bundleName, url, AudioClip); - if (clip) { - this._isLoading = false; - - // 处理等待加载的背景音乐 - if (this._nextUrl != null) { - // 加载等待播放的背景音乐 - this.loadAndPlay(this._nextUrl, this._nextParams); - this._nextUrl = null!; - this._nextParams = null!; - } - else { - // 正在播放的时候先关闭 - if (this.playing) { - this.stop(); - } - - // 删除当前正在播放的音乐 - this.release(); - - // 播放背景音乐 - this.clip = clip; - this.loop = loop; - this.volume = volume; - this.currentTime = 0; - this.play(); + + let clip: AudioClip = null!; + if (path.indexOf("http") == 0) { + const extension = path.split('.').pop(); + clip = await resLoader.loadRemote(path, { ext: `.${extension}` }); + } + else { + clip = await resLoader.loadAsync(bundleName, path, AudioClip); + } + + this._isLoading = false; + + // 处理等待加载的背景音乐 + if (this._nextUrl != null) { + // 加载等待播放的背景音乐 + this.loadAndPlay(this._nextUrl, this._nextParams); + this._nextUrl = null!; + this._nextParams = null!; + } + else { + // 正在播放的时候先关闭 + if (this.playing) { + this.stop(); } + + // 删除当前正在播放的音乐 + this.release(); + + // 播放背景音乐 + this.clip = clip; + this.loop = loop; + this.volume = volume; + this.currentTime = 0; + this.play(); } } diff --git a/assets/core/common/loader/ResLoader.ts b/assets/core/common/loader/ResLoader.ts index 7c6e8db..ad2c911 100644 --- a/assets/core/common/loader/ResLoader.ts +++ b/assets/core/common/loader/ResLoader.ts @@ -75,32 +75,29 @@ export class ResLoader { * @param options 资源参数,例:{ ext: ".png" } * @param onComplete 加载完成回调 * @example -var opt: IRemoteOptions = { ext: ".png" }; -var onComplete = (err: Error | null, data: ImageAsset) => { - const texture = new Texture2D(); - texture.image = data; - - const spriteFrame = new SpriteFrame(); - spriteFrame.texture = texture; - - var sprite = this.sprite.addComponent(Sprite); - sprite.spriteFrame = spriteFrame; -} -oops.res.loadRemote(this.url, opt, onComplete); + var opt: IRemoteOptions = { ext: ".png" }; + var data = await oops.res.loadRemote(this.url, opt); + const texture = new Texture2D(); + texture.image = data; + + const spriteFrame = new SpriteFrame(); + spriteFrame.texture = texture; + + var sprite = this.sprite.addComponent(Sprite); + sprite.spriteFrame = spriteFrame; */ - loadRemote(url: string, options: IRemoteOptions | null, onComplete?: CompleteCallback): void; - loadRemote(url: string, onComplete?: CompleteCallback): void; - loadRemote(url: string, ...args: any): void { - let options: IRemoteOptions | null = null; - let onComplete: CompleteCallback = null; - if (args.length == 2) { - options = args[0]; - onComplete = args[1]; - } - else { - onComplete = args[0]; - } - assetManager.loadRemote(url, options, onComplete); + loadRemote(url: string, options: IRemoteOptions | null): Promise; + loadRemote(url: string): Promise { + return new Promise((resolve, reject) => { + let options: IRemoteOptions | null = null; + assetManager.loadRemote(url, options, (err, data: T) => { + if (err) { + reject(null); + return; + } + resolve(data); + }); + }); } //#endregion diff --git a/assets/libs/animator-effect/EffectSingleCase.ts b/assets/libs/animator-effect/EffectSingleCase.ts index 4121288..6f3cf47 100644 --- a/assets/libs/animator-effect/EffectSingleCase.ts +++ b/assets/libs/animator-effect/EffectSingleCase.ts @@ -183,9 +183,9 @@ export class EffectSingleCase { */ put(node: Node) { //@ts-ignore - let name = node.res_path; + const name = node.res_path; if (name) { - let np = this.effects.get(name); + const np = this.effects.get(name); if (np) { // 回收使用的节点 this.effects_use.delete(node); @@ -202,7 +202,7 @@ export class EffectSingleCase { */ clear(path?: string) { if (path) { - var np = this.effects.get(path); + const np = this.effects.get(path); if (np) np.clear(); } else { -- Gitee From 6d61b845ee6495118112fb14abea07dc97da18b0 Mon Sep 17 00:00:00 2001 From: dgflash Date: Fri, 22 Aug 2025 16:16:37 +0800 Subject: [PATCH 09/20] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/core/common/loader/ResLoader.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/assets/core/common/loader/ResLoader.ts b/assets/core/common/loader/ResLoader.ts index ad2c911..3ec738a 100644 --- a/assets/core/common/loader/ResLoader.ts +++ b/assets/core/common/loader/ResLoader.ts @@ -73,7 +73,6 @@ export class ResLoader { * 加载远程资源 * @param url 资源地址 * @param options 资源参数,例:{ ext: ".png" } - * @param onComplete 加载完成回调 * @example var opt: IRemoteOptions = { ext: ".png" }; var data = await oops.res.loadRemote(this.url, opt); @@ -86,8 +85,7 @@ export class ResLoader { var sprite = this.sprite.addComponent(Sprite); sprite.spriteFrame = spriteFrame; */ - loadRemote(url: string, options: IRemoteOptions | null): Promise; - loadRemote(url: string): Promise { + loadRemote(url: string, options: IRemoteOptions | null = null): Promise { return new Promise((resolve, reject) => { let options: IRemoteOptions | null = null; assetManager.loadRemote(url, options, (err, data: T) => { -- Gitee From a89eba46a80abe7aca0ed0e4f30d55becb04966d Mon Sep 17 00:00:00 2001 From: dgflash Date: Fri, 22 Aug 2025 19:07:46 +0800 Subject: [PATCH 10/20] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/core/common/loader/ResLoader.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/assets/core/common/loader/ResLoader.ts b/assets/core/common/loader/ResLoader.ts index 3ec738a..5202b6a 100644 --- a/assets/core/common/loader/ResLoader.ts +++ b/assets/core/common/loader/ResLoader.ts @@ -87,7 +87,6 @@ export class ResLoader { */ loadRemote(url: string, options: IRemoteOptions | null = null): Promise { return new Promise((resolve, reject) => { - let options: IRemoteOptions | null = null; assetManager.loadRemote(url, options, (err, data: T) => { if (err) { reject(null); -- Gitee From 5bd45653bf5ff40cd4f8e66b6f0a84fc25044bf3 Mon Sep 17 00:00:00 2001 From: dgflash Date: Wed, 27 Aug 2025 17:17:34 +0800 Subject: [PATCH 11/20] =?UTF-8?q?=E9=9F=B3=E6=95=88=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=89=A9=E5=B1=95=E5=88=86=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/core/common/audio/AudioEffectPool.ts | 114 ++++++++++++++------ assets/core/common/audio/AudioManager.ts | 64 +++++++---- assets/core/common/audio/AudioMusic.ts | 52 +++++++-- assets/core/common/audio/IAudio.ts | 9 ++ 4 files changed, 179 insertions(+), 60 deletions(-) diff --git a/assets/core/common/audio/AudioEffectPool.ts b/assets/core/common/audio/AudioEffectPool.ts index e27d897..10dfe66 100644 --- a/assets/core/common/audio/AudioEffectPool.ts +++ b/assets/core/common/audio/AudioEffectPool.ts @@ -2,36 +2,16 @@ import { AudioClip, Node, NodePool } from "cc"; import { oops } from "../../Oops"; import { resLoader } from "../loader/ResLoader"; import { AudioEffect } from "./AudioEffect"; -import { IAudioParams } from "./IAudio"; +import { AudioEffectType } from "./AudioManager"; +import { IAudioData, IAudioParams } from "./IAudio"; /** 音乐效缓冲编号最大值 */ const AE_ID_MAX = 30000; /** 音效池 */ export class AudioEffectPool { - private _switch: boolean = true; - /** 音效开关 */ - get switch(): boolean { - return this._switch; - } - set switch(value: boolean) { - this._switch = value; - if (value) this.stop(); - } - - private _volume: number = 1; - /** 所有音效音量 */ - get volume(): number { - return this._volume; - } - set volume(value: number) { - this._volume = value; - - this.effects.forEach(ae => { - ae.volume = value; - }); - } - + /** 音效配置数据 */ + private data: { [node: string]: IAudioData } = null!; /** 音效播放器节点对象池 */ private pool: NodePool = new NodePool(); /** 对象池集合 */ @@ -49,6 +29,61 @@ export class AudioEffectPool { return this._aeId; } + /** + * 注册音效类型 + * @param type + */ + register(type: string) { + this.data[type] = { switch: true, volume: 1 }; + } + + /** + * 音效开关 + * @param type 音效类型 + * @returns 音效开关 + */ + getSwitch(type: string) { + let iad = this.data[type]; + if (iad == null) console.error(`类型为【${type}】的音效配置不存在`); + return iad.switch; + } + /** + * 音效音量设置 + * @param type 音效类型 + * @param value 音效开关 + */ + setSwitch(type: string, value: boolean) { + let iad = this.data[type]; + if (iad == null) console.error(`类型为【${type}】的音效配置不存在`); + iad.switch = value; + + if (!value) this.stop(); + } + + /** + * 音效音量获取 + * @param type 音效类型 + * @returns 音效音量 + */ + getVolume(type: string) { + let iad = this.data[type]; + if (iad == null) console.error(`类型为【${type}】的音效配置不存在`); + return iad.volume; + } + + /** + * 音效音量设置 + * @param type 音效类型 + * @param value 音效音量 + */ + setVolume(type: string, value: number) { + let iad = this.data[type]; + if (iad == null) console.error(`类型为【${type}】的音效配置不存在`); + iad.volume = value; + + this.effects.forEach(ac => ac.volume = value); + } + /** * 加载与播放音效 * @param path 音效资源地址与音效资源 @@ -57,21 +92,26 @@ export class AudioEffectPool { */ async loadAndPlay(path: string | AudioClip, params?: IAudioParams): Promise { return new Promise(async (resolve, reject) => { - if (!this.switch) return resolve(-1); - if (params == null) { params = { bundle: resLoader.defaultBundleName, - volume: this.volume, loop: false, + type: AudioEffectType.Effect } } else { if (params.bundle == null) params.bundle = resLoader.defaultBundleName; - if (params.volume == null) params.volume = this.volume; if (params.loop == null) params.loop = false; + if (params.type == null) params.type = AudioEffectType.Effect; } + let iad = this.data[params.type!]; + if (iad == null) console.error(`类型为【${params.type!}】的音效配置不存在`); + + if (!iad.switch) return resolve(-1); + + if (params.volume == null) params.volume = iad.volume; + let bundle = params.bundle!; let key: string = null!; @@ -157,7 +197,7 @@ export class AudioEffectPool { const bundle = ae.params!.bundle!; this.put(ae.aeid, ae.path, bundle); // 播放完回收对象 ae.params && ae.params.onPlayComplete && ae.params.onPlayComplete(ae.aeid, ae.path, bundle); - // console.log(`【音效】回收,池中剩余音效播放器【${this.pool.size()}】`); + console.log(`【音效】回收,池中剩余音效播放器【${this.pool.size()}】`); } /** @@ -189,19 +229,27 @@ export class AudioEffectPool { /** 停止播放所有音效 */ stop() { - this.effects.forEach(ae => ae.stop()); + this.effects.forEach(ae => { + ae.stop(); + this.onAudioEffectPlayComplete(ae); + }); + this.effects.clear(); } /** 恢复所有音效 */ play() { - if (!this.switch) return; + // if (!this.switch) return; this.effects.forEach(ae => ae.play()); } /** 暂停所有音效 */ pause() { - if (!this.switch) return; - this.effects.forEach(ae => ae.pause()); + // if (!this.switch) return; + this.effects.forEach(ae => { + ae.pause(); + this.onAudioEffectPlayComplete(ae); + }); + this.effects.clear(); } /** 释放所有音效资源与对象池中播放器 */ diff --git a/assets/core/common/audio/AudioManager.ts b/assets/core/common/audio/AudioManager.ts index 3eee464..03edee2 100644 --- a/assets/core/common/audio/AudioManager.ts +++ b/assets/core/common/audio/AudioManager.ts @@ -2,10 +2,18 @@ import { AudioClip, Component } from "cc"; import { oops } from "../../Oops"; import { AudioEffectPool } from "./AudioEffectPool"; import { AudioMusic } from "./AudioMusic"; -import { IAudioParams } from "./IAudio"; +import { IAudioData, IAudioParams } from "./IAudio"; const LOCAL_STORE_KEY = "game_audio"; +/** 音乐音效默认类型 */ +export enum AudioEffectType { + /** 背景音乐 */ + Music = "music", + /** 音乐音效 */ + Effect = "effect", +} + /** * 音频管理 * @help https://gitee.com/dgflash/oops-framework/wikis/pages?sort_id=12037893&doc_id=2873565 @@ -20,7 +28,7 @@ export class AudioManager extends Component { effect: AudioEffectPool = new AudioEffectPool(); /** 音乐管理状态数据 */ - private localData: any = {}; + private data: { [node: string]: IAudioData } = {}; /** * 播放音效 @@ -38,13 +46,12 @@ export class AudioManager extends Component { /** 恢复当前暂停的音乐与音效播放 */ resumeAll() { - if (!this.music.playing && this.music.progress > 0) this.music.play(); - this.effect.play(); + this.music.resume(); } /** 暂停当前音乐与音效的播放 */ pauseAll() { - if (this.music.playing) this.music.pause(); + this.music.pause(); this.effect.pause(); } @@ -56,20 +63,15 @@ export class AudioManager extends Component { /** 保存音乐音效的音量、开关配置数据到本地 */ save() { - this.localData.volume_music = this.music.volume; - this.localData.volume_effect = this.effect.volume; - this.localData.switch_music = this.music.switch; - this.localData.switch_effect = this.effect.switch; - - oops.storage.set(LOCAL_STORE_KEY, this.localData); + oops.storage.set(LOCAL_STORE_KEY, this.data); } /** 本地加载音乐音效的音量、开关配置数据并设置到游戏中 */ load() { this.music = this.getComponent(AudioMusic) || this.addComponent(AudioMusic)!; - this.localData = oops.storage.getJson(LOCAL_STORE_KEY); - if (this.localData) { + this.data = oops.storage.getJson(LOCAL_STORE_KEY); + if (this.data) { try { this.setState(); } @@ -83,17 +85,37 @@ export class AudioManager extends Component { } private setState() { - this.music.volume = this.localData.volume_music; - this.effect.volume = this.localData.volume_effect; - this.music.switch = this.localData.switch_music; - this.effect.switch = this.localData.switch_effect; + for (let type in this.data) { + let iad = this.data[type]; + switch (type) { + case "music": + this.music.switch = this.data.music.switch; + this.music.volume = this.data.music.volume; + break; + default: + this.effect.setSwitch(type, iad.switch); + this.effect.setVolume(type, iad.volume); + break; + } + } } private setStateDefault() { - this.localData = {}; + this.data = { + music: { switch: true, volume: 1 }, + effect: { switch: true, volume: 1 }, + }; + + //@ts-ignore + this.music.data = this.data; + this.music.switch = true this.music.volume = 1; - this.effect.volume = 1; - this.music.switch = true; - this.effect.switch = true; + + //@ts-ignore + this.effect.data = this.data; + this.effect.setSwitch(AudioEffectType.Effect, true); + this.effect.setVolume(AudioEffectType.Effect, 1); + + this.save(); } } \ No newline at end of file diff --git a/assets/core/common/audio/AudioMusic.ts b/assets/core/common/audio/AudioMusic.ts index a0ed7e0..e12ae23 100644 --- a/assets/core/common/audio/AudioMusic.ts +++ b/assets/core/common/audio/AudioMusic.ts @@ -6,7 +6,8 @@ */ import { AudioClip, AudioSource, _decorator } from 'cc'; import { resLoader } from '../loader/ResLoader'; -import { IAudioParams } from './IAudio'; +import { AudioEffectType } from './AudioManager'; +import { IAudioData, IAudioParams } from './IAudio'; const { ccclass } = _decorator; @@ -16,22 +17,50 @@ const { ccclass } = _decorator; */ @ccclass('AudioMusic') export class AudioMusic extends AudioSource { + /** 音效配置数据 */ + private data: { [node: string]: IAudioData } = null!; + private _progress: number = 0; private _isLoading: boolean = false; private _nextUrl: string = null!; private _nextParams: IAudioParams = null!; private _params: IAudioParams = null!; - /** 背景音乐开关 */ - private _switch: boolean = true; - get switch(): boolean { - return this._switch; + /** + * 音效开关 + * @param type 音效类型 + * @returns 音效开关 + */ + get switch() { + return this.data[AudioEffectType.Music].switch; } + /** + * 音效音量设置 + * @param type 音效类型 + * @param value 音效开关 + */ set switch(value: boolean) { - this._switch = value; + this.data[AudioEffectType.Music].switch = value; if (!value) this.stop(); } + /** + * 音效音量获取 + * @param type 音效类型 + * @returns 音效音量 + */ + get volume() { + return this.data[AudioEffectType.Music].volume; + } + /** + * 音效音量设置 + * @param value 音效音量 + */ + set volume(value: number) { + this.data[AudioEffectType.Music].volume = value; + super.volume = value; + } + /** 获取音乐播放进度 */ get progress(): number { if (this.duration > 0) @@ -125,6 +154,17 @@ export class AudioMusic extends AudioSource { } } + /** 恢复当前暂停的音乐与音效播放 */ + resume() { + if (!this.playing && this.progress > 0) super.play(); + } + + /** 暂停当前音乐与音效的播放 */ + pause() { + if (this.playing) super.pause(); + } + + /** 停止当前音乐与音效的播放 */ stop(): void { if (this.switch && this.playing) { super.stop(); diff --git a/assets/core/common/audio/IAudio.ts b/assets/core/common/audio/IAudio.ts index 68b5ecf..fe59f42 100644 --- a/assets/core/common/audio/IAudio.ts +++ b/assets/core/common/audio/IAudio.ts @@ -1,4 +1,6 @@ export interface IAudioParams { + /** 音乐分类 */ + type?: string, /** 资源包名 */ bundle?: string, /** 是否循环播放 */ @@ -7,4 +9,11 @@ export interface IAudioParams { volume?: number; /** 播放完成事件 */ onPlayComplete?: Function; +} + +export interface IAudioData { + /** 音乐开关 */ + switch: boolean; + /** 音量 */ + volume: number; } \ No newline at end of file -- Gitee From 3e47b3daa617c736e655d907d302eebaf8910f35 Mon Sep 17 00:00:00 2001 From: dgflash Date: Thu, 28 Aug 2025 16:04:21 +0800 Subject: [PATCH 12/20] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=9F=B3=E4=B9=90?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E5=88=86=E7=B1=BB=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/core/common/audio/AudioEffectPool.ts | 72 ++++++++++----------- assets/core/common/audio/AudioManager.ts | 7 +- assets/module/common/GameComponent.ts | 67 +++++++++---------- 3 files changed, 67 insertions(+), 79 deletions(-) diff --git a/assets/core/common/audio/AudioEffectPool.ts b/assets/core/common/audio/AudioEffectPool.ts index 10dfe66..2a4979f 100644 --- a/assets/core/common/audio/AudioEffectPool.ts +++ b/assets/core/common/audio/AudioEffectPool.ts @@ -87,10 +87,10 @@ export class AudioEffectPool { /** * 加载与播放音效 * @param path 音效资源地址与音效资源 - * @param params 音效附加参数 + * @param params 音效附加参数 * @returns */ - async loadAndPlay(path: string | AudioClip, params?: IAudioParams): Promise { + async loadAndPlay(path: string | AudioClip, params?: IAudioParams): Promise { return new Promise(async (resolve, reject) => { if (params == null) { params = { @@ -102,28 +102,30 @@ export class AudioEffectPool { else { if (params.bundle == null) params.bundle = resLoader.defaultBundleName; if (params.loop == null) params.loop = false; - if (params.type == null) params.type = AudioEffectType.Effect; + if (params.type == null) params.type = AudioEffectType.Effect; // 默认音效类型 } let iad = this.data[params.type!]; if (iad == null) console.error(`类型为【${params.type!}】的音效配置不存在`); - if (!iad.switch) return resolve(-1); + if (!iad.switch) { + resolve(null!); + return; + } if (params.volume == null) params.volume = iad.volume; let bundle = params.bundle!; - let key: string = null!; let clip: AudioClip | undefined; // 通过预制自动加载的音效资源(音效内存跟随预制体的内存一并释放) if (path instanceof AudioClip) { + key = `${params.type}_${path.uuid}`; clip = path; - key = path.uuid; } // 非引擎管理的远程资源加载 else if (path.indexOf("http") == 0) { - key = path; + key = `${params.type}_${path}`; clip = this.res_remote.get(path); if (clip == null) { const extension = path.split('.').pop(); @@ -133,7 +135,7 @@ export class AudioEffectPool { } // 资源加载 else { - key = `${bundle}_${path}`; + key = `${params.type}_${bundle}_${path}`; clip = resLoader.get(path, AudioClip, bundle)!; // 加载音效资源 @@ -153,7 +155,7 @@ export class AudioEffectPool { // 资源已被释放 if (!clip.isValid) { - resolve(-1); + resolve(null!); return; } @@ -177,8 +179,7 @@ export class AudioEffectPool { else { node = this.pool.get()!; ae = node.getComponent(AudioEffect)!; - ae.key = ae.key; - ae.aeid = ae.aeid; + key = ae.key; } // 记录正在播放的音效播放器 @@ -189,41 +190,28 @@ export class AudioEffectPool { ae.volume = params.volume!; ae.clip = clip; ae.play(); - resolve(aeid); + resolve(ae); }); } + /** 音效播放完成 */ private onAudioEffectPlayComplete(ae: AudioEffect) { - const bundle = ae.params!.bundle!; - this.put(ae.aeid, ae.path, bundle); // 播放完回收对象 - ae.params && ae.params.onPlayComplete && ae.params.onPlayComplete(ae.aeid, ae.path, bundle); + this.put(ae); + ae.params && ae.params.onPlayComplete && ae.params.onPlayComplete(ae); console.log(`【音效】回收,池中剩余音效播放器【${this.pool.size()}】`); } /** * 回收音效播放器 - * @param aeid 播放器编号 - * @param path 音效路径 - * @param bundleName 资源包名 + * @param ae loadAndPlay 方法返回的音效播放器对象 */ - put(aeid: number, path: string | AudioClip, bundleName: string = resLoader.defaultBundleName) { - let key: string; - if (path instanceof AudioClip) { - key = path.uuid; - } - else if (path.indexOf("http") == 0) { - key = path; - } - else { - key = `${bundleName}_${path}`; - } - key += "_" + aeid; - - let ae = this.effects.get(key); - if (ae && ae.clip) { - this.effects.delete(key); - ae.stop(); - this.pool.put(ae.node); + put(ae: AudioEffect) { + let effect = this.effects.get(ae.key); + if (effect && effect.clip) { + effect.stop(); + effect.clip = null; + this.effects.delete(ae.key); + this.pool.put(effect.node); } } @@ -238,13 +226,11 @@ export class AudioEffectPool { /** 恢复所有音效 */ play() { - // if (!this.switch) return; this.effects.forEach(ae => ae.play()); } /** 暂停所有音效 */ pause() { - // if (!this.switch) return; this.effects.forEach(ae => { ae.pause(); this.onAudioEffectPlayComplete(ae); @@ -252,6 +238,16 @@ export class AudioEffectPool { this.effects.clear(); } + /** 施放音乐资源对象 */ + releaseAudioClip(ae: AudioEffect) { + if (ae.path instanceof AudioClip) { + ae.path.decRef(); + } + else { + resLoader.release(ae.path, ae.params!.bundle) + } + } + /** 释放所有音效资源与对象池中播放器 */ release() { // 释放池中音乐播放器 diff --git a/assets/core/common/audio/AudioManager.ts b/assets/core/common/audio/AudioManager.ts index 03edee2..71c4f3f 100644 --- a/assets/core/common/audio/AudioManager.ts +++ b/assets/core/common/audio/AudioManager.ts @@ -1,5 +1,6 @@ import { AudioClip, Component } from "cc"; import { oops } from "../../Oops"; +import { AudioEffect } from "./AudioEffect"; import { AudioEffectPool } from "./AudioEffectPool"; import { AudioMusic } from "./AudioMusic"; import { IAudioData, IAudioParams } from "./IAudio"; @@ -35,13 +36,13 @@ export class AudioManager extends Component { * @param url 资源地址 * @param params 音效参数 */ - playEffect(url: string | AudioClip, params?: IAudioParams): Promise { + playEffect(url: string | AudioClip, params?: IAudioParams): Promise { return this.effect.loadAndPlay(url, params); } /** 回收音效播放器 */ - putEffect(aeid: number, url: string | AudioClip, bundleName?: string) { - this.effect.put(aeid, url, bundleName); + putEffect(ae: AudioEffect) { + this.effect.put(ae); } /** 恢复当前暂停的音乐与音效播放 */ diff --git a/assets/module/common/GameComponent.ts b/assets/module/common/GameComponent.ts index 4a0e35e..7ea2bb5 100644 --- a/assets/module/common/GameComponent.ts +++ b/assets/module/common/GameComponent.ts @@ -6,6 +6,7 @@ */ import { Asset, Button, Component, EventHandler, EventKeyboard, EventTouch, Input, Node, Prefab, Sprite, SpriteFrame, __private, _decorator, input, isValid } from "cc"; import { oops } from "../../core/Oops"; +import { AudioEffect } from "../../core/common/audio/AudioEffect"; import { IAudioParams } from "../../core/common/audio/IAudio"; import { EventDispatcher } from "../../core/common/event/EventDispatcher"; import { EventMessage, ListenerFunc } from "../../core/common/event/EventMessage"; @@ -17,8 +18,7 @@ const { ccclass } = _decorator; /** 加载资源类型 */ enum ResType { Load, - LoadDir, - Audio + LoadDir } /** 资源加载记录 */ @@ -33,7 +33,6 @@ interface ResRecord { resId?: number } - /** * 游戏显示对象组件模板 * 1、当前对象加载的资源,会在对象释放时,自动释放引用的资源 @@ -138,7 +137,7 @@ export class GameComponent extends Component { * @param bundleName 资源包名 * @param paths 资源路径 */ - private addPathToRecord(type: ResType, bundleName: string, paths?: string | string[] | AssetType | ProgressCallback | CompleteCallback | null, resId?: number) { + private addPathToRecord(type: ResType, bundleName: string, paths?: string | string[] | AssetType | ProgressCallback | CompleteCallback | null) { if (this.resPaths == null) this.resPaths = new Map(); var rps = this.resPaths.get(type); @@ -151,45 +150,44 @@ export class GameComponent extends Component { let realBundle = bundleName; for (let index = 0; index < paths.length; index++) { let realPath = paths[index]; - let key = this.getResKey(realBundle, realPath, resId); + let key = this.getResKey(realBundle, realPath); let rp = rps.get(key); if (rp) { rp.refCount++; } else { - rps.set(key, { path: realPath, bundle: realBundle, refCount: 1, resId: resId }); + rps.set(key, { path: realPath, bundle: realBundle, refCount: 1 }); } } } else if (typeof paths === "string") { let realBundle = bundleName; let realPath = paths; - let key = this.getResKey(realBundle, realPath, resId); + let key = this.getResKey(realBundle, realPath); let rp = rps.get(key); if (rp) { rp.refCount++; } else { - rps.set(key, { path: realPath, bundle: realBundle, refCount: 1, resId: resId }); + rps.set(key, { path: realPath, bundle: realBundle, refCount: 1 }); } } else { let realBundle = oops.res.defaultBundleName; let realPath = bundleName; - let key = this.getResKey(realBundle, realPath, resId); + let key = this.getResKey(realBundle, realPath); let rp = rps.get(key); if (rp) { rp.refCount++; } else { - rps.set(key, { path: realPath, bundle: realBundle, refCount: 1, resId: resId }); + rps.set(key, { path: realPath, bundle: realBundle, refCount: 1 }); } } } - private getResKey(realBundle: string, realPath: string, resId?: number): string { + private getResKey(realBundle: string, realPath: string): string { let key = `${realBundle}:${realPath}`; - if (resId != null) key += ":" + resId; return key; } @@ -304,13 +302,12 @@ export class GameComponent extends Component { /** 释放音效资源 */ releaseAudioEffect() { - if (this.resPaths) { - const rps = this.resPaths.get(ResType.Audio); - if (rps) { - rps.forEach((value: ResRecord) => { - oops.audio.putEffect(value.resId!, value.path, value.bundle); // 回收音乐效到音效池中等下次使用 - }); - } + if (this.audioEffects) { + this.audioEffects.forEach((ae: AudioEffect) => { + ae.stop(); + oops.audio.effect.releaseAudioClip(ae); + }); + this.audioEffects.clear(); } } @@ -346,37 +343,30 @@ export class GameComponent extends Component { oops.audio.music.loadAndPlay(url, params); } + /** 音效缓存 */ + private audioEffects: Map = null!; + /** * 播放音效 * @param url 资源地址 * @param params 音效播放参数 */ - async playEffect(url: string, params?: IAudioParams): Promise { + async playEffect(url: string, params?: IAudioParams): Promise { return new Promise(async (resolve, reject) => { - let bundleName = resLoader.defaultBundleName; - if (params == null) { - params = { bundle: bundleName } - } - else if (params.bundle != null) { - bundleName = params.bundle; - } - // 音效播放完,关闭正在播放状态的音乐效果 - params.onPlayComplete = (aeid: number, url: string, bundleName: string) => { + if (params == null) params = {}; + params.onPlayComplete = (ae: AudioEffect) => { // 音效播放完前,界面被释放 if (!this.isValid) return; // 删除界面音效的播放记录 - const rps = this.resPaths.get(ResType.Audio); - if (rps) { - const key = this.getResKey(bundleName, url, aeid); - rps.delete(key); - } + this.audioEffects.delete(ae.key); } - let resId = await oops.audio.playEffect(url, params); - this.addPathToRecord(ResType.Audio, bundleName, url, resId); // 支持界面释放时,立即停止所有音效的播放 - resolve(resId); + let ae = await oops.audio.playEffect(url, params); + if (this.audioEffects == null) this.audioEffects = new Map(); + this.audioEffects.set(ae.key, ae); + resolve(ae); }); } //#endregion @@ -528,11 +518,12 @@ export class GameComponent extends Component { // 自动释放资源 if (this.resPaths) { - this.releaseAudioEffect(); this.release(); this.releaseDir(); this.resPaths.clear(); this.resPaths = null!; } + + this.releaseAudioEffect(); } } \ No newline at end of file -- Gitee From e94f265cb87fccc404724ad298367a85a56658f2 Mon Sep 17 00:00:00 2001 From: dgflash Date: Thu, 28 Aug 2025 20:29:58 +0800 Subject: [PATCH 13/20] =?UTF-8?q?=E9=9F=B3=E6=95=88=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/core/common/audio/AudioEffect.ts | 10 ++++++++-- assets/core/common/audio/AudioEffectPool.ts | 13 ++++++------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/assets/core/common/audio/AudioEffect.ts b/assets/core/common/audio/AudioEffect.ts index d7522e1..89b454f 100644 --- a/assets/core/common/audio/AudioEffect.ts +++ b/assets/core/common/audio/AudioEffect.ts @@ -4,8 +4,7 @@ * @LastEditors: dgflash * @LastEditTime: 2022-09-02 10:22:36 */ -import { AudioClip } from 'cc'; -import { AudioSource, _decorator } from 'cc'; +import { AudioClip, AudioSource, _decorator } from 'cc'; import { IAudioParams } from './IAudio'; const { ccclass } = _decorator; @@ -30,4 +29,11 @@ export class AudioEffect extends AudioSource { private onAudioEnded() { this.onComplete && this.onComplete(this); } + + reset() { + this.stop(); + this.clip = null; + this.path = null!; + this.params = null!; + } } \ No newline at end of file diff --git a/assets/core/common/audio/AudioEffectPool.ts b/assets/core/common/audio/AudioEffectPool.ts index 2a4979f..0736f1d 100644 --- a/assets/core/common/audio/AudioEffectPool.ts +++ b/assets/core/common/audio/AudioEffectPool.ts @@ -168,28 +168,27 @@ export class AudioEffectPool { key += "_" + aeid; node = new Node("AudioEffect"); - ae = node.addComponent(AudioEffect); ae.key = key; ae.aeid = aeid; - ae.path = path; - ae.params = params; ae.onComplete = this.onAudioEffectPlayComplete.bind(this); } else { node = this.pool.get()!; ae = node.getComponent(AudioEffect)!; - key = ae.key; } // 记录正在播放的音效播放器 - this.effects.set(key, ae); + this.effects.set(ae.key, ae); node.parent = oops.audio.node; + ae.path = path; + ae.params = params; ae.loop = params.loop!; ae.volume = params.volume!; ae.clip = clip; ae.play(); + resolve(ae); }); } @@ -208,8 +207,8 @@ export class AudioEffectPool { put(ae: AudioEffect) { let effect = this.effects.get(ae.key); if (effect && effect.clip) { - effect.stop(); - effect.clip = null; + effect.reset(); + this.effects.delete(ae.key); this.pool.put(effect.node); } -- Gitee From 790db9ff9894e524f7a8cf30a678d37dccaa0b0b Mon Sep 17 00:00:00 2001 From: dgflash Date: Thu, 28 Aug 2025 20:43:15 +0800 Subject: [PATCH 14/20] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=83=8C=E6=99=AF?= =?UTF-8?q?=E9=9F=B3=E4=B9=90=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/core/common/audio/AudioMusic.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/assets/core/common/audio/AudioMusic.ts b/assets/core/common/audio/AudioMusic.ts index e12ae23..2bc09c7 100644 --- a/assets/core/common/audio/AudioMusic.ts +++ b/assets/core/common/audio/AudioMusic.ts @@ -107,13 +107,11 @@ export class AudioMusic extends AudioSource { let bundleName = resLoader.defaultBundleName; let loop = false; let volume = this.volume; - let onPlayComplete: Function = null!; if (params) { this._params = params! if (params.bundle != null) bundleName = params.bundle; if (params.loop != null) loop = params.loop; if (params.volume != null) volume = params.volume; - if (params.onPlayComplete != null) onPlayComplete = params.onPlayComplete; }; this._isLoading = true; -- Gitee From 0581773c949204405a2979c3d1dce3b6da0998ac Mon Sep 17 00:00:00 2001 From: dgflash Date: Fri, 29 Aug 2025 14:45:17 +0800 Subject: [PATCH 15/20] =?UTF-8?q?=E9=9F=B3=E4=B9=90=E9=9F=B3=E6=95=88?= =?UTF-8?q?=E6=9E=9C=E6=94=AF=E6=8C=81=E6=92=AD=E6=94=BE=E5=AE=8C=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E9=87=8A=E6=94=BE=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/core/common/audio/AudioEffect.ts | 2 +- assets/core/common/audio/AudioEffectPool.ts | 38 ++++++++++----------- assets/core/common/audio/AudioManager.ts | 32 +++++------------ assets/core/common/audio/AudioMusic.ts | 16 ++++----- assets/core/common/audio/IAudio.ts | 2 ++ assets/module/common/GameComponent.ts | 27 +-------------- 6 files changed, 40 insertions(+), 77 deletions(-) diff --git a/assets/core/common/audio/AudioEffect.ts b/assets/core/common/audio/AudioEffect.ts index 89b454f..354539a 100644 --- a/assets/core/common/audio/AudioEffect.ts +++ b/assets/core/common/audio/AudioEffect.ts @@ -18,7 +18,7 @@ export class AudioEffect extends AudioSource { /** 音效果资源路径 */ path: string | AudioClip = null! /** 音效参数 */ - params?: IAudioParams; + params: IAudioParams = null! /** 背景音乐播放完成回调 */ onComplete: Function | null = null; diff --git a/assets/core/common/audio/AudioEffectPool.ts b/assets/core/common/audio/AudioEffectPool.ts index 0736f1d..3c6d39c 100644 --- a/assets/core/common/audio/AudioEffectPool.ts +++ b/assets/core/common/audio/AudioEffectPool.ts @@ -42,7 +42,7 @@ export class AudioEffectPool { * @param type 音效类型 * @returns 音效开关 */ - getSwitch(type: string) { + getSwitch(type: string = AudioEffectType.Effect) { let iad = this.data[type]; if (iad == null) console.error(`类型为【${type}】的音效配置不存在`); return iad.switch; @@ -52,7 +52,7 @@ export class AudioEffectPool { * @param type 音效类型 * @param value 音效开关 */ - setSwitch(type: string, value: boolean) { + setSwitch(value: boolean, type: string = AudioEffectType.Effect) { let iad = this.data[type]; if (iad == null) console.error(`类型为【${type}】的音效配置不存在`); iad.switch = value; @@ -65,7 +65,7 @@ export class AudioEffectPool { * @param type 音效类型 * @returns 音效音量 */ - getVolume(type: string) { + getVolume(type: string = AudioEffectType.Effect) { let iad = this.data[type]; if (iad == null) console.error(`类型为【${type}】的音效配置不存在`); return iad.volume; @@ -73,10 +73,10 @@ export class AudioEffectPool { /** * 音效音量设置 - * @param type 音效类型 * @param value 音效音量 + * @param type 音效类型 */ - setVolume(type: string, value: number) { + setVolume(value: number, type: string = AudioEffectType.Effect) { let iad = this.data[type]; if (iad == null) console.error(`类型为【${type}】的音效配置不存在`); iad.volume = value; @@ -94,15 +94,17 @@ export class AudioEffectPool { return new Promise(async (resolve, reject) => { if (params == null) { params = { + type: AudioEffectType.Effect, bundle: resLoader.defaultBundleName, loop: false, - type: AudioEffectType.Effect + destroy: false } } else { + if (params.type == null) params.type = AudioEffectType.Effect; if (params.bundle == null) params.bundle = resLoader.defaultBundleName; if (params.loop == null) params.loop = false; - if (params.type == null) params.type = AudioEffectType.Effect; // 默认音效类型 + if (params.type == null) params.destroy = false; } let iad = this.data[params.type!]; @@ -195,9 +197,17 @@ export class AudioEffectPool { /** 音效播放完成 */ private onAudioEffectPlayComplete(ae: AudioEffect) { - this.put(ae); + if (ae.params.destroy) { + if (ae.path instanceof AudioClip) { + ae.path.decRef(); + } + else { + resLoader.release(ae.path, ae.params!.bundle); + } + } ae.params && ae.params.onPlayComplete && ae.params.onPlayComplete(ae); - console.log(`【音效】回收,池中剩余音效播放器【${this.pool.size()}】`); + this.put(ae); + // console.log(`【音效】回收,池中剩余音效播放器【${this.pool.size()}】`); } /** @@ -237,16 +247,6 @@ export class AudioEffectPool { this.effects.clear(); } - /** 施放音乐资源对象 */ - releaseAudioClip(ae: AudioEffect) { - if (ae.path instanceof AudioClip) { - ae.path.decRef(); - } - else { - resLoader.release(ae.path, ae.params!.bundle) - } - } - /** 释放所有音效资源与对象池中播放器 */ release() { // 释放池中音乐播放器 diff --git a/assets/core/common/audio/AudioManager.ts b/assets/core/common/audio/AudioManager.ts index 71c4f3f..4913107 100644 --- a/assets/core/common/audio/AudioManager.ts +++ b/assets/core/common/audio/AudioManager.ts @@ -73,12 +73,7 @@ export class AudioManager extends Component { this.data = oops.storage.getJson(LOCAL_STORE_KEY); if (this.data) { - try { - this.setState(); - } - catch { - this.setStateDefault(); - } + this.setState(); } else { this.setStateDefault(); @@ -86,19 +81,10 @@ export class AudioManager extends Component { } private setState() { - for (let type in this.data) { - let iad = this.data[type]; - switch (type) { - case "music": - this.music.switch = this.data.music.switch; - this.music.volume = this.data.music.volume; - break; - default: - this.effect.setSwitch(type, iad.switch); - this.effect.setVolume(type, iad.volume); - break; - } - } + //@ts-ignore + this.music.data = this.data; + //@ts-ignore + this.effect.data = this.data; } private setStateDefault() { @@ -109,13 +95,13 @@ export class AudioManager extends Component { //@ts-ignore this.music.data = this.data; - this.music.switch = true - this.music.volume = 1; + this.music.setSwitch(true); + this.music.setVolume(1); //@ts-ignore this.effect.data = this.data; - this.effect.setSwitch(AudioEffectType.Effect, true); - this.effect.setVolume(AudioEffectType.Effect, 1); + this.effect.setSwitch(true, AudioEffectType.Effect); + this.effect.setVolume(1, AudioEffectType.Effect); this.save(); } diff --git a/assets/core/common/audio/AudioMusic.ts b/assets/core/common/audio/AudioMusic.ts index 2bc09c7..a2000f5 100644 --- a/assets/core/common/audio/AudioMusic.ts +++ b/assets/core/common/audio/AudioMusic.ts @@ -31,7 +31,7 @@ export class AudioMusic extends AudioSource { * @param type 音效类型 * @returns 音效开关 */ - get switch() { + getSwitch() { return this.data[AudioEffectType.Music].switch; } /** @@ -39,7 +39,7 @@ export class AudioMusic extends AudioSource { * @param type 音效类型 * @param value 音效开关 */ - set switch(value: boolean) { + setSwitch(value: boolean) { this.data[AudioEffectType.Music].switch = value; if (!value) this.stop(); } @@ -49,16 +49,16 @@ export class AudioMusic extends AudioSource { * @param type 音效类型 * @returns 音效音量 */ - get volume() { + getVolume(): number { return this.data[AudioEffectType.Music].volume; } /** * 音效音量设置 * @param value 音效音量 */ - set volume(value: number) { + setVolume(value: number) { this.data[AudioEffectType.Music].volume = value; - super.volume = value; + this.volume = value; } /** 获取音乐播放进度 */ @@ -95,7 +95,7 @@ export class AudioMusic extends AudioSource { * @param params 背景音乐资源播放参数 */ async loadAndPlay(path: string, params?: IAudioParams) { - if (!this.switch) return; // 禁止播放音乐 + if (!this.getSwitch()) return; // 禁止播放音乐 // 下一个加载的背景音乐资源 if (this._isLoading) { @@ -105,7 +105,7 @@ export class AudioMusic extends AudioSource { } let bundleName = resLoader.defaultBundleName; - let loop = false; + let loop = true; let volume = this.volume; if (params) { this._params = params! @@ -164,7 +164,7 @@ export class AudioMusic extends AudioSource { /** 停止当前音乐与音效的播放 */ stop(): void { - if (this.switch && this.playing) { + if (this.getSwitch() && this.playing) { super.stop(); } } diff --git a/assets/core/common/audio/IAudio.ts b/assets/core/common/audio/IAudio.ts index fe59f42..b423ef1 100644 --- a/assets/core/common/audio/IAudio.ts +++ b/assets/core/common/audio/IAudio.ts @@ -7,6 +7,8 @@ export interface IAudioParams { loop?: boolean; /** 音效音量 */ volume?: number; + /** 是否在播放完后自动释放音乐资源(默认不释放) */ + destroy?: boolean; /** 播放完成事件 */ onPlayComplete?: Function; } diff --git a/assets/module/common/GameComponent.ts b/assets/module/common/GameComponent.ts index 7ea2bb5..bc29552 100644 --- a/assets/module/common/GameComponent.ts +++ b/assets/module/common/GameComponent.ts @@ -300,17 +300,6 @@ export class GameComponent extends Component { } } - /** 释放音效资源 */ - releaseAudioEffect() { - if (this.audioEffects) { - this.audioEffects.forEach((ae: AudioEffect) => { - ae.stop(); - oops.audio.effect.releaseAudioClip(ae); - }); - this.audioEffects.clear(); - } - } - /** * 设置图片资源 * @param target 目标精灵对象 @@ -343,9 +332,6 @@ export class GameComponent extends Component { oops.audio.music.loadAndPlay(url, params); } - /** 音效缓存 */ - private audioEffects: Map = null!; - /** * 播放音效 * @param url 资源地址 @@ -355,17 +341,8 @@ export class GameComponent extends Component { return new Promise(async (resolve, reject) => { // 音效播放完,关闭正在播放状态的音乐效果 if (params == null) params = {}; - params.onPlayComplete = (ae: AudioEffect) => { - // 音效播放完前,界面被释放 - if (!this.isValid) return; - - // 删除界面音效的播放记录 - this.audioEffects.delete(ae.key); - } - let ae = await oops.audio.playEffect(url, params); - if (this.audioEffects == null) this.audioEffects = new Map(); - this.audioEffects.set(ae.key, ae); + this.addPathToRecord(ResType.Load, ae.params!.bundle!, url); resolve(ae); }); } @@ -523,7 +500,5 @@ export class GameComponent extends Component { this.resPaths.clear(); this.resPaths = null!; } - - this.releaseAudioEffect(); } } \ No newline at end of file -- Gitee From b54fd6b0e7f17e7c74ac03eced6083b45afa668d Mon Sep 17 00:00:00 2001 From: dgflash Date: Fri, 29 Aug 2025 18:02:16 +0800 Subject: [PATCH 16/20] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/core/common/audio/AudioEffectPool.ts | 2 +- assets/core/common/audio/AudioEnum.ts | 7 ++++ assets/core/common/audio/AudioEnum.ts.meta | 9 +++++ assets/core/common/audio/AudioManager.ts | 45 +++++++++++---------- assets/core/common/audio/AudioMusic.ts | 2 +- 5 files changed, 41 insertions(+), 24 deletions(-) create mode 100644 assets/core/common/audio/AudioEnum.ts create mode 100644 assets/core/common/audio/AudioEnum.ts.meta diff --git a/assets/core/common/audio/AudioEffectPool.ts b/assets/core/common/audio/AudioEffectPool.ts index 3c6d39c..f478b8b 100644 --- a/assets/core/common/audio/AudioEffectPool.ts +++ b/assets/core/common/audio/AudioEffectPool.ts @@ -2,7 +2,7 @@ import { AudioClip, Node, NodePool } from "cc"; import { oops } from "../../Oops"; import { resLoader } from "../loader/ResLoader"; import { AudioEffect } from "./AudioEffect"; -import { AudioEffectType } from "./AudioManager"; +import { AudioEffectType } from "./AudioEnum"; import { IAudioData, IAudioParams } from "./IAudio"; /** 音乐效缓冲编号最大值 */ diff --git a/assets/core/common/audio/AudioEnum.ts b/assets/core/common/audio/AudioEnum.ts new file mode 100644 index 0000000..15974b0 --- /dev/null +++ b/assets/core/common/audio/AudioEnum.ts @@ -0,0 +1,7 @@ +/** 音乐音效默认类型 */ +export enum AudioEffectType { + /** 背景音乐 */ + Music = "music", + /** 音乐音效 */ + Effect = "effect", +} diff --git a/assets/core/common/audio/AudioEnum.ts.meta b/assets/core/common/audio/AudioEnum.ts.meta new file mode 100644 index 0000000..41c7c1f --- /dev/null +++ b/assets/core/common/audio/AudioEnum.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "68e8e4a0-93f5-4606-9c42-06b1daff6d1e", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/core/common/audio/AudioManager.ts b/assets/core/common/audio/AudioManager.ts index 4913107..aec7f46 100644 --- a/assets/core/common/audio/AudioManager.ts +++ b/assets/core/common/audio/AudioManager.ts @@ -2,18 +2,12 @@ import { AudioClip, Component } from "cc"; import { oops } from "../../Oops"; import { AudioEffect } from "./AudioEffect"; import { AudioEffectPool } from "./AudioEffectPool"; +import { AudioEffectType } from "./AudioEnum"; import { AudioMusic } from "./AudioMusic"; import { IAudioData, IAudioParams } from "./IAudio"; const LOCAL_STORE_KEY = "game_audio"; -/** 音乐音效默认类型 */ -export enum AudioEffectType { - /** 背景音乐 */ - Music = "music", - /** 音乐音效 */ - Effect = "effect", -} /** * 音频管理 @@ -88,21 +82,28 @@ export class AudioManager extends Component { } private setStateDefault() { - this.data = { - music: { switch: true, volume: 1 }, - effect: { switch: true, volume: 1 }, - }; - - //@ts-ignore - this.music.data = this.data; - this.music.setSwitch(true); - this.music.setVolume(1); - - //@ts-ignore - this.effect.data = this.data; - this.effect.setSwitch(true, AudioEffectType.Effect); - this.effect.setVolume(1, AudioEffectType.Effect); - + this.data = {}; + for (const key in AudioEffectType) { + //@ts-ignore + const value = AudioEffectType[key]; + if (typeof value === 'string') { + this.data[value] = { switch: true, volume: 1 }; + switch (value) { + case AudioEffectType.Music: + //@ts-ignore + this.music.data = this.data; + this.music.setSwitch(true); + this.music.setVolume(1); + break; + default: + //@ts-ignore + this.effect.data = this.data; + this.effect.setSwitch(true, value); + this.effect.setVolume(1, value); + break; + } + } + } this.save(); } } \ No newline at end of file diff --git a/assets/core/common/audio/AudioMusic.ts b/assets/core/common/audio/AudioMusic.ts index a2000f5..f0fc4b8 100644 --- a/assets/core/common/audio/AudioMusic.ts +++ b/assets/core/common/audio/AudioMusic.ts @@ -6,7 +6,7 @@ */ import { AudioClip, AudioSource, _decorator } from 'cc'; import { resLoader } from '../loader/ResLoader'; -import { AudioEffectType } from './AudioManager'; +import { AudioEffectType } from './AudioEnum'; import { IAudioData, IAudioParams } from './IAudio'; const { ccclass } = _decorator; -- Gitee From 4d6e2af88bec695b119675630dc9e805f0c50971 Mon Sep 17 00:00:00 2001 From: dgflash Date: Sat, 30 Aug 2025 11:34:15 +0800 Subject: [PATCH 17/20] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/core/common/audio/AudioEffectPool.ts | 22 ++++++++++----------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/assets/core/common/audio/AudioEffectPool.ts b/assets/core/common/audio/AudioEffectPool.ts index f478b8b..c676057 100644 --- a/assets/core/common/audio/AudioEffectPool.ts +++ b/assets/core/common/audio/AudioEffectPool.ts @@ -16,8 +16,8 @@ export class AudioEffectPool { private pool: NodePool = new NodePool(); /** 对象池集合 */ private effects: Map = new Map(); - /** 用过的音效资源记录 */ - private res: Map = new Map(); + /** 记录项目资源库中使用过的音乐资源 */ + private res_project: Map = new Map(); /** 外网远程资源记录(地址、音效对象) */ private res_remote: Map = new Map(); @@ -140,23 +140,21 @@ export class AudioEffectPool { key = `${params.type}_${bundle}_${path}`; clip = resLoader.get(path, AudioClip, bundle)!; - // 加载音效资源 + // 加载音效资源 - 如果一个预制上加载了了音乐同一个音乐资源,此处不会记录音乐资源路径数据,资源内存由预制释放时一起释放 if (clip == null) { - let urls = this.res.get(bundle); - if (urls == null) { - urls = []; - this.res.set(bundle, urls); - urls.push(path); - } - else if (urls.indexOf(path) == -1) { - urls.push(path); + let paths = this.res_project.get(bundle); + if (paths == null) { + paths = []; + this.res_project.set(bundle, paths); } + if (paths.indexOf(path) == -1) paths.push(path); clip = await resLoader.loadAsync(bundle, path, AudioClip); } } // 资源已被释放 if (!clip.isValid) { + console.warn(`音效资源【${key}】已被释放`); resolve(null!); return; } @@ -270,7 +268,7 @@ export class AudioEffectPool { /** 释放各个资源包中的音效资源 */ releaseRes() { - this.res.forEach((paths: string[], bundleName: string) => { + this.res_project.forEach((paths: string[], bundleName: string) => { paths.forEach(path => resLoader.release(path, bundleName)); }); } -- Gitee From 3a45484bd2ea8ca7c8c808e6288260232aeb8f9d Mon Sep 17 00:00:00 2001 From: dgflash Date: Mon, 1 Sep 2025 15:34:25 +0800 Subject: [PATCH 18/20] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=83=8C=E6=99=AF?= =?UTF-8?q?=E9=9F=B3=E4=B9=90=E5=9C=A8=E6=81=A2=E5=A4=8D=E9=9F=B3=E9=87=8F?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E6=97=B6=EF=BC=8C=E9=9F=B3=E9=87=8F=E6=95=B0?= =?UTF-8?q?=E5=80=BC=E6=9C=AA=E8=B5=8B=E5=80=BC=E6=88=90=E5=8A=9F=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/core/common/audio/AudioMusic.ts | 29 +++++++++++++++----------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/assets/core/common/audio/AudioMusic.ts b/assets/core/common/audio/AudioMusic.ts index f0fc4b8..f6327ef 100644 --- a/assets/core/common/audio/AudioMusic.ts +++ b/assets/core/common/audio/AudioMusic.ts @@ -104,15 +104,20 @@ export class AudioMusic extends AudioSource { return; } - let bundleName = resLoader.defaultBundleName; - let loop = true; - let volume = this.volume; - if (params) { - this._params = params! - if (params.bundle != null) bundleName = params.bundle; - if (params.loop != null) loop = params.loop; - if (params.volume != null) volume = params.volume; - }; + if (params == null) { + params = { + type: AudioEffectType.Music, + bundle: resLoader.defaultBundleName, + loop: true, + volume: this.getVolume() + } + } + else { + if (params.type == null) params.type = AudioEffectType.Music; + if (params.bundle == null) params.bundle = resLoader.defaultBundleName; + if (params.loop == null) params.loop = true; + if (params.volume == null) params.volume = this.getVolume() + } this._isLoading = true; @@ -122,7 +127,7 @@ export class AudioMusic extends AudioSource { clip = await resLoader.loadRemote(path, { ext: `.${extension}` }); } else { - clip = await resLoader.loadAsync(bundleName, path, AudioClip); + clip = await resLoader.loadAsync(params.bundle!, path, AudioClip); } this._isLoading = false; @@ -145,8 +150,8 @@ export class AudioMusic extends AudioSource { // 播放背景音乐 this.clip = clip; - this.loop = loop; - this.volume = volume; + this.loop = params.loop!; + this.volume = params.volume!; this.currentTime = 0; this.play(); } -- Gitee From 79da7946d9a520b60050fd468fb841fa27c90209 Mon Sep 17 00:00:00 2001 From: dgflash Date: Tue, 2 Sep 2025 17:14:45 +0800 Subject: [PATCH 19/20] =?UTF-8?q?=E6=B8=B8=E6=88=8F=E8=83=8C=E6=99=AF?= =?UTF-8?q?=E9=9F=B3=E4=B9=90=E7=8B=AC=E7=AB=8B=E8=8A=82=E7=82=B9=E7=AE=A1?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/core/common/audio/AudioManager.ts | 4 +- assets/core/common/audio/AudioMusic.ts | 76 +++++++++++------------- 2 files changed, 39 insertions(+), 41 deletions(-) diff --git a/assets/core/common/audio/AudioManager.ts b/assets/core/common/audio/AudioManager.ts index aec7f46..fdd1374 100644 --- a/assets/core/common/audio/AudioManager.ts +++ b/assets/core/common/audio/AudioManager.ts @@ -63,7 +63,8 @@ export class AudioManager extends Component { /** 本地加载音乐音效的音量、开关配置数据并设置到游戏中 */ load() { - this.music = this.getComponent(AudioMusic) || this.addComponent(AudioMusic)!; + this.music = new AudioMusic(); + this.music.parent = this.node; this.data = oops.storage.getJson(LOCAL_STORE_KEY); if (this.data) { @@ -81,6 +82,7 @@ export class AudioManager extends Component { this.effect.data = this.data; } + /** 默认音乐配置数据 */ private setStateDefault() { this.data = {}; for (const key in AudioEffectType) { diff --git a/assets/core/common/audio/AudioMusic.ts b/assets/core/common/audio/AudioMusic.ts index f6327ef..f676796 100644 --- a/assets/core/common/audio/AudioMusic.ts +++ b/assets/core/common/audio/AudioMusic.ts @@ -4,27 +4,26 @@ * @LastEditors: dgflash * @LastEditTime: 2023-05-16 09:11:30 */ -import { AudioClip, AudioSource, _decorator } from 'cc'; +import { AudioClip, Node } from 'cc'; import { resLoader } from '../loader/ResLoader'; +import { AudioEffect } from './AudioEffect'; import { AudioEffectType } from './AudioEnum'; import { IAudioData, IAudioParams } from './IAudio'; -const { ccclass } = _decorator; - /** * 背景音乐 - * 1、播放一个新背景音乐时,先加载音乐资源,然后停止正在播放的背景资源同时施放当前背景音乐资源,最后播放新的背景音乐 + * 1、播放一个新背景音乐时,先加载音乐资源,然后停止正在播放的背景资源同时释放当前背景音乐资源,最后播放新的背景音乐 + * 2、背景音乐循环播放时,不会触发播放完成事件 */ -@ccclass('AudioMusic') -export class AudioMusic extends AudioSource { +export class AudioMusic extends Node { /** 音效配置数据 */ private data: { [node: string]: IAudioData } = null!; private _progress: number = 0; private _isLoading: boolean = false; - private _nextUrl: string = null!; + private _nextPath: string = null!; private _nextParams: IAudioParams = null!; - private _params: IAudioParams = null!; + private _ae: AudioEffect = null!; /** * 音效开关 @@ -58,13 +57,11 @@ export class AudioMusic extends AudioSource { */ setVolume(value: number) { this.data[AudioEffectType.Music].volume = value; - this.volume = value; } /** 获取音乐播放进度 */ get progress(): number { - if (this.duration > 0) - this._progress = this.currentTime / this.duration; + if (this._ae.duration > 0) this._progress = this._ae.currentTime / this._ae.duration; return this._progress; } /** @@ -73,20 +70,19 @@ export class AudioMusic extends AudioSource { */ set progress(value: number) { this._progress = value; - this.currentTime = value * this.duration; + this._ae.currentTime = value * this._ae.duration; } - start() { - // this.node.on(AudioSource.EventType.STARTED, this.onAudioStarted, this); - this.node.on(AudioSource.EventType.ENDED, this.onAudioEnded, this); + constructor() { + super(); + this.name = "AudioMusic"; + this._ae = this.addComponent(AudioEffect); + this._ae.onComplete = this.onAudioEffectPlayComplete.bind(this); } - // private onAudioStarted() { } - - private onAudioEnded() { - if (this._params && this._params.onPlayComplete) { - this._params.onPlayComplete(); - } + /** 音效播放完成 */ + private onAudioEffectPlayComplete(ae: AudioEffect) { + ae.params && ae.params.onPlayComplete && ae.params.onPlayComplete(ae); } /** @@ -99,7 +95,7 @@ export class AudioMusic extends AudioSource { // 下一个加载的背景音乐资源 if (this._isLoading) { - this._nextUrl = path; + this._nextPath = path; this._nextParams = params!; return; } @@ -133,53 +129,53 @@ export class AudioMusic extends AudioSource { this._isLoading = false; // 处理等待加载的背景音乐 - if (this._nextUrl != null) { + if (this._nextPath != null) { // 加载等待播放的背景音乐 - this.loadAndPlay(this._nextUrl, this._nextParams); - this._nextUrl = null!; + this.loadAndPlay(this._nextPath, this._nextParams); + this._nextPath = null!; this._nextParams = null!; } else { // 正在播放的时候先关闭 - if (this.playing) { - this.stop(); - } + if (this._ae.playing) this.stop(); // 删除当前正在播放的音乐 this.release(); // 播放背景音乐 - this.clip = clip; - this.loop = params.loop!; - this.volume = params.volume!; - this.currentTime = 0; - this.play(); + this._ae.params = params; + this._ae.path = path; + this._ae.clip = clip; + this._ae.loop = params.loop!; + this._ae.volume = params.volume!; + this._ae.currentTime = 0; + this._ae.play(); } } /** 恢复当前暂停的音乐与音效播放 */ resume() { - if (!this.playing && this.progress > 0) super.play(); + if (!this._ae.playing && this.progress > 0) this._ae.play(); } /** 暂停当前音乐与音效的播放 */ pause() { - if (this.playing) super.pause(); + if (this._ae.playing) this._ae.pause(); } /** 停止当前音乐与音效的播放 */ stop(): void { - if (this.getSwitch() && this.playing) { - super.stop(); + if (this.getSwitch() && this._ae.playing) { + this._ae.stop(); } } /** 释放当前背景音乐资源 */ release() { - if (this.clip) { + if (this._ae.clip) { this.stop(); - this.clip.decRef(); - this.clip = null; + this._ae.clip.decRef(); + this._ae.clip = null; } } } \ No newline at end of file -- Gitee From 58e225cddd84b540bc468af151b538c08102fd7e Mon Sep 17 00:00:00 2001 From: dgflash Date: Tue, 2 Sep 2025 17:21:21 +0800 Subject: [PATCH 20/20] =?UTF-8?q?=E8=A1=A5=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/core/common/audio/AudioManager.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/assets/core/common/audio/AudioManager.ts b/assets/core/common/audio/AudioManager.ts index fdd1374..d13885a 100644 --- a/assets/core/common/audio/AudioManager.ts +++ b/assets/core/common/audio/AudioManager.ts @@ -8,7 +8,6 @@ import { IAudioData, IAudioParams } from "./IAudio"; const LOCAL_STORE_KEY = "game_audio"; - /** * 音频管理 * @help https://gitee.com/dgflash/oops-framework/wikis/pages?sort_id=12037893&doc_id=2873565 @@ -25,13 +24,22 @@ export class AudioManager extends Component { /** 音乐管理状态数据 */ private data: { [node: string]: IAudioData } = {}; + /** + * 播放背景音乐 + * @param path 资源路径 + * @param params 音效参数 + */ + playMusic(path: string, params?: IAudioParams) { + this.music.loadAndPlay(path, params); + } + /** * 播放音效 - * @param url 资源地址 - * @param params 音效参数 + * @param path 资源路径 + * @param params 音效参数 */ - playEffect(url: string | AudioClip, params?: IAudioParams): Promise { - return this.effect.loadAndPlay(url, params); + playEffect(path: string | AudioClip, params?: IAudioParams): Promise { + return this.effect.loadAndPlay(path, params); } /** 回收音效播放器 */ -- Gitee