//********************* // create by 流云 // time: 2022年2月15日 // desc: 音频管理器 //********************* import { _decorator, Node, AudioSource, AudioClip, game, Game, assetManager, director, Asset, AssetManager, sys } from 'cc'; import { BattlePerformance } from '../../../script/ui/BattleGame/BattlePerformance'; /** * 音频管理器 */ export class SoundManager { private static _ins: SoundManager = null; public static get Ins() { if (!this._ins) this._ins = new SoundManager(); return this._ins; } private _node: Node = null; private _music: AudioSource = null; private _effect: AudioSource = null; private _game_id = "default_game"; private _user_name = "default_user"; private _click_name = ""; private _bundle = ""; private _path = ""; private _curent_music = ""; private _isPause = false; MusicVolume = 1; EffectVolume = 1; MuteMusic = false; MuteEffect = false; private _clip_map: Map = new Map(); private _clip_loading: Map> = new Map(); init({ gameid = "default_game", userName = "default_user", bundle = "resources", path = "sound/", clickEffname = ``, music_volume = 1, effect_volume = 1, mute_music = false, mute_effect = false }) { this._game_id = gameid; this._user_name = userName; this._click_name = clickEffname; this._bundle = bundle; this._path = path; this.MusicVolume = music_volume; this.EffectVolume = effect_volume; this.MuteMusic = mute_music; this.MuteEffect = mute_effect; if (!this._node) { this._node = new Node("soundNode"); director.addPersistRootNode(this._node); } this._music = this._node.addComponent(AudioSource); this._effect = this._node.addComponent(AudioSource); game.on(Game.EVENT_HIDE, this.onHide, this); game.on(Game.EVENT_SHOW, this.onShow, this); this._node.off(AudioSource.EventType.ENDED, this.onAudioEnded, this); this._node.on(AudioSource.EventType.ENDED, this.onAudioEnded, this); this.getStatus(); } clear() { this._clip_map.clear(); director.removePersistRootNode(this._node); this._node = null; } onHide() { this.pause(); } onShow() { this.resume(); } /** * 暂停 */ pause() { this._isPause = true; if (this._music.playing) this._music.pause(); if (this._effect.playing) this._effect.pause(); } /** * 恢复 */ resume() { this._isPause = false; this.updateStatus(); } updateStatus() { this._music.volume = this._isPause || this.MuteMusic ? 0 : this.MusicVolume; this._effect.volume = this._isPause || this.MuteEffect ? 0 : this.EffectVolume; if (this._music) this._music.play(); } /** * 从本地存储获取音频设置状态 */ private getStatus() { let temp = sys.localStorage.getItem(`${this._game_id + this._user_name}_music_volume`); if (temp) this.MusicVolume = Number(temp); temp = sys.localStorage.getItem(`${this._game_id + this._user_name}_music_mute`); if (temp) this.MuteMusic = Boolean(temp); temp = sys.localStorage.getItem(`${this._game_id + this._user_name}_effect_volume`); if (temp) this.EffectVolume = Number(temp); temp = sys.localStorage.getItem(`${this._game_id + this._user_name}_effect_mute`); if (temp) this.MuteEffect = Boolean(temp); } /** * 保存音频设置到持久化存储 */ saveStatus() { sys.localStorage.setItem(`${this._game_id + this._user_name}_music_volume`, this.MusicVolume + ``); sys.localStorage.setItem(`${this._game_id + this._user_name}_music_mute`, this.MuteMusic + ``); sys.localStorage.setItem(`${this._game_id + this._user_name}_effect_volume`, this.EffectVolume + ``); sys.localStorage.setItem(`${this._game_id + this._user_name}_effect_mute`, this.MuteEffect + ``); } /** * 释放所有允许释放的音频资源 */ release() { this._clip_map.forEach((v, k) => { if (v.isRelease && v.clip) { v.clip.decRef(); v.clip = null; } }) } /** * 播放音乐 * @param param * @returns */ async playMusic({ name, path = ``, bundle = ``, loop = true, isRelease = false, onEnd = null, clip = null }) { //console.log("开始播放音乐:" + name, this._curent_music, this._isPause); if (this._isPause || this.MuteMusic) return; if (this._music && this._music.playing && name == this._curent_music) return; this.stopMusic(); this._curent_music = name; if (clip != null) this._music.clip = clip; else { const rec = await this.getClip(bundle, path, name, isRelease); if (!rec?.clip) return; this._music.clip = rec.clip; } this._music.loop = loop; this._music.volume = this.MusicVolume; this._music.play(); } stopMusic() { this._curent_music = ""; if (this._music) { this._music.stop(); this._music.clip = null; } } private _PlayingEffects: AudioRecord[] = []; /** * 播放音效 * @param param */ async playEffect({ name, path = ``, bundle = ``, isRelease = true, onEnd = null, clip = null, finishRelease = false, stopBefore = false }) { if (this._isPause || this.MuteEffect) { let t = 2; if (clip != null) t = clip.getDuration(); if (onEnd) setTimeout(() => { onEnd(); }, t * 1000); return; } if (!BattlePerformance.shouldPlayWeaponSfx(path, name)) { if (onEnd) setTimeout(() => { onEnd(); }, 0); return; } let a: AudioRecord = null; if (clip == null) { a = await this.getClip(bundle, path, name, isRelease); } else { a = new AudioRecord(); a.clip = clip; a.bundle = bundle; a.path = path; a.name = name; a.key = `${bundle}_${path}_${name}`; a.isRelease = isRelease; } if (stopBefore) { let arr = this._PlayingEffects.filter(v => v.key == a.key); for (let i = 0; i < arr.length; i++) { arr[i].isPlaying = false; let index = this._PlayingEffects.indexOf(arr[i]) if (index > -1 && arr[i] != a) { this._PlayingEffects.splice(index, 1); if (arr[i].clip) { arr[i].clip.decRef(); arr[i].clip.destroy(); arr[i].clip = null; } } } } if (!a.clip) { if (onEnd) { setTimeout(() => { onEnd(); }, 2 * 1000); } return a; } this._effect.playOneShot(a.clip, this.EffectVolume); let len = a.clip.getDuration(); a.isPlaying = true; this._PlayingEffects.push(a); setTimeout(() => { a.isPlaying = false; let index = this._PlayingEffects.indexOf(a); if (index > -1) { this._PlayingEffects.splice(index, 1); } if (finishRelease) { if (a && a.clip) { a.clip.decRef(); a.clip = null; } if (this._clip_map.has(a.key)) this._clip_map.delete(a.key); } if (onEnd) onEnd(); }, len * 1000); return a; } /** * 播放点击音效 */ playClick() { if (this._click_name != "") this.playEffect({ name: this._click_name }); } /** * 当音效播放结束 * @param e */ onAudioEnded(e) { if (e["onSoundEnd"]) { e.onSoundEnd(); e.onSoundEnd = null; } } private async getClip(bundle, path, audioName, isRelease) { let b = bundle == `` ? this._bundle : bundle; let p = path == `` ? this._path : path; let k = `${b}_${p}_${audioName}`; let a: AudioRecord = this._clip_map.get(k); if (!a) { a = new AudioRecord(); this._clip_map.set(k, a); } a.bundle = b; a.path = p; a.name = audioName; a.key = k; a.isRelease = isRelease; if (!a.clip) { let loading = this._clip_loading.get(k); if (!loading) { loading = this.loadClip(b, p + audioName); this._clip_loading.set(k, loading); loading.finally(() => this._clip_loading.delete(k)); } a.clip = await loading; if (!a.clip) { console.warn(`[Sound] 音效加载失败: ${k}`); return a; } a.clip.addRef(); } return a; } private async loadClip(bundleName, assetPath): Promise { let bundle = assetManager.getBundle(bundleName); if (!bundle) bundle = await this.loadBundle(bundleName); if (!bundle) return null; return (await this.loadRes(bundle, assetPath)) as AudioClip | null; } private async loadBundle(bundleName) { return new Promise((resolve, reject) => { assetManager.loadBundle(bundleName, (err, bundle) => { if (err) { console.log(`加载Bundle:${bundleName}错误`, err); resolve(null); } else { resolve(bundle); } }) }) } private async loadRes(bundle: AssetManager.Bundle, assetPath) { return new Promise((resolve, reject) => { bundle.load(assetPath, AudioClip, (err, asset) => { if (err) { console.log(`加载Bundle:${bundle.name},path:${assetPath}资源错误`, err); resolve(null); } else { //asset.addRef(); resolve(asset); } }) }) } } class AudioRecord { bundle: string = ``; path: string = ``; name: string = ``; key: string = ``; clip: AudioClip = null; isRelease: boolean = true; isPlaying: boolean = false; }