音乐对于游戏来说绝对是画龙点睛之笔,它可以对玩家的情绪和心理产生强有力的影响,对于氛围的营造和游戏的新鲜感、真实感起到了不可替代的作用。不过音乐的编写需要有专业的人员才可做到,特别是在什么场景下播放什么音乐的策划方案都有很高的艺术门槛。在本文中我们并不想讨论怎样制作一首音乐,而是简单了解一下,当我们手上已经有了合适的音乐,在程序中如何播放的问题。

对于制作小游戏来说,音乐主要分为两种:一种是播放时间较长的背景音乐,这种音乐可能会在游戏中一直循环播放;另一种是特效音乐,只在达到某种状态或者行为的情况下才会播放,例如打开一道门时播放的开门声,或者点击按钮产生的反馈声等等,这种声音一般较短,大多也只播放一遍。

我以前写过一篇文章提到过在 Windows 平台上播放声音的方法。在使用 SDL 编写程序的时候,如果你的程序仍然准备在 Windows 上运行,那么上述方法仍然有效,并且很简单,仅仅两个简单的函数调用而已。但是当你在其它平台,或者的音频文件格式不是普通的 WAV 、MP3 格式的时候,你可能需要使用 SDL 提供的声音播放方法。

原始的 SDL 库虽然可播放声音,但是需要开发者手动混合音频流,复杂程度简直爆表,个人非常不推荐新手使用,特别是你制作的游戏非常简单,音频不是特别重要的时候,推荐你直接使用 SDL 的扩展库 SDL_mixer

官方播放这里介绍两种方法,分别对应上文提到的背景音乐和特效音乐。不管什么声音方法,你都需要注意一点,确保你所使用的音频播放方法可以自动地将播放的音频混合到一起,而不是在播放背景音乐的时候,无法播放音效的尴尬场景。

1. 背景音乐的播放方法

先介绍播放背景音乐的方式,播放背景音乐可以使用 Windows 自带的多媒体播放函数 mciSendString,这个函数可以通过发送命令的形式来播放声音,并且支持 MP3 的格式。但是这个 API 有一个缺点,就是无法加载内存中的声音,至少目前我还没有找到具体的方法。和这个对应,SDL 有一个 Music 工作集,整个工作集包括很多函数,包括加载声音的 Mix_LoadMUS ,播放声音的 MixPlayMusic 等等。

这些函数都非常简单,这里介绍几个接口,首先是加载声音的:

Mix_Music *Mix_LoadMUS(const char *file)

这个函数的参数是一个音频文件,这里的音频文件支持的格式就比较多了,包括 WAVE、MOD、MIDI、OGG、MP3、FLAC 等等,当然在播放之前不要忘记和其它 SDL 库相似的初始化操作。

Mix_Init
Mix_Quit

这里就细说了,方法和初始化 SDL 基本类似。最后别忘记在不需要播放声音的时候,使用 Mix_FreeMUS 释放音频资源。

再有就是音频播放接口:

int Mix_PlayMusic(Mix_Music *music, int loops)

第一个参数是加载音乐返回的 Mix_Music 指针,第二个参数是播放的次数,如果为 -1,则一直重复播放。有个地方需要了解,当这个函数被重复调用的时候,并不会产生多个音频同时播放的效果,最终播放永远是最后播放的那个音频文件,并且总是从头开始播放。

2. 特效音乐的播放方法

特效音乐大多播放的时间比较短,并且需要和背景音乐叠加到一起,因为使用 Mix_PlayMusic 播放音乐总会只播放最后一个,所以在播放背景音乐的时候,不能再使用它播放音效文件,这里可以使用 Windows 自带的 PlaySound 方法。不过这个方法也有一个奇葩的地方,就是会不断地产生线程,如果你使用异步方法播放音频文件,它的内部为了不堵塞主线程,会以创建线程的方法进行播放,当播放完成后自动退出线程,这会导致你播放音效的时候,调试窗口产生大量的线程退出日志。当然对于普通的玩家可能是感觉不出来的,因为效率可以接受的,如果你不是强迫症患者其实也无需在意。

当然你还可以使用 SDL_Mixer 自带的另一种音频播放方式,这种方式就是使用 Mix_LoadWAV 和 Mix_PlayChannel 函数,不过在使用它们之前,你需要先手动使用 Mix_OpenAudio 打开音频设备:

int Mix_OpenAudio(int frequency, Uint16 format, int channels, int chunksize)

这个函数的几个参数都是音频相关的参数,这里推荐一种简单的模式,直接使用 SDL_mixer 提供的默认值即可:

Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, MIX_DEFAULT_CHANNELS, 2048);

具体这些参数的函数可以上官网自行查看。接下来介绍一下音效的加载,虽然 Mix_LoadWAV 在名称上看起来只能播放 WAV 文件,但是它其实是支持很多音频格式,包括 WAVE、AIFF、RIFF、OGG 以及 VOC 格式。函数的原型如下:

Mix_Chunk *Mix_LoadWAV(char *file)

和 Mix_LoadMUS 类似,一样是以文件形式进行加载,返回的是 Mix_Chunk 结构体指针。加载完成后就是播放操作,你可以使用 Mix_PlayChannel 函数,这个函数需要指定播放的通道,最终的播放结果会自动的和背景音乐混合到一起:

int Mix_PlayChannel(int channel, Mix_Chunk *chunk, int loops)

这里最简单的使用方法如下:

// Mix_Chunk *sample;
Mix_PlayChannel(-1, sample, 0)

通道参数传 -1,SDL 会自动寻找第一个空闲的通道进行播放。最后当播放完毕后,不要忘记关闭音频设备:

void Mix_CloseAudio()

3. 总结

大体上会了上面两种播放方法就可以让你应付绝大部分简单游戏的开发了,在以后的文章我们继续介绍一些游戏开发里使用的前置知识,为接下来的小游戏开发做好万全准备。