问题
要让你的3D游戏更真实,你想让每个声音都位于3D空间的某个位置。通过这种方式,在相机右侧发生的爆炸会主要通过右声道播放,这样用户就会感到爆炸是真的发生在他的右侧。即使爆炸不在相机的视野中,玩家也可以知道在他的右侧发生了什么事。
注意:Zune不支持Xact和下面的Apply3D()方法,只支持Cue对象,3D音效只被PC和Xbox 360支持。
解决方案
XNA可以让这些事变得简单。对每个声音,你必须设置相机的3D位置和使用声音Cue对象的Apply3D方法设置声源的位置。注意当相机或声源移动时,你需要调用这个方法更新Cue。工作原理如果你想设置声源的3D位置,首先必须为这个音源设置一个AudioEmitter对象和为相机设置一个AudioListener对象。当将这两者传递到Cue的Apply3D方法中时,XNA会计算音响(或耳机)左右声道声音的分配。
下面的方法创建了两个对象:
private void UpdateSoundPosition(Cue cue, Vector3 sourcePos, Vector3 camPos, Vector3 camForward, Vector3 camUp) ...{ AudioEmitter emitter = new AudioEmitter(); emitter.Position = sourcePos; AudioListener listener = new AudioListener(); listener.Position = camPos; listener.Forward = camForward; listener.Up = camUp; cue.Apply3D(listener, emitter); }
对emitter(音源)来说,你只需设置位置。对listener(相机)来说,你还要设置Forward和Up向量,因为当相机旋转了180度后,左右声道会互换。
当创建了这两个变量后,你可以将它们传递到Cue的Apply3D方法中,当音源或相机发生移动时都要进行这个操作。
你需要在第一次调用Play方法前调用Apply3D方法。所以,确保在Cue中还没有调用 Play方法:
protected override void Initialize() ...{ fpsCam = new QuakeCamera(GraphicsDevice.Viewport, new Vector3(0,0,5) , 0, 0); audioEngine = new AudioEngine("Content/Audio/MyXACTproject.xgs"); waveBank = new WaveBank(audioEngine, "Content/Audio/myWaveBank.xwb"); soundBank = new SoundBank(audioEngine, "Content/Audio/mySoundBank.xsb"); cue1 = soundBank.GetCue("audio1"); base.Initialize(); }
在Update方法中,如果你移动了相机位置或音源位置,就需要调用UpdateSoundPosition方法:
protected override void Update(GameTime gameTime) ...{ GamePadState gamePadState = GamePad.GetState(PlayerIndex.One); if (gamePadState.Buttons.Back == ButtonState.Pressed) this.Exit(); MouseState mouseState = Mouse.GetState(); KeyboardState keyState = Keyboard.GetState(); fpsCam.Update(mouseState, keyState, gamePadState); float time = (float)gameTime.TotalGameTime.TotalMilliseconds/1000.0f; Vector3 startingPos = new Vector3(0, 0, -10); Matrix rotMatrix = Matrix.CreateRotationY(time); modelPos = Vector3.Transform(startingPos, rotMatrix); UpdateSoundPosition(cue1, modelPos, fpsCam.Position, fpsCam.Forward, fpsCam.UpVector); if (cue1.IsPrepared) cue1.Play(); audioEngine.Update(); base.Update(gameTime); }
这个方法的前半部分检查用户输入并将输入传递到相机,解释请见教程2-3。当中的代码块更新音源位置。当相机和音源更新后,你需要调用UpdateSoundPosition方法告知cue1对象最后的位置。如果cue1还没有播放,则开始播放。
当心:因为3D effect是通过控制左右声道的音量实现的,所以声音中的任何立体声信息会丢失:首先将声音转换为单声道,然后对应物体的3D位置设置左右声道的音量。
代码
所有的Initialize,UpdateSoundPosition和Update方法前面已经写过了。
扩展阅读
Xact音频工具让你可以在cue中添加一些很棒的3D音效,诸如距离衰减和多普勒效应。因为是图形界面,在视频中介绍要比在书中好得多。在http://creators.xna.com 网站上有一些很好的视频介绍如何将3D音效添加到cue中。
译者注:在http://creators.xna.com/en-US/sample/3daudio网页上也有一个3D音效的例子,实现了距离衰减,多普勒效应。