问题

你想在游戏中播放声音。

注意:Zune不支持Xact,所以参见教程7-1和7-2学习如何在Zune上播放声音。

解决方案

通过使用Xact,一个XNA Game Studio 3.0自带的免费工具,你可以创建包含游戏中使用的所有声音的Xact项目。你可以将这个Xact项目导入到XNA项目中,这样就可以使用一行简单的代码播放存储在XAct项目中的声音。

工作原理

首先,确保你已经打开了一个XNA项目并存储到了磁盘中的某处。要更好的组织素材,你应该创建一个新文件夹存储所有的声音素材。在XNA项目的Solution Explorer中,找到Content,右击它,选择Add→New Folder,起一个名称。

现在通过点击Start菜单并点击Programs→Microsoft XNA Game Studio 30→Tools→ Microsoft Cross-Platform Audio Creation Tool (XAct)打开Xact。

当开始Xact时,你要做的第一件事就是通过选择File→New Project开始一个新项目,给你的项目起一个名称,并将它保存到XNA项目的Content\Audio目录中。

现在有了一个新的空项目,在左边的树形目录中,找到Wave Banks,它包含了waves,即音频文件。右击Wave Banks选择New Wave Bank。然后,通过右击Sound Banks选择New Sound Bank创建一个新的sound bank,sound bank包含声音,它们是在XNA项目中实际播放的对象。一个sound指向一个wave,可以在wave上添加一些效果。

现在在窗口的右边应该有两个面板。点击Wave Bank面板,你应该可以在Xact窗口的左下角看到它的属性。找到Name属性,将它从Wave Bank改为myWaveBank。然后,点击Sound Bank面板,将Name属性改为mySoundBank。这些名称可以从XNA项目中访问到。

你可以使用wave bank存储所有. wav文件,所以右击面板的某处选择Insert Wave File(s)。浏览到磁盘上你想在游戏中播放的. wav文件,点击Open。你可以看到它被添加到了wave bank中,显示为红色,因为你还没有在sound bank中创建一个sound 使用这个wave。

技巧:文件的位置是相对于Xact项目存储的。如果你改变了声音文件的位置或Xact项目的位置就会遇到麻烦,所以在将声音文件导入到wave bank前先将它们复制到Content\Audio目录中。

一个wave是磁盘上一个声音文件的引用,一个sound使用一个wave让你可以添加一些效果,诸如改变音量和音高。从一个wave你可以生成多个sound。但是,程序员使用一个cue访问sound。所以对每个sound,在可以通过XNA代码访问前需要创建一个cue。一个cue可以包含多个声音,所以一行代码可以播放多个声音。

所以,在可以从wave bank播放一个wave前,你需要创建一个sound和使用这个wave的cue,这可以通过选择wave并拖动到Sound Bank面板的cue区域做到,如图7-1所示。

当心:确保将sound拖动到Sound Bank面板底部的cue列表中,而不是顶部的sound列表。

wave bank和sound bank是分离的,这样你可以从一个wave文件创建多个sound,通过这种方式,你可以在同一个wave上在不同的sound上添加不同的音效。

image

图7-1 将你的wave拖动到cue区域

这就是Xact工具的用法,别忘了通过选择File→Save Projec保存项目,并将文件导入到XNA项目中,方法和导入一张图像或对象文件是一样的。你可以将. xap 文件拖动到Content\Audio文件夹上,或在Solution Explorer中右击Audio文件夹并选择Add→Existing Item。

myXactProject. Xap文件会立刻显示在XNA Game Studio 3.0程序的Solution Explorer中。

当编译项目时,Xact项目会被读取并转换为二进制文件,这个二进制文件会被写入到项目的输出文件夹中。对添加到XNA项目的每个. xap Xact项目文件,XNA内容管道会创建一个. xgs二进制文件(与Xact项目的名称相同),为每个wave bank创建一个. xwb二进制文件,为每个sound bank 创建一个. xsb二进制文件。

在XNA项目中,你需要将这三个文件连接到对应的变量中,首先在代码顶部进行声明:

AudioEngine audioEngine; 
WaveBank waveBank; 
SoundBank soundBank; 

然后将它们连接到二进制文件(它们是在你编译项目时创建/更新的),例如,在Initialize方法中:

audioEngine = new AudioEngine("Content/Audio/MyXACTproject.xgs"); 
waveBank = new WaveBank(audioEngine, "Content/Audio/myWaveBank.xwb"); 
soundBank = new SoundBank(audioEngine, "Content/Audio/mySoundBank.xsb");

变量soundBank包含所有可以播放的cue。但是,当你实例化一个新的SoundBank时,需要将它传递到一个AudioEngine,所以首先需要初始化AudioEngine。只要你从soundBank播放一个cue,它就会从AudioEngine对应的waveBank中找到wave,所以你需要第二行代码。

注意:myWaveBank和mySoundBank的名称是在Xact工具中命名的。

现在,当你想播放一个简单的声音时,你要做的就是调用soundBank变量的PlayCue方法,将要播放的cue名称传递到这个方法中!

soundBank.PlayCue("audio1"); 

当播放结束要确保所有cue从内存中移除,所以要在每一帧都调用 audioEngine的Update方法:

audioEngine.Update(); 

当心:当你改变了Xact项目并存储到文件后,你无法简单地运行XNA项目的. exe指望它将这个改变施加到Xact项目中。首先需要重新编译(按F5)XNA项目使内容管道从Xact项目中创建新的二进制文件。

代码

你需要通过全局变量播放包含在Xact项目中的声音cue:

AudioEngine audioEngine; 
WaveBank waveBank; 
SoundBank soundBank; 

在项目的开始处进行初始化:

protected override void Initialize()
...{
    audioEngine = new AudioEngine("myXactProject.xgs"); 
    waveBank = new WaveBank(audioEngine, "myWaveBank.xwb"); 
    soundBank = new SoundBank(audioEngine, "mySoundBank.xsb"); 

    base.Initialize();
}

使用soundBank. PlayCue方法播放声音,例如下面代码中在按下一个键时播放声音。确保每一帧调用audioEngine. Update方法:

protected override void Update(GameTime gameTime)
...{
    if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) 
        this.Exit();

    KeyboardState keybState = Keyboard.GetState(); 
    if (keybState.IsKeyDown(Keys.Space)) 
        soundBank.PlayCue("audio1");
    audioEngine.Update();
    base.Update(gameTime);
}

现在当运行代码时,当你按下空格键时就会播放声音。