问题产生:
1、cocos creator的AudioSource,如果使用.Play()方法,微信端每次都会生成一个innerAudioContext实例。Cocos对这个实例是有规定最大播放数量限制,可以使用AudioSource.maxAudioChannel获取。
2、打包微信端最大播放限制10例,html5是24例,原生app是32。这些都是cocos自己规定的,使用cocos的音频接口就无法绕过。一旦超出就会自动停止之前的播放,造成意外停止,对音乐影响甚大。
解决:
1、可以用一个AudioSource来使用.PlayOneShot()方法播短音效,不会列入计数,代价是播出后的音频不受控。极简单的项目无所谓。
2、对于声音设计师,如果不能容忍播出后无法控制的情况,毕竟很多音频逻辑的构建都是基于播出后可控。可以从不超量的播放来解决,并且大多数游戏都是需要人为限制播放量,避免声音嘈杂。
2.1 音频配表增加限制、权重参数
这是我设计的一份音频配表,是否限制重复播放栏:0不限制,1不播新的,2停旧播新(默认1),基本不会配0。权重栏:1-100(默认50)。
2.2 限制重复播放
// 计数,以及是否限制重复
if (this.playCountMap.has(audioName)) {
if (limit == 1 && this.playCountMap.get(audioName)! > 0) {
this.Log(`限制重复 ${audioName} 不播出!`);
return;
} else if (limit == 2 && this.playCountMap.get(audioName)! > 0) {
this.Log(`限制重复 ${audioName} 停旧播新!`);
for (let handler of this.playingPool) {
// 实际上是旧的进度拉到0,不新增播放器
if (handler.name === audioName) {
if (handler.isStopping) {
handler.isStopping = false;
}
handler.fadeTween?.stop();
let as = handler.audioNode.getComponent(AudioSource)!;
if(handler.delayCall){
as.unschedule(handler.delayCall);
handler.delayCall = null;
}
// 进度回到起始
as.currentTime = 0;
// 重新计时停止和回收
handler.delayCall = () => {
this.stopSound(handler);
}
as.scheduleOnce(handler.delayCall, as.duration);
as.volume = PlaySettingM.getSoundVolume();
break;
}
}
return;
}
} else {
this.playCountMap.set(audioName, 0);
}
在音频管理器的播放方法内,判断是否发生重复,再根据配表执行相应操作。一般的技能音效适合不播新的,按钮类、十连抽爆奖类适合停旧播新。我的停旧播新逻辑,实际上是不新增播放,只是把旧的播放进度拉回0。
有限制重复加持,就比较不容易超出播放量。也不容易发生大量叠加播放,造成听觉上的灾难。
**可以用一个AudioSource来使用.PlayOneShot()方法播短音效(配置“不限制重复”或者“不播新”时),其他的AudioSource仍然使用play方法来播放音乐、循环音效、配置“停旧播新”的短音效。
2.3 发生超量时,权重系统起作用
/**
* 检查播放数量
* @param weight 新一例播放的权重
*/
private checkPlayCount(weight: number) {
if (this.playingPool.length >= this.maxPlayCount) {
this.playingPool.sort((a, b) => a.weight - b.weight);
if (weight > this.playingPool[0].weight) {
let as = this.playingPool[0].audioNode.getComponent(AudioSource)!;
if (as.loop && this.playingPool[0] != this.curBGMPlayer) {
this.stopLoopSound(this.playingPool[0].name);
} else {
this.stopSound(this.playingPool[0]);
}
} else {
return false;
}
}
return true;
}
超量时,播出列表按权重升序。来判断究竟是停掉最低权重的播放器,还是不播最低权重的新实例。
**播完时,需要对播放器的audioSource执行.stop(),才会销毁该innerAudioContext实例。播循环音频时,一定会手动停止,那时去停止即可。单遍短音效需要tween来计时停止。
3、确实想在微信端突破最大播放数量限制,构造更复杂的播放功能时。可以改为直接调用微信的InnerAudio或者webAudio。即抛弃cocos的音频接口。
这样超量有可能会增加小程序的性能负担,另外其他平台如原生app、html5还是需要使用cocos音频接口或者它们的原生接口。