Cocos2.X开发的小游戏,在淘宝环境运行时使用cc.audioEngine播放音频和音效,无法正常使用uncache/uncacheAll销毁音频实例。依赖音效较多时,会有较大概率连续报错(见下图),造成游戏卡顿的客诉。
使用以下《音频管理器》章节的代码片段作为基础,自行实现单例音频管理器,替换AudioEngine使用。
const interval = 180;
const preload = 5;
export class SoundMgr {
/**
* 音乐和单次音效播放
*/
private _audioComp: cc.AudioSource = new cc.AudioSource();
/**
* 循环音效播放
*/
private _audioPool: cc.AudioSource[] = [];
private _clipTime: Map<cc.AudioClip, number> = new Map()
/**
用于挂载的组件
*/
private _soundNode = null;
constructor() {
this._soundNode = new cc.Node();
cc.game.addPersistRootNode(this._soundNode);
for (let i = 0; i < preload; i++) {
const audio = this._soundNode.addComponent(cc.AudioSource);
this._audioPool.push(audio);
}
}
public play(audioClip: cc.AudioClip, isLoop: boolean = true) {
let clip = audioClip;
if (!clip) return;
this._audioComp.clip = clip;
this._audioComp.loop = isLoop;
this._audioComp.play();
}
public stop() {
this._audioComp.stop();
}
public setVolume(volume: number) {
this._audioComp.volume = volume;
}
private getAudio(): cc.AudioSource {
let audio: cc.AudioSource
if (this._audioPool.length === 0) {
audio = this._soundNode.addComponent(cc.AudioSource);
} else {
audio = this._audioPool.pop();
}
return audio;
}
private putAudio(audio: cc.AudioSource) {
// @ts-ignore
audio.clip.src = null;
this._audioPool.push(audio);
}
/**
* @en : play effect once
* @zh : 播放一次特效
* @param {string} audioClip
* @param {*} volume
*/
public playEffect(audioClip: cc.AudioClip, volume = 0.5) {
let clip = audioClip;
if (!clip) return;
const lastTime = this._clipTime.get(clip) || 0;
const currentTime = cc.director.getTotalTime();
/* if interval is too low, return it for performance */
if ((currentTime - lastTime) < interval) {
return;
}
this._clipTime.set(clip, currentTime);
const audio = this.getAudio();
audio.clip = clip;
audio.play();
const audioDuration = audio.getDuration();
setTimeout(() => {
if (this._audioPool.length >= preload) {
audio.destroy();
} else {
this.putAudio(audio);
}
}, audioDuration * 1000)
}
}
const SOUND = new SoundMgr();
export default SOUND;
import SOUND from "../frame/SoundMgr";
import RES from "../frame/ResMgr";
const { ccclass, property } = cc._decorator;
@ccclass
export default class play extends cc.Component {
@property(cc.Slider)
volume: cc.Slider = null;
protected onEnable(): void {
this.volume.node.on('slide', this.callback, this);
}
protected onDisable(): void {
this.volume.node.off('slide', this.callback, this);
}
callback(slider:cc.Slider) {
SOUND.setVolume(slider.progress);
}
playBgm() {
const clip = RES.getClip("bgm");
SOUND.play(clip,0.65);
this.volume.progress = 0.65;
}
stopBgm() {
SOUND.stop();
}
playEffect() {
const clip = RES.getClip("chick");
SOUND.playEffect(clip);
}
playEffect1() {
const clip = RES.getClip("btn");
SOUND.playEffect(clip);
}
}