ScriptHighlighter

Monday, December 28, 2015

Windows 8/10 Game Development using C#, XNA, and MonoGame 3.4: Building a Shooter Game Walkthrough – Part 9: Adding Sound Effects and Music


A Few Words Before We Begin

If you are following along you will notice that the title of this series is updated to reflect Windows 10 and Monogame 3.4.  

Don’t worry!  All of the code we have written and the projects we created will run just fine on Windows 8, 8.1, or 10 if you are working in Visual Studio 2010 or 2013 and have installed the XNA Game Studio 4.0.  If that is your situation skip the next couple of paragraphs.

However, for those of you that are ready to put the past behind you and completely sever the cord with the Microsoft XNA framework or you are brand new to game development and Monogame then the following steps walk you through creating a new MonoGame project for Windows 10.  I also provide a quick outline for using the MonoGame content pipeline to add graphics, sound, and effects to your games.

Creating a New Windows 10 MonoGame UAP Project

Let's get started!  First we want to create a new Windows 10 UAP MonoGame project.

Step 1.) Open Visual Studio and select File->New Project.  Call it "ShooterTutorial-step-9-uap".  

Step 2) Copy in the source code from previous tutorials.




In this tutorial we will reuse the png files used previous installments of the series.  This should help to illustrate the point that the everything both source and content can be copied and pasted into a MonoGame project directly from an XNA project.

Or you could just fork my Windows 10 UAP app here from here and skip all the details. 

Note: The repository linked above is a Git repository not HG as used in the past.

Now, where was I?

Ah yes I remember, sound, music and explosions.  In this blog post we will expand our game project to add game music and sound effects.  Music and sound can greatly improve the immersion and set the mood of your game.  Game sounds can be nearly as important to your game as graphics.  How bland would Mario Brothers be if there were no sound associated with picking coins?  Sounds may become game play elements in and of themselves.   Imagine your game character being a dark room and footsteps approaching from behind?  

Add the Content Files to Our Project

Our first order of business will be to add the sound game music and sound effect contents to our project.  Start by creating a new folder in the project called Sounds. I typically locate this folder below the Content folder that appears in our new MonoGame Windows 10 UAP solution.  Next copy in the gameMusic.mp3, laserfire.wav, and explosion.wav files into the Sounds folder.

Next launch the MonoGame Content PipeLine Tool by double clicking the Content.mgcb file in the solution explorer.  You may be prompted about which application to use to open the file type.  If you are prompted, brows to the pipeline.exe file located in the directory where you installed Monogame. It should similar to this C:\Program Files (x86)\MSBuild\MonoGame\v3.0\Tools\Pipeline.exe.

To add content to the Monogame Content Pipeline tool right-click on "Content" and click "Add".  Now browse to existing images, models, sounds, or effects you want to add and click "open".

If you have done everything correctly your pipeline tool should look like this:



The next step is to save the content project and attempt to compile your assets.  On the Build menu select "build".  The content tool will attempt to build all assets; images, sounds, and models. There is no need to build each type individually.



If all goes well the content tool should your content files will build with no errors and your content will be ready to use in your game projects. 

Consuming our Sound Resources

First, let's add a sound that represents the discharge of our ship's laser cannon.  XNA/Mongame provides two class for working with sound effects; SoundEffect and SoundEffectInstance.  Let's add variables for each to our class.

 //Our Laser Sound and Instance
 private SoundEffect laserSound;
 private SoundEffectInstance laserSoundInstance;

Next, we need to load the sound from our compiled content file into the soundEffect class. Loading sounds isn't much different from loading images.  We will use the contentmanager class and load the sound in the LoadContent Method of the main game class. Add the following line of code to the LoadContent method.

 // Load the laserSound Effect and create the effect Instance
 laserSound = Content.Load<SoundEffect>("Sounds\\laserFire");


Once we have the laserSound class populated with sound data we need to create an instance of the sound effect.  We can do that using the CreateInstance mthod on the SoundEffect class.  Add this last line just below the one above in the LoadContent method.

 laserSoundInstance = laserSound.CreateInstance();

Now for the fun bit!  I decided to add this last snippet of code to the method we call when the user presses the fire button. This method is appropriately named "FireLaser".

Add this line of code inside of the "if" constrains our rate of fire by check the elasped time since the last time the fire button was pressed.  Here is the entire code block as reference.

         // govern the rate of fire for our lasers
            if (gameTime.TotalGameTime - previousLaserSpawnTime >   laserSpawnTime)
            {
                previousLaserSpawnTime = gameTime.TotalGameTime;

                // Add the laer to our list.
                AddLaser();

                // Play the laser sound!
                laserSoundInstance.Play();
            }

Time to test! If everything is correct then you should hear a distinct "ziiiip!" when you press the fire button (space bar).  This sound should occur at the same rate as the laser image appears on the screen.  The sound should not play any faster than the actual ship is firing.  In short, the sound is constrained to the rate of fire.

If you made it this far, then I assume the laser sound is working as you would expect.  Now let's add the explosion sound.  All of the steps are the same so this time I will breeze through it.

First, add the two variables we need to hold and play the explosion sound. Remember these variables are members of the Game class.

 //Our Explosion Sound.
 private SoundEffect explosionSound;

 private SoundEffectInstance explosionSoundInstance;


Next, load the sound effect in the LoadContent method and instantiate the SoundEffectInstance class.

// Load the laserSound Effect and create the effect Instance
explosionSound = Content.Load<SoundEffect>("Sounds\\explosion");

explosionSoundInstance = explosionSound.CreateInstance();

Finally, let's play the explosion sound when we add a new explosion to the game.  Add the following code to the AddExplosion method.

  /* play the explosion sound. */

  explosionSoundInstance.Play();

If you run the game you will find the explosion sound is played either when a laser blast or the player's ship collides with an aerial mine.

You may also notice the only one instance of the explosion sound is played at a time.  This means that if you were to destroy two targets in in rapid succession you may find that you only hear the explosion sound once.  Let's alter our code to play the sound from the SoundEffect class rather then the SoundEffectInstance class.

Change the line above to:
explosionSound.Play();

Remember to clean up the SoundEffectInstance classes with a dispose method when you are done.  This can be done when exiting the game or perhaps moving between screens when a given sound is no longer needed.

I added the following code the UnloadContent method.

laserSoundInstance.Dispose();

explosionSoundInstance.Dispose();

Adding Game Music

I will wrap up this installment of the tutorial by adding game music to our demo.  The steps for adding game music are similar to those for adding other types of content.  However, the playback makes use of the Song class and the MediaPlayer class.

First declare a variable that will hold our game music.  Add this line of code the Game class.

// Game Music.
private Song gameMusic;

Next add the following line of code to the LoadContent method.

// Load the game music
gameMusic = Content.Load<Song>("Sounds\\gameMusic");

Lastly, let's kick off the playback of our game music by using the MediaPlayer class.  You can start the playback with this line of code, again, in the LoadContent method.

// Start playing the music.
 MediaPlayer.Play(gameMusic);

When you want to stop the music you can call the stop method like this:

MedialPlayer.Stop();

Conclusion

Today we learned how to convert our Windows 7/8.1 based game projects to Windows 10 UAP projects.  We also learned how to use the MonoGame Content Pipeline tool to completely remove the dependence on XNA.

We also learned how to add cool sound effects and music play back to our games.  Of course this tutorial only scratches the surface.  There is a lot more ground to cover when it comes to sounds such as adjusting pitch, tone, volume, and of course 3D effects.  Next time we will add a GameState class and create menus to navigate to and from.  Thanks for reading and following along!

As always, you can find the complete source code for this tutorial here.
Chris


Saturday, February 28, 2015

Windows 8 Game Development using C#, XNA and MonoGame 3.0: Building a Shooter Game Walkthrough – Part 8: Here comes the Boom!

In part seven of this series we expanded our game by adding a laser gun to the player’s ship and code that detects collisions between a laser beam and an enemy mine.  When a laser beam collides with an enemy mine the enemy is removed from the list of “active” mines.  Effectively, the enemy is destroyed.  Pretty cool eh?  But, wouldn’t it be cooler and more satisfying if the enemy mine was destroyed in a burst of flames, their remains scattered to the winds?  That is exactly the effect we will be adding in this step.

First let’s “true up” our project.  You can download the code base from which I will be working here. This project should contain all the features of Tara Walker’s tutorial series and also my contribution from part seven.

Step 1 – Create the Explosion Class

The first thing we need to do is add a new class to our game project.  Let’s call this class Explosion.cs.  The explosions class is not much different than our player, enemy, or laser classes.  This class has many of the save variables and methods as the classes mentioned above.
At the top of our new class we Ithe following namespace references.

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using ShooterTutorial;

Just like we did in previous steps we are going to add the following stub methods to our class, Initialize(), Update(), and Draw().
public void Initialize()
{
}
public void Update(GameTime gameTime)
{
}
public void Draw(SpriteBatch spriteBatch)
{
}

Next we will define a series of variables that will render our sprite animation, track the location of the explosion, return the height and width of the explosion sprite, and finally, track the explosion's time to live.  This is the amount of time that we will render the explosion animation.  Explosions can’t go on forever (as much as we may wish they would).  All good things must come to an end.
Animation explosionAnimation;
Vector2 Position;
public bool Active;
int timeToLive;
public int Width
{
    get { return explosionAnimation.FrameWidth; }
}
public int Height
{
    get { return explosionAnimation.FrameWidth; }
}

Let’s modify the Initialize() method to accept an instance of our Animation class and a Vector2 called position.  The position will be that of the exploding enemy mine.  The animation class will be responsible for looping through each frame of our sprite strip and displaying the a single image from a series.

public void Initialize(Animation animation, Vector2 position)
{
    explosionAnimation = animation;
    Position = position;
    Active = true;
    timeToLive = 30;
}

The next step is to modify the Update() method to call our animation class’ update method, decrement the time to live and set the explosion’s active flag to false once it’s time to live has expired.

public void Update(GameTime gameTime)
{
    explosionAnimation.Update(gameTime);

    timeToLive -= 1;

    if (timeToLive <= 0)
    {
        this.Active = false;
    }
}

The final modification to the explosion class is in the Draw() method.  Here we invoke our animation class’ draw method to send the sprite frame to the buffer for rendering.

public void Draw(SpriteBatch spriteBatch
{
    explosionAnimation.Draw(spriteBatch);
}

Step 2 - Wiring the Explosion Class into the Main Game Class

For this tutorial I decided that it may be possible to have more than one “active” explosion being rendered at one time. Therefore I am going to need a collection to hold any possible explosions that are touched off.   Add the following lines of code to the top of the Game1.cs class.

// Collections of explosions
List<Explosion> explosions;

//Texture to hold explosion animation.
Texture2D explosionTexture;

The first line of code establishes a list of explosions.  The second line defines a Texture2D structure that will hold our sprite strip.
Now is a good time to add the explosion sprite strip to our content builder project.  To do this, first navigate to the location of the explosion.png graphics file.  If you open the contents of this files using a graphics editor such as Paint.NET you will see that this is a long rectangular “strip” image containing eleven sprites of the explosion in various states.  Our animation class loops across this strip displaying each image briefly to provide the illusion of continuous animation.  This works exactly the same way a cartoonist would draw and photograph a series of images and then display them in rapid succession to produce an animated feature.
Next we will add a single line of code to the Initialize() method. This will instantiate our list of explosions.

// init our collection of explosions.
explosions = new List<Explosion>();

Now let’s load our sprite strip into the Texture2D struction defined earlier.  Add this line of code to the LoadContent() method of the Game1.cs class.

// load the explosion sheet
explosionTexture = Content.Load<Texture2D>("Graphics\\explosion");

There is one last bit of setup we need to do before we can get to the fun stuff (blowing up enemies).  Let’s create a helper method that will initialize and insert a new instance of an explosion into our explosions collection.  Add this method to the bottom of the Game1.cs class.

protected void AddExplosion(Vector2 enemyPosition)
{
    Animation explosionAnimation = new Animation();

    explosionAnimation.Initialize(explosionTexture,
        enemyPosition,
        134,
        134,
        12,
        30,
        Color.White,
        1.0f,
        true);

    Explosion explosion = new Explosion();
    explosion.Initilize(explosionAnimation, enemyPosition);

    explosions.Add(explosion);
}

This method is pretty simple.  First we create a new instance of the animation class using the explosion sprite strip.  Next we create a new instance of an explosion and pass in the explosion animation and the last position of the enemy ship that will be exploding.  Lastly, this explosion instance is added to the list of “active” explosions.
Now the fun begins!   We already have code that detects collisions between the enemies and the player and also between the enemies and laser beams.  Let’s inject code at those locations to add an explosion.
Find the DetectCollisions() method. Inside that method you will find a loop for each laser beam that has been fired and is still active.  Within that loop is a bounds check between the rectangle of the laser beam and that of an enemy mine.  Let’s add our explosion code inside of that bounds check.

// test the bounds of the laser and enemy
if (laserRectangle.Intersects(enemyRectangle))
{
    // Show the explosion where the enemy was...
    AddExplosion(enemies[i].Position);
    // kill off the enemy
    enemies[i].Health = 0;

    // kill off the laserbeam
    laserBeams[l].Active = false;
}

Add the same line of code inside the bounds check for the player’s ship and the an enemy.

if (playerRectangle.Intersects(enemyRectangle))
{
    // kill off the enemy
    enemies[i].Health = 0;
    // Show the explosion where the enemy was...
    AddExplosion(enemies[i].Position);
    // deal damge to the player
    _player.Health -= enemies[i].Damage;

    // if the player has no health destroy it.
    if (_player.Health <= 0)
    {
       _player.Active = false;

    }
}

The last two steps for adding the explosion animation to our game is to add the Draw() and Update() methods for our explosions.
Add this code to the Draw() method of the Game1.cs class.

// draw explosions
foreach(var e in explosions)
{
    e.Draw(_spriteBatch);
}

This code simply loops across the explosion list and calls the Draw() method of each explosion class it contains.
Next we call the update method of our explosion class(es).  This advances the displayed animation from based on the game time.  This code is very similar to the draw code we added above.  Let’s extract this loop into it’s own method.

private void UpdateExplosions(GameTime gameTime)
{
    for (var e = 0; e < explosions.Count; e++ )
    {
        explosions[e].Update(gameTime);

        if (!explosions[e].Active)
        explosions.Remove(explosions[e]);
    }
 }

This code loops across the explosions collection and checks to see if the explosion is still active.  If the explosion is no longer active (it has played its entire animation) then it is removed from the explosions list.  Let’s add our new method inside the Update() method of the Game1.cs class.

UpdateExplosions(gameTime);

Conclusion


In this tutorial we didn’t really do anything differently than was has already been demonstrated in previous steps.  You may begin to see a pattern emerging in these game classes. They all contain an Initialize(), Update(), and Draw() methods.  These are the bacis functions that any game object must perform.  It must be created, it must react to user input, AI, or the world around it, and it must be rendered to the game screen.
In the next tutorial we will add sound effects and game music.  It’s really simple to do and adds a new level of polish and immersion to your game project.