import { assetManager, AudioClip, AudioSource, director, Node } from "cc"; const ch_log = chsdk.log; const ch_storage = chsdk.storage; /**音频等资源加载方式*/ export enum loadType { /**未知*/ none = 0, /**bundle*/ bundle = 1, /**远程*/ remote = 2, } /**音频播放控制 * 需要初始化资源加载方式 * loadType.bundle 时需要设置 bundle名 * loadType.remote 测试使用远程的音频需要设置远程地址 */ export default class ch_audio { private static _instance: ch_audio; public static getInstance(): ch_audio { if (!this._instance) this._instance = new ch_audio(); return this._instance; } private _volume_music: number = 1; private _volume_effect: number = 1; private _switch_music: boolean = true; private _switch_effect: boolean = true; private readonly _effect_max: number = 5; private _effect_index: number = 0; private _effect_source_pool: AudioSource[] = []; private _music_source: AudioSource; private _load_type: loadType = loadType.none; private _bundle_name: string; private _remote_url: string; private _playing_sound: Set = new Set(); constructor() { const audio = new Node(); audio.name = '__ch_audio__'; director.getScene().addChild(audio); director.addPersistRootNode(audio); this._music_source = this._create(audio); for (let i = 0; i < this._effect_max; i++) { this._effect_source_pool.push(this._create(audio)); } this.load(); } /** * 创建音频源 * @param node 节点 * @param volume 音量 * @returns AudioSource 音频源组件 */ private _create(node: Node): AudioSource { const source = node.addComponent(AudioSource); source.loop = false; source.playOnAwake = false; source.volume = 0.5; return source; } /**初始化*/ init(load_type: loadType, bundle_name: string, remote_url: string): void { this._load_type = load_type; this._bundle_name = bundle_name; this._remote_url = remote_url; } /**切换bundle*/ set_bundle_name(bundle_name: string): void { this._bundle_name = bundle_name; } /** * 释放通过 [[load]] 或者 [[loadDir]] 加载的声音资源。 * @param sound 声音资源路径 */ release(sound?: string): void { if (this._load_type == loadType.none) { ch_log.warn('音频模块未初始化'); } else if (this._load_type == loadType.bundle) { const bundle = assetManager.getBundle(this._bundle_name); if (!sound) { bundle.releaseAll(); } else { bundle.release(sound, AudioClip); } } } /** 保存音乐音效的音量、开关配置数据到本地 */ save() { const local_data: any = {}; local_data.volume_music = this._volume_music; local_data.volume_effect = this._volume_effect; local_data.switch_music = this._switch_music; local_data.switch_effect = this._switch_effect; ch_storage.set("ch_audio", local_data); } /** 本地加载音乐音效的音量、开关配置数据并设置到游戏中 */ load() { const local_data = ch_storage.getObject("ch_audio"); if (local_data) { try { this.setState(local_data); } catch (e) { this.setStateDefault(); } } else { this.setStateDefault(); } } private setState(local_data: { volume_music: number, volume_effect: number, switch_music: boolean, switch_effect: boolean }) { this.volumeMusic = local_data.volume_music; this.volumeEffect = local_data.volume_effect; this.switchMusic = local_data.switch_music; this.switchEffect = local_data.switch_effect; } private setStateDefault() { this.volumeMusic = 0.8; this.volumeEffect = 0.8; this.switchMusic = true; this.switchEffect = true; } /** * 获取背景音乐音量 */ get volumeMusic(): number { return this._volume_music; } /** * 设置背景音乐音量 * @param value 音乐音量值 */ set volumeMusic(value: number) { this._volume_music = value; this._music_source.volume = value; } /** * 获取背景音乐开关值 */ get switchMusic(): boolean { return this._switch_music; } /** * 设置背景音乐开关值 * @param value 开关值 */ set switchMusic(value: boolean) { this._switch_music = value; if (value == false) this._music_source.stop(); } /** * 获取音效音量 */ get volumeEffect(): number { return this._volume_effect; } /** * 设置获取音效音量 * @param value 音效音量值 */ set volumeEffect(value: number) { this._volume_effect = value; for (let i = 0; i < this._effect_source_pool.length; i++) { this._effect_source_pool[i].volume = this._volume_effect; } } /** * 获取音效开关值 */ get switchEffect(): boolean { return this._switch_effect; } /** * 设置音效开关值 * @param value 音效开关值 */ set switchEffect(value: boolean) { this._switch_effect = value; if (value == false) { for (let i = 0; i < this._effect_source_pool.length; i++) { this._effect_source_pool[i].stop(); } } } /** * @en * play short audio, such as strikes,explosions * @zh * 播放短音频,比如 打击音效,爆炸音效等 * @param sound clip or url for the audio * @param interval 同名字音频限制播放间隔(毫秒) (默认:0不限制 特殊系数:>0 <=1 使用音频时间X此系数) */ playOneShot(sound: AudioClip | string, interval: number = 0, remote_ext: string = '.mp3') { if (!this._switch_effect) return; if (sound instanceof AudioClip) { this.doPlayOneShot(sound, interval); } else { if (this._load_type == loadType.none) { ch_log.warn('音频模块未初始化'); } else if (this._load_type == loadType.bundle) { const bundle = assetManager.getBundle(this._bundle_name); if (!bundle) { ch_log.warn(`请确保 bundle${this._bundle_name} 已加载`); } else { bundle.load(sound, (err, clip: AudioClip) => { if (err) { ch_log.error(err); } else { this.doPlayOneShot(clip, interval); } }); } } else if (this._load_type == loadType.remote) { assetManager.loadRemote(this._remote_url + sound + remote_ext, (err: Error | null, clip: AudioClip) => { if (err) { ch_log.error(err); } else { this.doPlayOneShot(clip, interval); } }); } } } private doPlayOneShot(clip: AudioClip, interval: number): void { const name: string = clip.name; if (interval > 0) { if (this._playing_sound.has(name)) return; this._playing_sound.add(name); const time = interval <= 1 ? clip.getDuration() * interval * 1000 : interval; setTimeout(() => { this._playing_sound.delete(name); }, time); this.getNextEffectSource().playOneShot(clip, this._volume_effect); } else { this.getNextEffectSource().playOneShot(clip, this._volume_effect); } } private getNextEffectSource(): AudioSource { const source = this._effect_source_pool[this._effect_index]; this._effect_index = (this._effect_index + 1) % this._effect_max; return source; } /** * @en * play long audio, such as the bg music * @zh * 播放长音频,比如 背景音乐 * @param sound clip or url for the sound */ play(sound: AudioClip | string, remote_ext: string = '.mp3') { if (!this._switch_music) return; if (sound instanceof AudioClip) { this._music_source.loop = true; this._music_source.stop(); this._music_source.clip = sound; this._music_source.play(); this._music_source.volume = this._volume_music; } else { if (this._load_type == loadType.none) { ch_log.warn('音频模块未初始化'); } else if (this._load_type == loadType.bundle) { const bundle = assetManager.getBundle(this._bundle_name); if (!bundle) { ch_log.warn(`请确保 bundle${this._bundle_name} 已加载`); } else { bundle.load(sound, (err, clip: AudioClip) => { if (err) { ch_log.error(err); } else { this._music_source.loop = true; this._music_source.stop(); this._music_source.clip = clip; this._music_source.play(); this._music_source.volume = this._volume_music; } }); } } else if (this._load_type == loadType.remote) { assetManager.loadRemote(this._remote_url + sound + remote_ext, (err: Error | null, clip: AudioClip) => { if (err) { ch_log.error(err); } else { this._music_source.loop = true; this._music_source.stop(); this._music_source.clip = clip; this._music_source.play(); this._music_source.volume = this._volume_music; } }); } } } /** * stop the audio play */ stop() { this._music_source.stop(); for (let i = 0; i < this._effect_source_pool.length; i++) { this._effect_source_pool[i].stop(); } } /**stop and clean */ clean() { this._music_source.stop(); this._music_source.clip?.name this._music_source.clip = null; for (let i = 0; i < this._effect_source_pool.length; i++) { this._effect_source_pool[i].stop(); this._effect_source_pool[i].clip = null; } } /** * pause the audio play */ pause() { this._music_source.pause(); } /** * resume the audio play */ resume() { if (!this._switch_music) return; this._music_source.play(); } /** 重播当前音乐 */ public replay_music(): void { this._music_source.stop(); this._music_source.play(); } }