最近在用 Cocos Creator 3.8 制作一个 3D 联机坦克大战游戏。因为项目需要,在 Cocos Creator 中实现了 3D 空间音效的方案,在此分享给大家,希望能对大家有所帮助。
作者:蔬菜土豆泥,前端工程师。
来源:Cocos
原文:https://mp.weixin.qq.com/s/6fpQGhfVFySOkMx7Q4Tgig
背景
在 3D 游戏中,合理搭配音效元素,可以显著提升游戏沉浸感。利用双通道管线和自定义音频混合创建具有 3D 空间效果的音频,已经被用于大部分的 3D 游戏场景,尤其是 FPS 类游戏。
在场景中添加具有 3D 效果的脚步声、枪声,玩家即可借助专业的耳机设备,通过 3D 空间音效做到“听声辨位”,从而丰富游戏的玩法。
本篇教程用于引导大家如何在 Cocos 中使用浏览器自带 AudioContext api
实现 3D 音效。
详情请参考 MDN 文档:
- https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Web_audio_spatialization_basics
AudioContext
音频上下文,是一张节点图,将音频模块节点连接在一起,构成音频处理流程图。类似于 Shader Graph,动画图,如下图所示:
![音频上下文](https://www.nxrte.com/wp-content/themes/justnews/themer/assets/images/lazy.png)
这里并没有提供可视化工具,但在代码中,我们可以通过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();
Listener(监听者)
在游戏开发中,摄像机是眼睛,那么 Listener 就类似于耳朵。大部分情况下我们的场景中仅有一个激活的 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);
销毁
由于我们是手动创建的音频上下文节点,所以 Cocos 并不会在销毁组件的时候自动释放这些音频实例,这可能导致我们已经切换场景了,但上一个场景的音频还在继续播放。
要解决这个问题,我们只需要在组件销毁的生命周期钩子函数 onDestroy 中手动释放我们创建的音频实例即可。
代码如下:
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);
}
}
源码获取:
前往:https://forum.cocos.org/t/topic/156994,可在文中找到本文源码。
以上就是在 Cocos 中应用 3D 音效的全部内容,欢迎各位大佬查漏补缺,评论区探讨交流。希望这篇教程能对您的工作提供帮助。
版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。