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.

Monday, December 15, 2014

Windows 8 Game Development using C#, XNA and MonoGame 3.0: Building a Shooter Game Walkthrough – Part 7: Load Your Weapons!

In this continuation of Tara Walker's series: Windows 8 Game Development Using C#, XNA and MonoGame we will expand our game by giving the player the ability to fire deadly laser blasts at the enemy. We will create the laser game object, add code to fire the laser when the user presses the space bar, and detect collisions between our laser blasts and the enemy areal mines.  You can find a link to her original series here.


Creating the Laser Blast

Step 1 - Create the Laser Class

Add a new class to your project called Laser.cs. Add the following using statements to the top of the class definition.

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

As in our player and enemy classes, we will stub out the methods for Draw(), Update(), and Initialize().  We will also define a couple of new variables that represent the characteristics of our laser blast.  Add the following definitions to the top of the Laser.cs class.

// animation the represents the laser animation.
public Animation LaserAnimation;

// the speed the laser travels
float laserMoveSpeed = 30f;

// position of the laser
public Vector2 Position;

// The damage the laser deals.
int Damage = 10;

// set the laser to active
public bool Active;

// Laser beams range.
int Range;

// the width of the laser image.
public int Width
{
    get { return LaserAnimation.FrameWidth; }
}

// the height of the laser image.
public int Height
{
    get { return LaserAnimation.FrameHeight; }
}

Design Break:

As you look over the variables above, hopefully you are already getting cool ideas for power ups and enhancements.  Perhaps the player can get bonuses to the laser's damage and speed characteristics.  Will you permit your laser to fly all the way across the screen or will it fizzle out shortly after being fired?  As a game designer these are all aspects you must consider.  All of which will have a effect on game play and ultimately, the finished product. It's a good idea to write these ideas down as part of a game design before you begin coding.  Having a formal game design will save you time during implementation.  Now back to coding!

Add the following implementations for the Initialize(), Update(), and Draw() methods.

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

public void Update(GameTime gameTime)
{
    Position.X += laserMoveSpeed;
    LaserAnimation.Position = Position;
    LaserAnimation.Update(gameTime);
}

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

Step 2 - Add the Laser Texture Content and Load It

Locate the laser.xnb from the downloaded folder and add it to your project.  In my project I placed this texture file in the \Content\Graphics folder.  Once you have added this file. Right-click on the file in the solution explorer and click "properties".  Set the "build action" setting  to "content" and set the "copy to output directory" setting to "Copy if newer".  

Next let's add the following variable near the top of the game class Game1.cs.  This variable will hold the texture for our laser beam.



// texture to hold the laser.
Texture2D laserTexture;
// govern how fast our laser can fire.
TimeSpan laserSpawnTime;
TimeSpan previousLaserSpawnTime;
I will mention the two TimeSpan variables in detail later.  For now let's just add them just below the declaration of our texture variable.  Add the following line of code the the LoadContent() method of the Game class Game1.cs.  
// load th texture to serve as the laser
laserTexture = Content.Load<Texture2D>("Graphics\\laser");

Step 3 - Initialize our Laser Object List


Design Break:

Before we can add our laser we must make a couple of game design decisions.  Firstly, will the player be able to fire only one laser beam at a time or will we permit them to fire multiple beams in a hail of flaming hot destruction?  Trust me, it matters!  For this tutorial we will go with the latter option. We will establish a variable that governs a rate of fire for our laser cannon.  That means we may have more than one active beam at a time.  This requires us to create a collection of laser beam objects.

To store the volley of laser fire, add this list definition near the top of the Game1 class just below the laserTexture declaration:

List<Laser> laserBeams;
Next add the following variable definitions to the Initialize() method.  I will explain each one below.

// init our laser
laserBeams = new List<Laser>();
const float SECONDS_IN_MINUTE = 60f;
const float RATE_OF_FIRE = 200f ;
laserSpawnTime = TimeSpan.FromSeconds(SECONDS_IN_MINUTE /RATE_OF_FIRE);
previousLaserSpawnTime = TimeSpan.Zero;

The first variable should be fairly obvious. We are simply instantiating ("newing up") a strongly typed list of our Laser class.  The next line is for nothing more than readability.  I create a float to hold the number of seconds in one minute.  Then I create another float to hold a rate of fire (lasers per minute).  II use the seconds in one minute and the rate of fire variables to calculate a time span, in seconds, that governs how quickly my laser can fire another round.  In this case I want a rate of fire of 200 rounds per second so my time span between shots will be less than one second (60/200).  The very last line initializes a variable that will be set when a new laser is fired.  When the time between "now" and the previousLaserSpawnTime is greater or = the laserSpawnTime then I can fire a new beam.


Step 4 - Add Code to Fire a Laser

To support firing the player's weapons I added the following two classes to the Game1.cs class.

protected void FireLaser(GameTime gameTime)
{
    // govern the rate of fire for our lasers
    if (gameTime.TotalGameTime - previousLaserSpawnTime > laserSpawnTime)
    {
        previousLaserSpawnTime = gameTime.TotalGameTime;
        // Add the laer to our list.
        AddLaser();
    }
}

protected void AddLaser()
{
    Animation laserAnimation = new Animation();
    // initlize the laser animation
    laserAnimation.Initialize(laserTexture,
        player.Position,
        46,
        16,
        1, 
        30,
        Color.White,
        1f,
        true);

    Laser laser = new Laser();
    // Get the starting postion of the laser.

    var laserPostion = player.Position;
    // Adjust the position slightly to match the muzzle of the cannon.
    laserPostion.Y += 37;
    laserPostion.X += 70;
    
    // init the laser
    laser.Initialize(laserAnimation, laserPostion);
    laserBeams.Add(laser);
    /* todo: add code to create a laser. */
    // laserSoundInstance.Play();
}


The FireLaser() method is pretty straight-forward.  First, I check to see if enough time has passed between now and the last time I fired a laser.  If it has then I record the current time and call the AddLaser() method.

The AddLaser() method simply initializes a new instance of the animation class using our laser beam texture.  Then we set the laser's starting position to that of the player's current position.  We assume the laser is emitted from the player's ship.  Finally, we pass our animation class into the Initialize() method of our laser class and add it to our collection of Laser objects.  Did you see the commented code to play the sound of a laser blast?  That's a teaser for a future tutorial! 

The last thing we need to do is to wire up our FireLaser() method to the press of the space bar on the keyboard or the X button on the XBox controller.  Add this code to the UpdatePlayer() method of the Game1.cs class.


if (_currentKeyboardState.IsKeyDown(Keys.Space) || _currentGamePadState.Buttons.X == ButtonState.Pressed)
{
    FireLaser(gameTime);
}

Step 5 - Add Code to Track Laser Beams and Detect Collisions
Now that we can add laser beams to our collection we need to loop through all active lasers and update their positions and detect collisions with enemies.  This part is pretty simple.  Just add the following code to the Update() method of the Game1.cs class.

// update laserbeams
for (var i = 0; i < laserBeams.Count;i++ )
{
    laserBeams[i].Update(gameTime);
    // Remove the beam when its deactivated or is at the end of the screen.
    if (!laserBeams[i].Active || laserBeams[i].Position.X > GraphicsDevice.Viewport.Width)
    {
        laserBeams.Remove(laserBeams[i]);
    }
}

Notice that I never used the "range" variable I declared earlier in my code.  I just decided that a beam would fly across the screen until it reached the right side or collided with an enemy mine. 

Next we need to loop through all enemies and check to see if they are colliding with any of the laser beams in our collection.  To do this, we need a nested loop.  Add this code to the UpdateCollisions() method in the Game1.cs class.


Rectangle laserRectangle;


This rectangle is used to define the bounding box around a laser beam and is used in collision detection against the rectangles of enemies.  Locate the section of code in the UpdateCollisions() method where we loop through our collection of enemies testing for collisions with the player.  Add this code INSIDE the enemy loop as shown.

// detect collisions between the player and all enemies.
enemies.ForEach(e =>
{
   //create a retangle for the enemy
   enemyRectangle = new Rectangle(
       (int)e.Position.X,
       (int)e.Position.Y,
       e.Width,
       e.Height);

   // now see if this enemy collide with any laser shots
   laserBeams.ForEach(lb =>
   {
       // create a rectangle for this laserbeam
       laserRectangle = new Rectangle(
       (int)lb.Position.X,
       (int)lb.Position.Y,
       lb.Width,
       lb.Height);

       // test the bounds of the laer and enemy
       if (laserRectangle.Intersects(enemyRectangle))
       {
           // play the sound of explosion.
           var explosion = explosionSound.CreateInstance();
           explosion.Play();
           
           // Show the explosion where the enemy was...
           AddExplosion(e.Position);

           // kill off the enemy
           e.Health = 0;

           //record the kill
           myGame.Stage.EnemiesKilled++;

           // kill off the laserbeam
           lb.Active = false;

           // record your score
           myGame.Score += e.Value;
        }
    });
});

Step 6 - Draw the Laser Beams

The last step is to add code in the Draw() method of the Game1.cs class to loop through each object in the laser beam collection and invoke it's draw method.  Add this code:


// Draw the lasers.
foreach (var l in laserBeams)
{
    l.Draw(spriteBatch);
}
Well That is it! If you have done everything correctly then your player's ship will now be armed with a deadly laser cannon.  Experiment with the rate of fire and laser speed variables.  Find a combination that is both fun and challenging.  

Now you can blast away at enemies!


Next time we will make these pesky enemy mines EXPLODE!

You can find the source code for this step here: