游戏的音频在很大的程度上可以影响游戏体验,所以在游戏开发中我们不得忽视音频开发的重要性。例如休闲小游戏《Rocket Plume》中,玩家利用火箭喷出的像素流来清理岩石,清理岩石的频繁音效一旦修饰不佳,容易让人感觉枯燥无味,从而带来不好的体验。

本文将由这款休闲小游戏的作者Joe Strout为大家分享他们在音频开发时遇到的问题和解决方案。下面是Joe Strout带来的分享内容。

音频开发问题
在开发《Rocket Plume》的过程中,音频方面主要遇到了两大难题。

第一个问题和火箭引擎发出的声音有关。在玩家游戏的大部分时间里引擎都是启动状态,无论怎样尝试着循环音频,玩家都能分辨出这是一段被反复播放的音频。这就会让游戏的声音听起来不太自然,分散玩家在游戏上的注意力。

第二个问题,也是最难搞定的问题,和游戏机制有关。在这款游戏的大部分时间里,玩家利用火箭喷出的像素流来清理岩石,且游戏中会非常频繁地重复这一过程,有时甚至会在一帧中摧毁多个像素的岩石。即便是用非常短的数字化声音,每摧毁一个像素就播放一次的话也会非常抓耳,让人感到枯燥乏味。我们也尝试了声音的各种修饰,使用随机声调和音量等,但效果仍然不理想。

程序化音频
对于《Rocket Plume》音频开发的两大难题,可行的解决方案就是程序化音频(Procedural Audio)。这是一种根据需求使用代码生成声音波形的方法。利用程序化音频能够使创造出永不重复的音频成为可能,同样也可以对游戏内发生的事情产生及时的反馈。

幸运的是,Unity支持直接在音频处理流中插入我们的代码,这一鲜为人知但简单易用的方法使得程序化音频实现非常便捷。
 



Unity中的声音来源于Audio Source组件,并通过GameObject上的一个或多个Audio Filter组件修饰。这些滤波器组件按照组件列表中的顺序应用到声音中。Unity内置的滤波器组件包含高通及低通滤波器、回声(Echo)、畸变(Distortion)、混响(Reverb)和合唱(Chorus)效果。

如何创建程序化音频
创建程序化音频,只需新建一个MonoBehaviour子类,并实现OnAudioFilterRead方法。这样就实现了一个自定义音频滤波器。您可以根据喜好在音频处理链中加入滤波器。需要特别说明的是,要想凭空创造出声音,必须将您的音频滤波器放在音频处理栈中的音源组件之后、,所有其它音频滤波器之前。如果Audio Source组件不包含任何音频组件,该组件就会向滤波器链中直接输入零值,也就是说没有声音,而这就是依靠代码发声的原理。

多说无益,下面来看一些例子。

案例一
首先来解决第一个问题:引擎的噪音。和很多自然界中的声音一样,引擎噪音本质上就是被某些滤波器修饰后的白噪音——即完全随机的声波。因此第一个程序化音频脚本非常简单,它基本上就是用来生成白噪音的。
 

[C#] 纯文本查看 复制代码
using UnityEngine;
public class EngineAudio : MonoBehaviour {
     
    [Range(-1f, 1f)]
    public float offset;
     
    System.Random rand = new System.Random();
     
    void OnAudioFilterRead(float[] data, int channels) {
        for (int i = 0; i < data.Length; i++) {
            data = (float)(rand.NextDouble() * 2.0 - 1.0 + offset);
        }
    }
}



神奇的OnAudioFilterRead方法的接收参数是一个浮点数数组data和正在使用的频道数量channels。数组表示波形,以交叉格式存储:首先是各个频道的第一个样本,然后是各个频道的第二个样本,以此类推。该方法会被频繁调用(差不多是每20毫秒一次),同时还需要适当大小的数据缓存。每个数据采样的取值范围是-1到1,不在范围内的数值均忽略不计。

由于是在所有的频道生成白噪音,因此不需要关注这些细节,只需在每次采样时向缓冲区中填一个随机数就好了。这里对白噪音做了一点点偏移处理,也算是一种非常简单的音频滤波器,这样做可以避免采样的某些片段越界。

将该脚本添加到一个带有AudioSource组件的GameObject上,然后运行游戏,您会听到刺耳的“静电声”。适当调整偏移量,可以让声音缓和一点。但我们并不想让玩家听到这样的声音,还需要添加更多的音频滤波器。我们还使用了音频低通滤波器,将截止频率(cutoff frequency)设置为800左右,Q参数设置为1。接着使用音频失真滤波器,并将失真水平(distortion level)设置为0.5。这样就得到了一种平滑丰富且听起来非常像引擎的声音。因为这种声音是即时生成的,所以它不会让人感到重复单调。

注意,以上代码是简单的范例,实际上《Rocket Plume》游戏中不是直接使用。因为直接打开或关闭(例如启用或禁用这个GameObject)这个引擎噪音带来的体验不是太好,对比引擎关闭后的寂静,引擎再次开启后简直震耳欲聋,尤其是在玩家关掉了背景音乐的情况下。

最后我们决定不完全关闭引擎的噪音,而是在引擎“空闲”时使用一种音量很低的背景音。测试过一些音频滤波器后,我们发现减少低通滤波频率可以达到这种效果,然后将这种机制与引擎的开关挂钩。我们对此有过一些争论,即这些脚本是否和白噪音生成器属于相同类型的程序,不过最后我们还是决定把所有引擎相关的代码保存到一起。

最终的脚本如下所示:

[C#] 纯文本查看 复制代码
 

锐亚教育