diff --git a/assets/core/common/audio/AudioEffect.ts b/assets/core/common/audio/AudioEffect.ts index 1dfadbf..354539a 100644 --- a/assets/core/common/audio/AudioEffect.ts +++ b/assets/core/common/audio/AudioEffect.ts @@ -4,12 +4,21 @@ * @LastEditors: dgflash * @LastEditTime: 2022-09-02 10:22:36 */ -import { AudioSource, _decorator } from 'cc'; +import { AudioClip, 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 = null! /** 背景音乐播放完成回调 */ onComplete: Function | null = null; @@ -18,6 +27,13 @@ export class AudioEffect extends AudioSource { } private onAudioEnded() { - this.onComplete && this.onComplete(); + 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 34e1a32..c676057 100644 --- a/assets/core/common/audio/AudioEffectPool.ts +++ b/assets/core/common/audio/AudioEffectPool.ts @@ -2,41 +2,24 @@ 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 "./AudioEnum"; +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(); /** 对象池集合 */ private effects: Map = new Map(); - /** 用过的音效资源记录 */ - private res: Map = new Map(); + /** 记录项目资源库中使用过的音乐资源 */ + private res_project: Map = new Map(); + /** 外网远程资源记录(地址、音效对象) */ + private res_remote: Map = new Map(); private _aeId: number = 0; /** 获取请求唯一编号 */ @@ -46,80 +29,149 @@ export class AudioEffectPool { return this._aeId; } + /** + * 注册音效类型 + * @param type + */ + register(type: string) { + this.data[type] = { switch: true, volume: 1 }; + } + + /** + * 音效开关 + * @param type 音效类型 + * @returns 音效开关 + */ + getSwitch(type: string = AudioEffectType.Effect) { + let iad = this.data[type]; + if (iad == null) console.error(`类型为【${type}】的音效配置不存在`); + return iad.switch; + } + /** + * 音效音量设置 + * @param type 音效类型 + * @param value 音效开关 + */ + setSwitch(value: boolean, type: string = AudioEffectType.Effect) { + let iad = this.data[type]; + if (iad == null) console.error(`类型为【${type}】的音效配置不存在`); + iad.switch = value; + + if (!value) this.stop(); + } + + /** + * 音效音量获取 + * @param type 音效类型 + * @returns 音效音量 + */ + getVolume(type: string = AudioEffectType.Effect) { + let iad = this.data[type]; + if (iad == null) console.error(`类型为【${type}】的音效配置不存在`); + return iad.volume; + } + + /** + * 音效音量设置 + * @param value 音效音量 + * @param type 音效类型 + */ + setVolume(value: number, type: string = AudioEffectType.Effect) { + let iad = this.data[type]; + if (iad == null) console.error(`类型为【${type}】的音效配置不存在`); + iad.volume = value; + + this.effects.forEach(ac => ac.volume = value); + } + /** * 加载与播放音效 - * @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; - } - - // 创建音效资源 - let clip: AudioClip; - if (url instanceof AudioClip) { - clip = url; + if (params == null) { + params = { + type: AudioEffectType.Effect, + bundle: resLoader.defaultBundleName, + loop: false, + destroy: false + } } else { - clip = resLoader.get(url, AudioClip, bundleName)!; + 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.destroy = false; + } - // 加载音效资源 + let iad = this.data[params.type!]; + if (iad == null) console.error(`类型为【${params.type!}】的音效配置不存在`); + + 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; + } + // 非引擎管理的远程资源加载 + else if (path.indexOf("http") == 0) { + key = `${params.type}_${path}`; + clip = this.res_remote.get(path); if (clip == null) { - let urls = this.res.get(bundleName); - if (urls == null) { - urls = []; - this.res.set(bundleName, urls); - urls.push(url); + const extension = path.split('.').pop(); + clip = await resLoader.loadRemote(path, { ext: `.${extension}` }); + this.res_remote.set(path, clip); + } + } + // 资源加载 + else { + key = `${params.type}_${bundle}_${path}`; + clip = resLoader.get(path, AudioClip, bundle)!; + + // 加载音效资源 - 如果一个预制上加载了了音乐同一个音乐资源,此处不会记录音乐资源路径数据,资源内存由预制释放时一起释放 + if (clip == null) { + let paths = this.res_project.get(bundle); + if (paths == null) { + paths = []; + this.res_project.set(bundle, paths); } - else if (urls.indexOf(url) == -1) { - urls.push(url); - } - clip = await resLoader.loadAsync(bundleName, url, AudioClip); + if (paths.indexOf(path) == -1) paths.push(path); + clip = await resLoader.loadAsync(bundle, path, AudioClip); } } // 资源已被释放 if (!clip.isValid) { - resolve(-1); + console.warn(`音效资源【${key}】已被释放`); + resolve(null!); 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 ae: AudioEffect; let node: Node = null!; if (this.pool.size() == 0) { - 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()}】`); - }; + aeid = this.getAeId(); + key += "_" + aeid; + + node = new Node("AudioEffect"); + ae = node.addComponent(AudioEffect); + ae.key = key; + ae.aeid = aeid; + ae.onComplete = this.onAudioEffectPlayComplete.bind(this); } else { node = this.pool.get()!; @@ -127,39 +179,46 @@ export class AudioEffectPool { } // 记录正在播放的音效播放器 - this.effects.set(key, ae); + this.effects.set(ae.key, ae); - ae.loop = loop; + node.parent = oops.audio.node; + ae.path = path; + ae.params = params; + ae.loop = params.loop!; + ae.volume = params.volume!; ae.clip = clip; - ae.volume = volume; - ae.currentTime = 0; ae.play(); - resolve(aeid); + resolve(ae); }); } + /** 音效播放完成 */ + private onAudioEffectPlayComplete(ae: AudioEffect) { + 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); + this.put(ae); + // console.log(`【音效】回收,池中剩余音效播放器【${this.pool.size()}】`); + } + /** * 回收音效播放器 - * @param aeid 播放器编号 - * @param url 音效路径 - * @param bundleName 资源包名 + * @param ae loadAndPlay 方法返回的音效播放器对象 */ - put(aeid: number, url: string | AudioClip, bundleName: string = resLoader.defaultBundleName) { - let key: string; - if (url instanceof AudioClip) { - key = url.uuid; - } - else { - key = `${bundleName}_${url}`; - } - key += "_" + aeid; + put(ae: AudioEffect) { + let effect = this.effects.get(ae.key); + if (effect && effect.clip) { + effect.reset(); - let ae = this.effects.get(key); - if (ae && ae.clip) { - this.effects.delete(key); - ae.stop(); - this.pool.put(ae.node); + this.effects.delete(ae.key); + this.pool.put(effect.node); } } @@ -167,41 +226,58 @@ export class AudioEffectPool { stop() { this.effects.forEach(ae => { ae.stop(); + this.onAudioEffectPlayComplete(ae); }); + this.effects.clear(); } /** 恢复所有音效 */ 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.onAudioEffectPlayComplete(ae); }); + this.effects.clear(); } /** 释放所有音效资源与对象池中播放器 */ release() { - // 释放正在播放的音效 - this.effects.forEach(ae => { - ae.node.destroy(); - }); - this.effects.clear(); + // 释放池中音乐播放器 + this.releasePool(); - // 释放音效资源 - this.res.forEach((urls: string[], bundleName: string) => { - urls.forEach(url => resLoader.release(url, bundleName)); - }); + // 释放各个资源包中的音效资源 + this.releaseRes(); - // 释放池中播放器 + // 释放外网远程音效资源 + this.releaseResRemote(); + } + + /** 释放池中音乐播放器 */ + releasePool() { this.pool.clear(); + + // 释放正在播放的音效对象 + this.effects.forEach(ae => ae.node.destroy()); + this.effects.clear(); + } + + /** 释放各个资源包中的音效资源 */ + releaseRes() { + this.res_project.forEach((paths: string[], bundleName: string) => { + paths.forEach(path => resLoader.release(path, bundleName)); + }); + } + + /** 释放外网远程音效资源 */ + 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/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 3eee464..d13885a 100644 --- a/assets/core/common/audio/AudioManager.ts +++ b/assets/core/common/audio/AudioManager.ts @@ -1,8 +1,10 @@ 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 { IAudioParams } from "./IAudio"; +import { IAudioData, IAudioParams } from "./IAudio"; const LOCAL_STORE_KEY = "game_audio"; @@ -20,31 +22,39 @@ export class AudioManager extends Component { effect: AudioEffectPool = new AudioEffectPool(); /** 音乐管理状态数据 */ - private localData: any = {}; + 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); } /** 回收音效播放器 */ - putEffect(aeid: number, url: string | AudioClip, bundleName?: string) { - this.effect.put(aeid, url, bundleName); + putEffect(ae: AudioEffect) { + this.effect.put(ae); } /** 恢复当前暂停的音乐与音效播放 */ 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,26 +66,17 @@ 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.music = new AudioMusic(); + this.music.parent = this.node; - this.localData = oops.storage.getJson(LOCAL_STORE_KEY); - if (this.localData) { - try { - this.setState(); - } - catch { - this.setStateDefault(); - } + this.data = oops.storage.getJson(LOCAL_STORE_KEY); + if (this.data) { + this.setState(); } else { this.setStateDefault(); @@ -83,17 +84,36 @@ 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; + //@ts-ignore + this.music.data = this.data; + //@ts-ignore + this.effect.data = this.data; } + /** 默认音乐配置数据 */ private setStateDefault() { - this.localData = {}; - this.music.volume = 1; - this.effect.volume = 1; - this.music.switch = true; - this.effect.switch = true; + 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 0f30441..f676796 100644 --- a/assets/core/common/audio/AudioMusic.ts +++ b/assets/core/common/audio/AudioMusic.ts @@ -4,38 +4,64 @@ * @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 { IAudioParams } from './IAudio'; - -const { ccclass } = _decorator; +import { AudioEffect } from './AudioEffect'; +import { AudioEffectType } from './AudioEnum'; +import { IAudioData, IAudioParams } from './IAudio'; /** * 背景音乐 - * 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!; - /** 背景音乐开关 */ - private _switch: boolean = true; - get switch(): boolean { - return this._switch; + /** + * 音效开关 + * @param type 音效类型 + * @returns 音效开关 + */ + getSwitch() { + return this.data[AudioEffectType.Music].switch; } - set switch(value: boolean) { - this._switch = value; + /** + * 音效音量设置 + * @param type 音效类型 + * @param value 音效开关 + */ + setSwitch(value: boolean) { + this.data[AudioEffectType.Music].switch = value; if (!value) this.stop(); } + /** + * 音效音量获取 + * @param type 音效类型 + * @returns 音效音量 + */ + getVolume(): number { + return this.data[AudioEffectType.Music].volume; + } + /** + * 音效音量设置 + * @param value 音效音量 + */ + setVolume(value: number) { + this.data[AudioEffectType.Music].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; } /** @@ -44,92 +70,112 @@ 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); } /** * 加载音乐并播放 - * @param url 音乐资源地址 + * @param path 音乐资源地址 * @param params 背景音乐资源播放参数 */ - async loadAndPlay(url: string, params?: IAudioParams) { - if (!this.switch) return; // 禁止播放音乐 + async loadAndPlay(path: string, params?: IAudioParams) { + if (!this.getSwitch()) return; // 禁止播放音乐 // 下一个加载的背景音乐资源 if (this._isLoading) { - this._nextUrl = url; + this._nextPath = path; this._nextParams = params!; return; } - 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; - }; + 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; - 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(); - } + 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(params.bundle!, path, AudioClip); + } - // 删除当前正在播放的音乐 - this.release(); + this._isLoading = false; - // 播放背景音乐 - this.clip = clip; - this.loop = loop; - this.volume = volume; - this.currentTime = 0; - this.play(); - } + // 处理等待加载的背景音乐 + if (this._nextPath != null) { + // 加载等待播放的背景音乐 + this.loadAndPlay(this._nextPath, this._nextParams); + this._nextPath = null!; + this._nextParams = null!; + } + else { + // 正在播放的时候先关闭 + if (this._ae.playing) this.stop(); + + // 删除当前正在播放的音乐 + this.release(); + + // 播放背景音乐 + 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._ae.playing && this.progress > 0) this._ae.play(); + } + + /** 暂停当前音乐与音效的播放 */ + pause() { + if (this._ae.playing) this._ae.pause(); + } + + /** 停止当前音乐与音效的播放 */ stop(): void { - if (this.switch && 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 diff --git a/assets/core/common/audio/IAudio.ts b/assets/core/common/audio/IAudio.ts index 68b5ecf..b423ef1 100644 --- a/assets/core/common/audio/IAudio.ts +++ b/assets/core/common/audio/IAudio.ts @@ -1,10 +1,21 @@ export interface IAudioParams { + /** 音乐分类 */ + type?: string, /** 资源包名 */ bundle?: string, /** 是否循环播放 */ loop?: boolean; /** 音效音量 */ volume?: number; + /** 是否在播放完后自动释放音乐资源(默认不释放) */ + destroy?: boolean; /** 播放完成事件 */ onPlayComplete?: Function; +} + +export interface IAudioData { + /** 音乐开关 */ + switch: boolean; + /** 音量 */ + volume: number; } \ No newline at end of file diff --git a/assets/core/common/loader/ResLoader.ts b/assets/core/common/loader/ResLoader.ts index 85098b1..5202b6a 100644 --- a/assets/core/common/loader/ResLoader.ts +++ b/assets/core/common/loader/ResLoader.ts @@ -73,34 +73,28 @@ export class ResLoader { * 加载远程资源 * @param url 资源地址 * @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 = null): Promise { + return new Promise((resolve, reject) => { + assetManager.loadRemote(url, options, (err, data: T) => { + if (err) { + reject(null); + return; + } + resolve(data); + }); + }); } //#endregion @@ -117,12 +111,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; 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 432a1c0..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); } /** @@ -59,36 +55,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); } } @@ -145,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); } @@ -172,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(); } } @@ -187,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!; } @@ -206,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 142a5d4..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 { @@ -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/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 { 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); } } diff --git a/assets/module/common/GameComponent.ts b/assets/module/common/GameComponent.ts index 4a0e35e..bc29552 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; } @@ -302,18 +300,6 @@ 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); // 回收音乐效到音效池中等下次使用 - }); - } - } - } - /** * 设置图片资源 * @param target 目标精灵对象 @@ -351,32 +337,13 @@ export class GameComponent extends Component { * @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 (!this.isValid) return; - - // 删除界面音效的播放记录 - const rps = this.resPaths.get(ResType.Audio); - if (rps) { - const key = this.getResKey(bundleName, url, aeid); - rps.delete(key); - } - } - - let resId = await oops.audio.playEffect(url, params); - this.addPathToRecord(ResType.Audio, bundleName, url, resId); // 支持界面释放时,立即停止所有音效的播放 - resolve(resId); + if (params == null) params = {}; + let ae = await oops.audio.playEffect(url, params); + this.addPathToRecord(ResType.Load, ae.params!.bundle!, url); + resolve(ae); }); } //#endregion @@ -528,7 +495,6 @@ export class GameComponent extends Component { // 自动释放资源 if (this.resPaths) { - this.releaseAudioEffect(); this.release(); this.releaseDir(); this.resPaths.clear(); diff --git a/assets/module/common/ModuleUtil.ts b/assets/module/common/ModuleUtil.ts index 5682187..7814aa1 100644 --- a/assets/module/common/ModuleUtil.ts +++ b/assets/module/common/ModuleUtil.ts @@ -1,15 +1,16 @@ 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 { UIConfig } from "../../core/gui/layer/UIConfig"; +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 { /** * 添加界面组件 @@ -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); @@ -98,28 +86,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(); - } + }; + oops.gui.remove(uiId, isDestroy); } - else { - ent.remove(ctor, isDestroy); - if (onRemoved) onRemoved(); - } - - oops.gui.remove(uiId, isDestroy); } } \ No newline at end of file