MetaSound 实战:还原《马里奥惊奇》吞食花关卡的音频协同机制

MetaSound 游戏音频实战,带程序化生成。做出成品已经到4月6日了。视频在这里:https://www.bilibili.com/video/BV1Pq421w7Eh/。这篇文章想以文字的方式记录一些要点。

起因就是群友传的图,关于《马里奥惊奇》(Super Mario Bros. Wonder)的:

赞美之词

尤其是这一段:

如果你看完上述细节之后,完全不觉得这些设计有什么厉害的……那么你可能是真的不了解游戏音乐吧。好做就都做了。

哪怕你是给奥本海默写了令我五体投地的原声带,也不能把游戏音乐想得太简单呢。

我看到后就想,这些想法作为用户来说可以理解,但是在开发人员的角度提到的功能是很容易做的。完全没有必要发表这样的评论;这令人笑掉大牙。游戏创意不错是真的(虽然也不是新鲜的想法),游戏音乐不简单也是真的,但图中列举的这些功能并不难做。为了打脸,我就以一个非技术音频的身份来尝试还原这些机制,以此来证明确实不难。在此之前我并没有音频中间件的使用经验,手头也没有主流音频中间件,也不想为了这么一个小打小闹去购买一个授权再学习。所以我就想到了 MetaSound,正好符合我的需求。

MetaSound 是什么?文档在这里。简单来说,它是 Unreal Engine 5 新引入的一个模块,可以实现动态音频生成和控制(包括混合)的功能。在2021年的时候杨彦君就跟我提到过它。不过当时没时间,也没有需求去探索这一块内容。刚好,这次的打脸行动就给我创造了机会。

虽说不难做,但是要是实现这些功能,需要一定的知识储备。具体包括:

  1. 音乐理论;
  2. 数字音乐中的数学;
  3. 主流游戏引擎的使用;
  4. 游戏编程;
  5. 动画制作。

其中我暂时还不会做出符合我要求的动画,所以上面的最后一条,关于动画的那一条,就不做了。如果会的话,动画放进去直接循环播放就行。那么我们开始吧。

一、音轨混合相关的功能

混合器是 MetaSound 的一部分,也就是说中间件常用的多轨混合可以很方便地实现。因此我们要准备每个音轨的内容,所以要先根据需求写好一个多声部的乐谱,并导出音频文件作为素材。例如,这里的需求是:

  • 吞食花要有多个声部;
  • 吞食花声部之间要有明确功能区分;
  • 要有铃鼓声部;
  • 要有哼唱声部;
  • 其他的声部和正常乐曲需求差不多。

准备好之后导出音频,就可以独立控制每个音轨的音量了。如果你想,还可以加效果器。这就足够完成声部相关的几个功能,只需要结合具体游戏逻辑即可。

二、和弦相关的功能

为了在恰当的时机演奏恰当的单个音符或者和弦,需要有能被订阅的事件。我在视频里是用了一个简单的相交查询来做的,还有就是订阅了角色落地事件,这里就略过了。另外,如果我们知道了此时的和弦,事件触发时,在什么时候演奏什么音,也是需要计算的。详细代码也略过,这是业务逻辑的一部分。

和弦和时间数据就用自定义资产类型来储存,只要代码里能拿到就行。

演奏单音很简单,只需要传入频率,接上波形生成器,并用节点控制持续时间即可。

演奏和弦(和琶音)稍微麻烦一点,要编写自定义的 MetaSound 节点。我参考的是 FMusicalScaleToNoteArrayNode(位于 MetasoundMidiScaleToArrayNode.cpp),节点输入和弦类型,输出和弦的一组半音。拿到半音数组后具体是齐奏(和弦)还是分解(琶音),在音频图里实现即可。

三、尾声

说是简单,不过具体代码写起来和调试起来还是很花时间的,毕竟我不是技术音频嘛。例如上面的时间同步,联调还挺麻烦的。而且还有不少的辅助代码……

Unity 那边的情况我在视频里也锐评了一下。

原型有了,但是打磨其实也要花功夫。优化是必需的,考虑到 Switch 的性能……嗯,是必需的。音色的调节需要音效师帮助。还有一些具体的播放策略:

  • 给定和弦,应该播放什么样的序列?(我只是选择了和弦特征音分解)
  • 演奏的时机如何?如果玩家在某个特定时间点做出动作,是立即播、延迟播,还是不播?(我是立即+限流的方式,听起来有瑕疵)
  • “前”“后”台音频(或者看作是音效和音乐)播放时是否需要其他音频控制,例如额外效果器?(我没做)

只有方方面面都配合好了,才能带给玩家出色的游戏体验。这些才是困难的部分。

分享到 评论