发生某些事情时播放声音的好方法?听起来如何?


10

因此,我一直在考虑我的课程在很多时候会变得多么单板。例如,在Character类的Jump方法中,可以引用声音效果对象并进行播放。就其本身而言,这很好,但是当考虑到物理,动画,碰撞等因素时,Jump方法将变得非常庞大,并且Character该类对许多不同的事物具有很多依赖性。不过,这可能很好。但是,如果我们不再希望角色跳跃时播放声音该怎么办?现在,我们必须在混乱的代码中找到特定的代码行,Jump并将其注释掉。

所以..我在想..

相反,如果有某种AudioSystem类,而它所做的只是订阅它对其他类感兴趣的随机事件,那该怎么办?例如,Character类可能有一个Jumped事件(我想也是静态的),该事件Character在方法的类内引发。然后,Character班级对角色跳跃时播放的声音效果一无所知。那AudioSystem将是一个巨大的类,程序员可以撤退以通过使用静态事件将声音效果与游戏中发生的某些事件联系起来。然后,如果它得到太大,它可以在像子分离EffectsAudioSystemBackgroundAudioSystemAmbientAudioSystem,等等。

然后,在游戏的选项中,可以有一个复选框来启用或禁用这些声音,而要做的只是用一个简单的单个布尔标志禁用一个系统。系统的概念也可以扩展到物理,动画等方面,以至于玩家行为所导致的大多数游戏响应都通过这些精心设计和分离的系统进行连接。

好的,我的问题可能有点含糊,但是这种声音听起来如何?我真的从未听说过有关这种系统的很多讨论。目前为止,这一切都在我的脑海中进行,到目前为止,尚未进行任何编码,因此,这也许是“理论上不错,但实际上却不好”的交易之一。这种系统是否可以在更大的游戏上使用,还是最终会崩溃,甚至比原始系统更像是意大利面条般的混乱?


5
听起来像是一个好主意:)(更重要的一点是:在松散耦合的子系统/类之间使用消息进行通信通常是设计上的好主意)
bummzack 2012年

1
这就是这样做的方法,如果还没有,还应该将渲染也放在单独的类中(例如,在Character类中不应该具有draw()函数)。
dreta 2012年

Answers:


2

消息难以调试和维护。从理论上讲,这听起来不错,但是一旦付诸实践,它就会因大量重复发送的数据而变得混乱。跳跃音效的末尾将需要更多数据,例如角色的位置,速度,材质,请命名,末尾的列表将很长。

因此,要么您需要收集此数据,然后通过一个非常特定的事件/消息(将数据复制到其中)将其发送到AudioManager,要么发送对消息中字符的引用,以便AudioManager可以访问数据,两者方式最终变得混乱,现在音频经理必须为地下材料等选择声音。

因此,最后,特定事件(这是仅针对此消息的非常特定的类)将再次使这些类再次耦合。赢不了多少,最后您会得到一堆非常具体的事件/类,这些列表很杂乱,仅用于发送数据(已经存在,可能已经过时,可能会过时,并且会遭受重复数据的所有其他问题) 。

因此,将会有大量不必要的类需要维护,这些类会在角色和AudioManager之间引入深层耦合,除非现在它分散在整个源代码中。不仅在Character类和AudioManager类中。

分离代码仍然是一个好主意,但是消息实际上只是深度耦合的另一种方式。只需耦合一些代码,使用最直接的方法将它们耦合,不要过度设计。


1
精心设计的消息传递系统如何“仅仅是深度耦合的另一种方式”?发送消息是绝对最小的耦合,对象之间永不通信。您设计的方式可能会引起问题,但是,如果系统仅采用声音,位置和类型,它将解决所有他的问题,并且不会引入您建议的任何问题。音频系统不应该计算需要什么声音,它应该抽象掉所有设置,最好是扩散问题。en.wikipedia.org/wiki/Coupling_(computer_programming)
ClassicThunder

1
@ClassicThunder实际上,这种方法的伸缩性不是很好。只要简单的应用程序就可以了,只要您需要一个通用的PlaySoundEvent。但是问题是AudioManager监听了一个专门的OnJump()事件,因此角色可以摆脱音频工作。但是,对于简单的PlaySoundEvent而言,情况并非如此,因为角色必须选择声音并将其发送到AudioManager,这使引入OnJumpEvent摆脱音频工作的初衷无效。
Maik Semder '02

1
但是,在使用OnJumpEvent时,可以选择将对字符的引用添加到事件中,或将所有重要数据从字符复制到事件中。当然,您是对的,后者不会引入深度耦合,但是它将遭受数据重复问题和必须维护的新数据传递对象的困扰,就像其他所有新事件一样。
Maik Semder '02

1
-1原因虽然我同意让消息如此专门化(OnJump)可能是一个坏主意,但这只是一个漫长的“不,这不是个坏主意”,而不是有用的信息,无法使人们进行以该名称命名的PlaySound事件音效及其发生的3D位置和/或音量的大小。
詹姆斯

@James感谢您的输入,我并不是要说使用PlaySound事件,而是要说事件对于GUI数据库应用程序是一个很好的抽象,但对于复杂的游戏却不实用。
Maik Semder '02

2

我不认为消息传递系统完全可以解决工程问题。实际上,它可以大大简化在抛光阶段完成工作的过程。您做对了!

您所描述的正是我去年为Global Game Jam游戏所付出的努力。我负责创建和编辑SFX,并将自己和另一位作曲家写的音乐以不吸引人的方式整合到游戏中。

从音频角度来看,此方法的优点在于,它可以使您用声音做更多有趣的事情。如果您认为游戏中的声音效果仅仅是声音文件,音量和声像,那么您做错了。

在我们的游戏中,您是一只恐龙,它乘坐飞船撞上行星来得分。我们在Flash中工作,因此不需要数据驱动的基础结构。AudioManager是由一系列静态方法组成的类,其唯一目的是控制响应游戏事件而发生的声音。

如果我要用C ++编写它,那么花更多的时间来提取声音可能具有的所有可能行为。通知系统已采取措施的消息的要求不会太复杂。它只需要消息类型,受影响的原始对象或对象,访问某种游戏状态上下文的权限,而无需太多。该协议可以随着游戏需求的增长而增长。自然地,如果您在代码实现中(例如我们的劣质GGJ代码)执行所有这些操作,那么您将遇到更严重的整体类问题。但是,通过制作数据驱动的系统,可以轻松地缓解这种情况。

无论如何,这是我们的游戏音频系统对各种消息的反应:

  • 玩家与行星碰撞:这将触发行星爆炸声,足够基本。然后立即查询正在运行的组合计数器。如果足够高,它将安排声音效果在恐龙发出胜利吼声的半秒后播放。同样在背景中计算了一个随机的星球人口值(大约600到3000之间-我不知道为什么选择这个范围,这是一些废弃的游戏机制,仍然让我用来使音频变得有趣),所以我用它来衡量远处尖叫声的音量(行星公民遇到不合时宜的命运)。

  • 玩家按住空格键以进行加速:听到此声音后,播放了一点“嘶哑”的推进器声音,同时还发出了1.5秒的低循环发动机轰鸣声。粒子系统还使用它来发射发射器IIRC

  • 播放器放开空格键以进行减速:现在,播放器放开了空格键,音频系统知道它必须使引擎回路减速。如果我有更多的时间,我会希望在它上面叠加另一种声音,这是一种吟的调低声音类型的声音。

  • 玩家与邪恶的太空地雷发生碰撞:太空地雷不好,因此不仅会产生金属撞击声并伴有爆炸声(被烤成一种声音),而且还会播放随机选择的恐龙令人沮丧的声音。随着玩家健康程度的降低,更有可能选择更多的“哭泣”之类的声音。

当原声带既活跃又动态时,即使只是像我上面所述的一些简单行为,已经很有趣的游戏也会变得很有趣。是的,需要进行一些后勤工作以确保传递正确的数据。但是,BFD。在更大范围的游戏代码中,这与最复杂的事情相去甚远。

实际上,FMOD和Wwise就是这样工作的。他们没有中央消息调度程序,但是您可以将事件有效地发布到他们的中央系统,并且它们通过播放由音频实现者在创作工具中预先设计的声音效果来做出反应。可以将其想象为为游戏提供现场DJ。他坐下来观察正在发生的事情,并在适当的时间触发声音片段以使事情有趣,将它们混合在一起,使它们很好地适合先前存在的音频环境。

[编辑]另外,我看到您已经标记了此C#。这是XNA,如果是,您是否使用XACT?如果使用XNA,则应该使用XACT。


1
这可能适用于小型项目,甚至可能很有趣。但是,在大型情况下,您最终需要维护大量的消息类,而简单的函数调用将具有相同的效果。这就是事件系统无法很好地扩展,难以管理更大项目的原因。
Maik Semder '02

1
顺便说一句,我们在工作室使用FMOD,没有消息/事件系统,您不向FMOD发送事件,您只需调用c函数或c ++方法即可播放某些内容。他们只是将声音称为“事件”,但这并不能使其成为事件系统,只是它们用来代替声音的术语。
Maik Semder '02

没有为什么?您只需要直接调用该函数即可,而不是使用事件来传递参数。最后,事件只是作为函数调用而已,它只是在事件对象中传递参数,而不是直接传递参数。唯一的区别是事件系统引入了新的间接寻址,但最后它只是一个简单的函数调用,只是不必要地过于复杂。
Maik Semder '02

@MaikSemder方法调用如何最终不会陷入他们各自为政的纠结之中?另外,我试图指出事件系统与Wwise和FMOD使用的“事件”之间的区别。我得到的想法是,复杂的音频逻辑不属于游戏对象类,并且将声音逻辑抽象化以使接口类似于调度事件,这使得拥有丰富的音频逻辑变得更加容易。我真的看不到EventManager->dispatch("Sound:PlayerJump")和之间的功能差异soundSystem->playFMODEvent("/MyGame/Player/Jump")
michael.bartnett'2

2
简化编码可能会带来好处,但它不是免费的,它会带来性能,维护和调试困难的成本。我的观点是,对于大型项目而言,这样做是不值得的。与没有事件相比,您处理的对象更多,并且必须为此付出代价。我考虑使用消息系统的唯一地方是进行线程间通信,以防止线程之间的锁定。
Maik Semder

0

我同意Maik Semder的观点,即消息传递系统可能设计过度(无论如何现在如此)。

据我了解,你的类目前看起来像比约恩“单片一流”为可在“A类整体”可以看出这里

我建议您阅读该文章,尽管目前完整的组件系统可能会过时,但如果您阅读“拆分其余内容”,那应该为您提供一种抽象行为的好方法,并最终可能会变得更复杂解。无论如何,它将为您提供一个良好的基础。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.