Sound类
创建Sound类真的很简单。你已经在第一章看到了一些Sound类,sound cue回放非常简单。为了使使用起来更简单,您可以定义一个枚举包含所有的声音cue名称。通过这种方式可以确保每一个sound cue确实存在,而且你不会输错名称。这样做的另一个优点是智能感知,它将会显示隐藏在XACT项目中所有可用的sound cue。花费两分钟时间写这些枚举是值得的。在Sound类中的Play方法帮助下,现在播放sound cue很容易。
/// <summary> /// Play /// </summary> /// <param name="soundName">Sound name</param> public static void Play(string soundName) { if (soundBank == null) return; try { soundBank.PlayCue(soundName); } // try catch (Exception ex) { Log.Write("Playing sound " + soundName + " failed: " + ex.ToString()); } // catch } // Play(soundName) /// <summary> /// Play /// </summary> /// <param name="sound">Sound</param> public static void Play(Sounds sound) { Play(sound.ToString()); } // Play(sound)
您也可以通过让字符串私有使得只允许声音枚举。如果sound bank无法在构造函数中初始化(例如,如果该文件丢失)此方法将立即返回而您无法播放任何声音。错误会被记录到日志文件中。接着你调用PlayCue播放并忘记sound cue。如果失败你只写入日志文件,因为你不想因为某些声音文件的丢失打断正常的程序逻辑。
正如你之前所见,您也可以使用GetCue方法记住一个sound cue,然后做多个事情。如果您想自己停止cue而不是等待声音放完,这样做是很有用的。作为一个例子,下面的代码用来停止播放音乐:
someSoundCue = soundBank.GetCue("SomeSound"); someSoundCue.Play(); ... someSoundCue.Stop(AudioStopOptions.Immediate);
本书中的游戏的Sound类中的大部分代码是相同的,下面看看不同和特殊功能(见图9-14)。
首先你会发现在Sound枚举中有不同的sound cue名称。Rocket Commander和赛车游戏使用自定义的picth cue。XNA Shooeter不使用任何特殊设置,你只需在游戏开始时播放音乐,然后播放所有音效。
为了使输入播放声音的代码更舒适,创建了几个辅助方法。例如,PlayExplosionSound只是取代了下面的代码使其能更快并更容易编写:
Sound.Play(Sound.Sounds.Explosion);
对于音乐播放添加了两个额外的方法:StartMusic和StopMusic,它们也保存了music cue的音轨。
每个sound类中还有一个单元测试用来检查回放声音是否正常,并检查音量是否正确。下面是Rocket Commander声音类中的单元测试:
/// <summary> /// Test play sounds /// </summary> public static void TestPlaySounds() { TestGame.Start( delegate { if (Input.MouseLeftButtonJustPressed || Input.GamePadAJustPressed) Sound.Play(Sounds.Bomb); else if (Input.MouseRightButtonJustPressed || Input.GamePadBJustPressed) Sound.Play(Sounds.Click); else if (Input.KeyboardKeyJustPressed(Keys.D1)) Sound.Play(Sounds.GameMusic); else if (Input.KeyboardKeyJustPressed(Keys.D2)) Sound.Play(Sounds.MenuMusic); else if (Input.KeyboardKeyJustPressed(Keys.D3)) Sound.Play(Sounds.Explosion); else if (Input.KeyboardKeyJustPressed(Keys.D4)) Sound.Play(Sounds.Fuel); else if (Input.KeyboardKeyJustPressed(Keys.D5)) Sound.Play(Sounds.Victory); else if (Input.KeyboardKeyJustPressed(Keys.D6)) Sound.Play(Sounds.Defeat); else if (Input.KeyboardKeyJustPressed(Keys.D7)) { Sound.PlayRocketMotorSound(0.75f); Sound.ChangeRocketMotorPitchEffect(0.5f); } // else if else if (Input.KeyboardKeyJustPressed(Keys.D8)) Sound.StopRocketMotorSound(); TextureFont.WriteText(2, 30, "Press 1-8 or A/B or left/right "+ "mouse buttons to play back sounds!"); }); } // TestPlaySounds()
最后,Update方法被BaseGame类中的Update方法自动调用。Update方法只为您确保在XACT中所有参数,循环,时间值的更新。
火箭发动机声音
快速看一下Rocket Commander中的火箭发动机声音。前面你已经看到完成这个工作所需的代码,下面是如何整合在一起并配合Rocket Commander游戏引擎,一开始这是用DirectSound写的,但在XACT中工作得也很好:
/// <summary> /// Play rocket motor sound /// </summary> public static void PlayRocketMotorSound() { // Get new cue everytime this is called, else we get Xact throwing // this: The method or function called cannot be used in the manner // requested. rocketMotorSound = soundBank.GetCue(Sounds.RocketMotor.ToString()); // Plays the sound looped, set in XACT rocketMotorSound.Play(); // Make motor category a little silent motorCategory.SetVolume(0.86f); } // PlayRocketMotorSound(volume) /// <summary> /// Change rocket motor pitch effect /// </summary> /// <param name="pitchFactor">Pitch factor</param> public static void ChangeRocketMotorPitchEffect(float pitchFactor) { rocketMotorSound.SetVariable("Pitch", 55 * MathHelper.Clamp(pitchFactor - 1, -1, 1)); } // ChangeRocketMotorPitchEffect(pitchFactor) /// <summary> /// Stop rocket motor sound /// </summary> public static void StopRocketMotorSound() { rocketMotorSound.Stop(AudioStopOptions.Immediate); } // StopRocketMotorSound()
游戏开始后你只需调用PlayRocketMotorSound方法就能播放火箭的声音,然后通过ChangeRocketMotorPitchEffect方法修改它:
// Adjust rocket playback frequency to flying speed Sound.ChangeRocketMotorPitchEffect(0.66f + speed * 0.9f);
如果你失败或任务结束,则调用StopRocketMotorSound方法停止播放火箭的声音。
赛车游戏中的声音逻辑工作方法类似,但更加复杂,因为你有13个不同的发动机声音。看一下赛车游戏的sound类了解更多细节。
Whoosh,那是什么呀?
初版本的Rocket Commander使用自己的3D音效计算公式并使用DirectSound的音量和和panning属性。在XNA中不能改变panning。所有声音必须以在XACT中创建和设置的方式播放。对于3D声音,你应该使用真实的3D声音代码,在XNA电测版本中我写了一些播放3D声音的代码,并实现了一个在三维世界中的3D监听对象。
这个3D监听对象在XNA中被移除,现在您不能使用3D音效。希望将来会做出改变,如果3D可以被实现,我一定会更新这本书中的代码,但现在你只能播放单声道的声音,没有立体声或环绕声。
如果你可以使用X3DAudio和X3DAudioListener (这些类可能有不同的名称,重要的是您可以设置和播放3D音频效果,并且在3D listener类的帮助下确定玩家的位置),在设置了所有重要的cue后,下面的代码用于播放3D声音:
// Setup 3D listener for 3D sound Sound.Listener = new X3DAudioListener( zAxis, yAxis, cameraPosition, movementVector);
现在,为每个声音设置发射器并播放3D音频实例。目前还没有C#代码可以做到这一点,如果你对此感到困惑,你可以看看下面的链接去了解X3Daudio类是如何使用C++的。在C#中做法类似,但希望能更容易编写。
菜单声音
这里有一些关于菜单提示声音的小技巧:
- 使用如PlayMenuHighlight或PlayMenuClick之类的辅助方法播放声音,而不是使用Sound枚举。
- 如果你想用不同的音量播放菜单声音,例如主要按钮声音响而小控件声音轻,可以在XACT中建立两个sound cue而不是编写自己的自定义代码,这样做工作量很大而且没有必要。
-
尝试写辅助方法判断鼠标是否移到了控件上并重用此代码,这样,你就无需每次都自己处理播放点击或高亮的声音。
-
请确认您还支持Xbox 360控制器和键盘。编写一个只支持鼠标的游戏在Xbox 360毫无意义,因为Xbox 360不支持鼠标。
-
当移动到菜单项时播放高亮的声音,用户按下手柄键或按下space或Enter键选择菜单时播放点击的声音。