§9.3Sound类
创建Sound类真的很简单。你已经在第一章看到了一些Sound类,sound cue回放非常简单。为了使使用起来更简单,您可以定义一个枚举包含所有的声音cue名称。通过这种方式可以确保每一个sound cue确实存在,而且你不会输错名称。这样做的另一个优点是智能感知,它将会显示隐藏在XACT项目中所有可用的sound cue。花费两分钟时间写这些枚举是值得的。在Sound类中的Play方法帮助下,现在播放sound cue很容易。
/// <summary>
/// Play
/// </summary>
/// <param name="soundName" /> Sound name
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
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)。
图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 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#中做法类似,但希望能更容易编写。
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directx9_c/ audio_xact_overview_x3daudio.asp
菜单声音
这里有一些关于菜单提示声音的小技巧:
- 使用如PlayMenuHighlight或PlayMenuClick之类的辅助方法播放声音,而不是使用Sound枚举。
- 如果你想用不同的音量播放菜单声音,例如主要按钮声音响而小控件声音轻,可以在XACT中建立两个sound cue而不是编写自己的自定义代码,这样做工作量很大而且没有必要。
- 尝试写辅助方法判断鼠标是否移到了控件上并重用此代码,这样,你就无需每次都自己处理播放点击或高亮的声音。
- 请确认您还支持Xbox 360控制器和键盘。编写一个只支持鼠标的游戏在Xbox 360毫无意义,因为Xbox 360不支持鼠标。
- 当移动到菜单项时播放高亮的声音,用户按下手柄键或按下space或Enter键选择菜单时播放点击的声音。
发布时间:2008/12/17 上午10:37:52 阅读次数:6933