Part Twelve - Audio
Prior to XNA 3.0, sound in XNA was handled via the XACT audio tool, which allows for some very complicated audio system creation, but is similarly complicated to use. With the release of XNA 3.0, a new audio system was added to XNA that is much, much simpler to work with. Additionally, the new SoundEffect and SoundEffectInstance classes are available on all supported XNA platforms, while XACT sound is only available on Windows and the XBox 360.
The sample sound effects I'm going to use are from this web site: http://www.grsites.com/archive/sounds/. As far as I can tell, they are all free sound effects for non-commercial use. Of course, you will want to either purchase or produce your own sound effects for your game project if you intend to sell it.
I have put together a small package of the five sounds we will be using. It can be downloaded from http://www.xnaresources.com/downloads/StarDefense_Sounds.zip and contains sounds from the Battle and Scifi categories of the site listed above.
In your Visual Studio project, right click on the root of your Content project and click "New Folder..." and create a folder called "Sounds". Extract the .WAV files from the zip file above to the Sounds folder and add them to your project just like you would add a texture. The Content Pipeline will detect the .WAV format and set the appropriate content importer automatically.
As for the code, we will be working exclusively in the Game1.cs file. We need to add a couple of declarations in our declarations area for the sound effects we will be playing:
static int iMaxExplosionSounds = 2;
private static SoundEffect PlayerShots = new SoundEffect;
private static SoundEffect ExplosionSounds = new SoundEffect[iMaxExplosionSounds];
private static SoundEffect PowerUpPickupSound;
Here we are simply declaring a few objects of type SoundEffect (there is also a "Song" object type for playing longer background song type files). We are actually doing a couple of things here, so:
- The PlayerShots sound effect array holds two possible SoundEffect objects. Recall that we can upgrade our weapons to dual cannons by picking up a powerup object. So we will use two different sounds to represent the different weapon levels.
- The ExplosionSounds array has two (slightly) different explosion sound effects in it. We will randomly play one whenever we need an explosion sound to happen. If you have a larger variety of explosion sounds available, simply increase iMaxExplosionSounds and load them in your LoadContent and you can generate a little more sound effect variety.
- The PowerUpPickupSound is played whenever the player... you guessed it... picks up a Power Up.
On to LoadContent, where we just need to load these resources just like we do our textures. Add the following to your LoadContent method:
PlayerShots = Content.Load<SoundEffect>(@"Sounds\Scifi002");
PlayerShots = Content.Load<SoundEffect>(@"Sounds\Scifi050");
ExplosionSounds = Content.Load<SoundEffect>(@"Sounds\battle003");
ExplosionSounds = Content.Load<SoundEffect>(@"Sounds\battle004");
PowerUpPickupSound = Content.Load<SoundEffect>(@"Sounds\Scifi041");
Again, nothing new here. This is identical to the way we load textures except for the Type specifier in Content.Load.
So now what we have our sounds, how do we play them? Very, very simply. Scroll down to your FireBullet method and add the following to the end:
PlayerShots[player.WeaponLevel].Play(1.0f, 0f, 0f);
The first line (if (iVerticalOffset==0)) is simply there to prevent us from playing two sound effects when the player has dual cannons (since we use the FireBullet method twice in that case, with the second one having a -4 pixel vertical offset). What we are really interested in here is the second line. We simply call the Play() method of the SoundEffect object, specifying the following:
- Volume - A value from 0.0f to 1.0f, indicating how loud (relative to the volume of the actual file) the sound should be played. 0.0f would be silent (and fairly pointless), while 1.0f is "full volume".
- Pitch - This float will shift the frequency of the sound up or down. 0f is the "normal" pitch for the sound file.
- Pan - If you are interested in simulating playing the sound in 3d-ish space, this float shifts the sound between the left speaker (-1.0f) and the right speaker (1.0f). This isn't really 3d sound, but more like turning the balance knob on your car radio. The 0f value is "centered".
Executing the Play method creates an instance of the sound playing, so it can be executed multiple times and will play multiple times simultaneously.
Lets add the last bits of code we need for the rest of our sound effects. In the DestroyEnemy method, add:
ExplosionSounds[rndGen.Next(0, iMaxExplosionSounds)].Play(1.0f, 0f, 0f);
Here you can see we are picking a random explosion sound to play, at full volume, normal pitch and pan, and non-looping. Next, we need to add two things to the CheckPlayerHits method. Right after fPlayerRespawnCount = 0.0f; add the following line:
ExplosionSounds.Play(1.0f, 0f, 0f);
And in the PowerUps section of the method, right after powerups[x].IsActive = false; add:
PowerUpPickupSound.Play(1.0f, 0f, 0f);
And that's all there is to it. Run your game and you should have sound for player cannons, enemy/player ship explosions, and picking up powerups.
Of course, you could add more sound effect for just about anything you want. A superbomb will make a super explosion already with all of the DestroyEnemy() calls firing off explosions, but you may want to add a sound effect specifically for the bomb. Or perhaps a sound to play when the player is "thrusting" the ship.
I've got a final wrap-up .ZIP file of the project for download that contains everything in all 12 parts: