背景
在3d游戏中,合理搭配音效元素,可以显著提升游戏沉浸感。利用双通道管线和自定义音频混合创建具有3d空间效果的音频,已经被用于大部分的3d游戏场景,尤其是fps类游戏,开发者在场景中添加具有3d效果的脚步声、枪声,玩家即可借助专业的耳机设备,可以通过3d空间音效做到“听声辨位”。从而丰富游戏的玩法。
本篇教程用于引导大家如何在cocos中使用浏览器自带AudioContext api
实现3d音效。官方文档参见: Web audio spatialization basics - Web APIs | MDN (mozilla.org)
AudioContext (上下文)
音频主体结构的上下文,表示由连接在一起的音频模块构建的音频处理图,其实类似于我们在游戏开发过程中编辑器自带的那种“节点编辑器”或者叫“路由图”,比如blender的着色器编辑器,cocos creater的动画状态机编辑器。只是这里没有可视化。在代码中,我们可以通过aNode.connect(bNode)
的方式来连接所有的音效节点,来达成类似于混合和编排的效果。(如图所示)
创建AudioContext的代码:
this.ac = new AudioContext();
Source(音频源)
在cocos中,我们推荐以buffer的形式获取音频源,因为这不会创建html元素,并且我们可以利用cocos的audioClip属性,快速拿到我们资产库中的音频源audiobuffer数据;
代码:
@property({
type: AudioClip,
displayName: "音频源",
})
audioClip: AudioClip;
//...略
this.source = this.ac.createBufferSource();
this.source.buffer = this.audioClip._player._player._audioBuffer;
this.source.start();
AudioListener(收听者)
类似于我们场景中的相机,排除一些特殊情况(如renderTexture、shader、自定义屏幕后效),大部分情况下我们的场景中仅有一台激活的相机,listener也是如此。
this.listener = this.ac.listener;
和相机一样,我们可以对listener
设置位置和方向:(请注意,这里传给setOrientation
的是cocos节点的forward
和up
向量);一般情况下,这里的listenerNode
可以传相机的节点。对应的就是以相机的视角为listener
的坐标计算空间音频的声道和衰减模型;
@property({
type: Node,
displayName: "收听者",
})
listenerNode: Node;
//...
this.listener.setOrientation(
this.listenerNode.forward.x || 0,
this.listenerNode.forward.y || 0,
this.listenerNode.forward.z || 0,
this.listenerNode.up.x || 0,
this.listenerNode.up.y || 1,
this.listenerNode.up.z || 0
);
this.listener.setPosition(
this.listenerNode.worldPosition.x || 0,
this.listenerNode.worldPosition.y || 0,
this.listenerNode.worldPosition.z || 0
);
PannerNode (发声节点)
与listener
对应,panner
对应“发声者”在3d空间中的对象,比如一个喇叭、脚步、枪声。常用的参数我们可以设置panner
的位置(position
)、最大距离( maxDistance
)等等内容,代码如下:
@property({
type: Node,
displayName: "发声者",
})
pannerNode: Node;
//...略
this.panner = this.ac.createPanner();
this.panner.panningModel = "equalpower"; // 音频空间化算法模型
this.panner.distanceModel = "linear"; // 远离时的音量衰减算法
this.panner.maxDistance = this.maxDistance; // 最大距离
this.panner.refDistance = 5; // 开始衰减的参考距离
this.panner.rolloffFactor = 3; // 衰减速度
this.panner.coneInnerAngle = 360; // 声音360度扩散
this.panner.orientationX.value = 1; // 声源朝向x分量
this.panner.orientationY.value = 0;
this.panner.orientationZ.value = 0;
this.panner.setPosition(
this.pannerNode.worldPosition.x,
this.pannerNode.worldPosition.y,
this.pannerNode.worldPosition.z
);
GainNode(增益节点)
还记得文章前面说的吗?AudioContext
是一个类似于“路由图”的集合,那这里我们就增加一个GainNode
(增益节点),增益是一个无单位的值,会对所有输入声道的音频进行相应的增加(相乘)。此处我们用于修改整体音频的音量大小。gain
修改的效果会影响到panner
的结果,举个例子:比如收音机的音量,如果音量很小,那么离太远就听不清了,反之音量很大,那离得很远也能听到。
代码如下:
this.gainNode = this.ac.createGain();
//修改音量
this.gainNode.gain.value = 0.2;
connect(连接)
把我们创建的节点连接起来,用connect方法;(还记得吗?路由图)
this.gainNode.connect(this.ac.destination);
this.gainNode.connect(this.panner);
this.source.connect(this.panner);
this.panner.connect(this.gainNode);
onDestroy的时候
由于我们是手动创建的音频上下文节点,所以cocos并不会在销毁组件的时候自动释放这些音频实例,这可能导致我们已经切换场景了,但上一个场景的音频还在继续播放,要解决这个问题,我们只需要在组件销毁的生命周期钩子函数中手动释放我们创建的音频实例即可;
代码如下:
onDestroy() {
try {
this.gainNode.disconnect(this.ac.destination);
this.gainNode.disconnect(this.panner);
this.source.disconnect(this.panner);
this.panner.disconnect(this.gainNode);
this.source.stop();
this.ac.close();
} catch (e) {
console.log(e);
}
}
大功告成
以上就是在cocos中应用3d音效的全部内容,欢迎各位大佬查漏补缺,新人一枚,如文章中存在错误,请在本帖中回复。希望这篇教程能对您的工作提供帮助。
源码附件和参数:
audio3DComponent.zip (1.3 KB)
题外话
1.这里有一个我在3d空间音频在坦克对战(3D)项目中的应用,这里是视频效果的传送门: Cocos坦克-引擎3D空间音效_网络游戏热门视频 (bilibili.com);
2.如对物理载具的绑定感兴趣,我也做了一个ammojs物理载具绑定系统,原价40元,现价2元: Cocos Store