import { _decorator,AudioClip, AudioSource} from 'cc'; import { Singleton } from './Singleton'; import { bundleMgr } from './BundleManager'; import { Constants } from '../../data/Constants'; import { settingData } from '../../data/SettingData'; import { Logger } from '../../extend/Logger'; import { Utils } from '../../utils/Utils'; const { ccclass, property } = _decorator; /**音乐播放数据结构*/ export interface IAudioData { clip: AudioClip;//音乐片段 audioSource: AudioSource;//音乐具柄 path: string //音乐名称路径 playStatus: boolean //播放状态 totalTime: number;//音乐总长度时间 time_id: number;//定时器句柄 t: number;//统计记时 } @ccclass('AudioManager') class AudioManager extends Singleton{ //缓存的音乐 private cachedAudioArr: Array = []; /** * 预加载音乐 * 这儿前面 bundleManager资源包已经 loadLaunchBundles加载过了 * 可以直接 再次 */ public async initialize(): Promise { const audioAssets = Object.entries(Constants.audios) .map(([key, path]) => ({ path: path, type: AudioClip })) .filter(asset => !!asset.path); await bundleMgr.loadMultipleAssets(Constants.bundleName.audios, audioAssets, (progress) => {/*进度条0~1*/}, (err:Error, clip: AudioClip, path) => { if(err) { Logger.error("加载资源失败:", err); }else{ this.cachedAudioArr.push({ clip: clip, audioSource: null, path: path, playStatus: false, totalTime: clip.getDuration(), time_id: -1, t: 0 }); } } ); } /** * 播放音效 不会两个相同的音乐重复播放 可以停止 * @param path 播放音乐的路径 * @param loop 是否循环播放 * @param s 如果为-1则最大音量立即播放 如果为其他则慢慢音量递增起来播放 * @returns */ private bgVolumeMax: number = 0.8; public async play(path: string,loop: boolean = false,s: number = -1): Promise { if(Utils.isNull(path) ||!settingData.data.bgMusic)return; const fun = (audio:IAudioData)=>{ if(audio.audioSource == null){ audio.audioSource = this.audioSourceLoop(loop); } if(audio.playStatus)return; audio.audioSource.clip = audio.clip; if(audio.time_id){ clearInterval(audio.time_id); if(audio.audioSource.playing){ audio.audioSource.pause(); } } audio.audioSource.play(); if(s == -1){ audio.audioSource.volume = this.bgVolumeMax; }else{ audio.t = 0; audio.audioSource.volume = 0; let interval: number = ((1 / s) / 10); audio.time_id = setInterval(()=>{ audio.t += interval; let volume: number = audio.t / s; if(volume >= this.bgVolumeMax){ clearInterval(audio.time_id); }else{ audio.audioSource.volume = volume; } },interval * 1000); } audio.playStatus = true; } let a: IAudioData = this.cachedAudioArr.find(e =>e.path == path); if(a){ fun(a); }else{ await bundleMgr.loadAsset(Constants.bundleName.audios,path, AudioClip,(err: Error, clip: AudioClip) => { if(err) { Logger.error("加载资源失败:", err); }else{ let data:IAudioData = { path: path, clip: clip, audioSource: null, playStatus: false, totalTime: clip.getDuration(), time_id: -1, t: 0 }; this.cachedAudioArr.push(data); fun(data); } }); } } /** * 单次播放一次的音效 可以停止 * @param path 音乐路径 * @param overlap 是否可以重复两个同一个音效连续播放 * @returns */ public async playOneShot(path: string,overlap: boolean = true): Promise { if(Utils.isNull(path) ||!settingData.data.soundFx)return; const fun = (audio:IAudioData)=>{ if(overlap || !audio.playStatus){ if(audio.audioSource == null){ audio.audioSource = this.audioSourceLoop(false); } audio.audioSource.clip = audio.clip; audio.audioSource.play(); audio.audioSource.volume = 1; audio.playStatus = true; setTimeout(()=>{ audio.playStatus = false; },audio.totalTime * 1000) } } let a: IAudioData = this.cachedAudioArr.find(e =>e.path == path); if(a){ fun(a); }else{ await bundleMgr.loadAsset(Constants.bundleName.audios,path, AudioClip,(err: Error, clip: AudioClip) => { if(err) { Logger.error("加载资源失败:", err); }else{ let data:IAudioData = { path: path, clip: clip, audioSource: null, playStatus: false, totalTime: clip.getDuration(), time_id: -1, t: 0 }; this.cachedAudioArr.push(data); fun(data); } }); } } /** * 停止播放音效 * @param path 音效路径名称 * @param s 为-1立即停止播放 非-1则是慢慢的声音越来越小的停止播放 * @returns */ public stop(path: string,s: number = -1): void { if(Utils.isNull(path))return; let audio: IAudioData = this.cachedAudioArr.find(e =>e.path == path); if(audio){ if(!audio.audioSource)return; if(!audio.audioSource.playing)return; if(audio.time_id){ clearInterval(audio.time_id); } let audioSource = audio.audioSource; if(s == -1){ audioSource?.pause(); }else{ audio.t = 0; let interval: number = ((1 / s) / 10); audio.time_id = setInterval(()=>{ if(audioSource){ audio.t += interval; let volume: number = this.bgVolumeMax - audio.t / s; if(volume <= 0){ clearInterval(audio.time_id); audioSource?.pause(); }else{ audioSource.volume = volume <= 0 ? 0 :volume; } } },interval * 1000); } audio.playStatus = false; } } /** * 加载音效 * @param loop 是否循环 */ public audioSourceLoop(loop: boolean): AudioSource{ let audioSource: AudioSource = null; if(loop){//播放单次的时候具柄 audioSource = this.getAudioSource(null,true,this.bgVolumeMax) }else{//循环播放的时候具柄 audioSource = this.getAudioSource(null,false,1); } return audioSource; } /** * 得到一个播放音乐的控制类 * @param clip 音效片段 * @param loop 是否循环 * @param volume 音量 * @returns 播放类 */ public getAudioSource(clip: AudioClip = null, loop: boolean = false, volume: number = 1): AudioSource{ let audioSource: AudioSource = new AudioSource(); if(clip){ audioSource.clip = clip; } audioSource.loop = loop; audioSource.volume = volume; return audioSource; } } //全局单例 export const audioMgr = AudioManager.ins();