Making a 2D XNA Game - When Cods Collide - The Final Chapter

 In previous posts you've seen me walk you through and discuss the XNA 2D Collision detection sample. I've been taunting you with screen shots of the goofy little 2D Windows game I made called "When Cods Collide" based on that sample...now, I need to come clean on the crude and simplistic methods I used to make the game a bit different from the sample. Note disclaimer on side of blog before trying this at home. :)

(  If you want elegant code, as I mentioned, talk to Shawn.)

Here I tackle:

  • Changes in movement (cod and martinis)
  • Changes in textures (cod and martinis)
  • Adding audio
  • Adding text
  • Adding score that only ever goes up
  • Other tips

While I changed the names of some things, I kept some the same as the Rectangle sample so that you can see what I morphed to my own purposes. For example, I don't have a cod object - I left it as person. Likewise, the martinis are still "blocks" according to the code. Only their textures have changed.

Movement changes...up and down

 Since the previous post left you thinking about how the random blocks fell on the person's head, I will start first on how I modified the falling  blocks code in order to make the martinis "rain" across the screen.

// Spawn new zooming blocks from the side

            // have them fall diagonally

            if (random.NextDouble() < BlockSpawnProbability)

           {

          float y = (float)random.NextDouble() *

                    (Window.ClientBounds.Height - blockTexture.Height);

                blockPositions.Add(new Vector2(-blockTexture.Width,y ));

            // I can have random x and y to start with

            float x = (float)random.NextDouble() *

           (Window.ClientBounds.Width - blockTexture.Width);

            blockPositions.Add(new Vector2(x, -blockTexture.Height));

            }

 In order to set this up to work, under   GraphicsDeviceManager graphics section I put...

// Cod

Vector2 personPosition;

const int PersonMoveSpeed = 5;

 

 // Drinks

        List<Vector2> blockPositions = new List<Vector2>();

        float BlockSpawnProbability = 0.005f;

        const int BlockFallSpeed = 7;

Image changes

To make things look different, even though the class names were kept the same  behind the scenes (blocks, person) I loaded different textures under the protected override void LoadContent() section ....

// Load textures

blockTexture = Content.Load<Texture2D>("martini2");

personTexture = Content.Load<Texture2D>("cod_image2");

Safety Zones

I didn't talk about this much in the rectangle sample blog posts, but there's a section of the code that keeps the objects/sprites contained to an area where they are visible to the player. I had to change the code so that wherever there was an x-direction capability, y was also represented. So under base.Initialize()....

// Start the player in the center along the bottom of the screen

personPosition.X = (safeBounds.Width - personTexture.Width) / 2;

personPosition.Y = safeBounds.Height - personTexture.Height;

Later I change the bounds of where the person (or cod) can move . So I add, under protected override void Update(GameTime gameTime)

           // Prevent the person from moving off of the screen in X direction

            personPosition.X = MathHelper.Clamp(personPosition.X,

                safeBounds.Left, safeBounds.Right - personTexture.Width);

          if (keyboard.IsKeyDown(Keys.Up) ||

              gamePad.DPad.Up == ButtonState.Pressed)

           {

              personPosition.Y -= PersonMoveSpeed;

           }

          if (keyboard.IsKeyDown(Keys.Down) ||

          gamePad.DPad.Down == ButtonState.Pressed)

         {

           personPosition.Y += PersonMoveSpeed;

          }

            // Prevent the person from moving off of the screen along the Y axis

            personPosition.Y = MathHelper.Clamp(personPosition.Y,

            safeBounds.Top, safeBounds.Bottom - personTexture.Height);

 

Adding Audio

It took quite a few MSDN articles to get me through this part. I used : 

How To: Add a Sound File to Your Game Using XACT

https://msdn2.microsoft.com/en-us/library/bb203879.aspx

And also...

How To: Play a Sound

https://msdn2.microsoft.com/en-us/library/bb195053.aspx

In terms of tips in setting things up to play audio, I found that the first article sets up a folder/asset structure that you need to scrupulously follow. In other words, you must have an audio folder inside your content folder and your .wav and .xap files go into that folder.

 At the very top of your game file, right after you setup the GraphicsDeviceManager, setup your audio objects...

public class Windows_Cod_CollisionGame : Microsoft.Xna.Framework.Game

{

GraphicsDeviceManager graphics;

// Audio objects

AudioEngine engine;

SoundBank soundBank;

WaveBank waveBank;

Next you will see I named the xact file project after the cod. You need to end up with an xgs file, a xsb file, and a xwb file when you are done with the audio tutorials. Below I put in "YourWavFileName" for the name of the wav file - that name needs to change depending on what music you want to use.

 

Under

base.Initialize();

Put

// Initialize audio objects.

engine = new AudioEngine("Content\\Audio\\codsoundxact.xgs");

soundBank = new SoundBank(engine, "Content\\Audio\\Sound Bank.xsb");

waveBank = new WaveBank(engine, "Content\\Audio\\Wave Bank.xwb");

// Play the sound.

soundBank.PlayCue("YourWavfileName");

In this case, I checked with my friend Clark (aka "Mad Malcolm" for permission to use his song in my game. If you wanted to use this song or any other song from him, you'd have to ask him for specific permission first...same with your favorite song from another band -- unless you write the song yourself, you need to get permission from the person who wrote it .

Adding Fonts

Fonts are another tricky area of game development because yes, other people own the rights to those fonts. Fortunately XNA provides some free ones for use in your games.

I had to use yet another tutorial to make words appear in my game; I added Kootenay as my font. Spritefont drawing is an interesting science and frankly, I didn't master positioning entirely. But at very least the score and the title/music credits appear in the cod game.... I was able to get the letters and numbers to appear. A key resource for this was the article...

How To: Draw Text

https://msdn2.microsoft.com/en-us/library/bb447673.aspx

So what does my code look like for displaying the text?  

In the load content section I added this to setup the assets for a "Victory message" when the cod and martini collide:

 

//load font and what's in that second string

Font1 = Content.Load<SpriteFont>("Kootenay");

VictoryMessage ="Victory";

//center the string

FontPos = new Vector2(graphics.GraphicsDevice.Viewport.Width / 2,

graphics.GraphicsDevice.Viewport.Height / 2);

To actually make something appear, after Sprite spriteBatch.Begin();

I put.....

// Setup a string called output for the VictoryMessage

string output = "";

// Find the center of the string

Vector2 FontOrigin = Font1.MeasureString(output) / 2;

//draw the string for the score

ScoreFontPos = new Vector2(graphics.GraphicsDevice.Viewport.Width / 10,

graphics.GraphicsDevice.Viewport.Height / 10);

spriteBatch.DrawString(Font1, "Your score is " + score, ScoreFontPos, Color.PapayaWhip,

0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);

ScoreFontPos = new Vector2(graphics.GraphicsDevice.Viewport.Width /6,

graphics.GraphicsDevice.Viewport.Height/200);

spriteBatch.DrawString(Font1, "When Cods Collide... Music By Mad Malcolm Productions", ScoreFontPos, Color.Honeydew,

0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);

Scoring

There are several places you need to update, to keep score. You added a font and set up the place where the game will display the value already. Now, you need to add the part of the code that does the math and keeps track of the collisions so that the cod can earn his martini points. First, I set up the score variable, right under where I set up my audio objects...

//keeping score

int score=0;

In the section of the program where sprites get drawn, I added the below chunk of code to show the score. You may remember it from the rectangle sample where the screen turns red if the person is hit by a block; in this case, we start tallying martini goodness as well as change the screen color (screen color changes, I kept the same):

//Change when the cod hits the martini

if (personHit)

{

device.Clear(Color.Crimson);

output = VictoryMessage;

score = score+1;

// Draw the string for the victory message

spriteBatch.DrawString(Font1, output, FontPos, Color.AntiqueWhite,

0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);

}

else

{

device.Clear(Color.CornflowerBlue);

}

 

You will notice that I setup the score to always rise. This is of course because I want my game to shine sweetness and light upon the player, rather than give them demerits. If there was some action where the cod could lose score (maybe by bumping into an obstacle) I'd have to create code that sets up a scenario wherescore = score-1 .

Cleanup and -- don't do what I did when naming things!

So making the above changes - assuming you loaded the new music and new textures - should be enough to make "When Cods Collide" run at least inside Game Studio. If you want to share the game with other folks, you have to pay attention to a few other things.

When I originally wrote When Cods Collide I only had the Windows platform in mind and I coded directly into a copy of the Rectangle sample. This was A Bad Thing in that there was a bunch of other housekeeping I overlooked that messed me up when I wanted to share the game.

One, was that I decided the game needed to be called "Windows Cod Collision." This meant I had to change references inside of game1.cs

namespace Windows_Cod_Collision

{

/// <summary>

/// This is the main type for your game

/// </summary>

public class Windows_Cod_CollisionGame : Microsoft.Xna.Framework.Game

 

Whereas before it looked like:

namespace RectangleCollision

{

    /// <summary>

    /// This is the main type for your game

    /// </summary>

    public class RectangleCollisionGame : Microsoft.Xna.Framework.Game

Going over to the properties folder in the solution, I changed assemblyinfo.cs  from

using System.Reflection;

using System.Runtime.CompilerServices;

using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following

// set of attributes. Change these attribute values to modify the information

// associated with an assembly.

[assembly: AssemblyTitle("Rectangle Collision")]

[assembly: AssemblyProduct("Rectangle Collision")]

 

to...

using System.Reflection;

using System.Runtime.CompilerServices;

using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following

// set of attributes. Change these attribute values to modify the information

// associated with an assembly.

[assembly: AssemblyTitle("Windows_Cod_CollisionGame")]

[assembly: AssemblyProduct("Windows_Cod_CollisionGame")]

However, even this cleanup above was a bit nuts. Because I was foolish and named my SOLUTION Windows_Cod_Collision. Then, my coworkers mocked me for not making something for the Xbox - after all, we ARE an Xbox team.

Fortunately XNA has a handy tool for this very conversion. However, because I had named my solution not thinking I'd have an Xbox version, when I used the  Cross-Platform Game Project Converter to add an Xbox project based on my Windows Project the Xbox game was then called "Windows Cod Collision" as well!

Dave Weller tells me there's a way to get around the fact I was dumb, but I figure if  leave it as is, but I blog about my idiocy, I will save many people rounds of exasperation - if you don't know which platform you are aiming for, don't name your solution based on platform!

Cross platform tips

The cod game, just like the rectangle sample, is flexible in terms of what platform it supports. You saw in earlier posts how the player input code takes input from Windows keys or the game pad, and the gamescreen area is sized to support them both (which means the safe zones for the sprites are also sized correctly). The cross-platform article has good reminders for other stuff to remember about Windows vs. Xbox games - since I am a noob I am not going to be able to give you any better info than that.

Building, rebuilding, debugging

 

There were many many times where I hit "the little green arrow" in Game Studio/Visual Studio to see if the last few bits of code caused the cods to crash. The gren arrow is located near a menu where you can select Debug, Release or Configuration Manager... . The important thing to know about that menu is that when you finally decide your game doesn't crash, and you build it, you target a folder where it will be compiled.

If you were a Web developer in days of yore as I was, you worked with scripting languages and didn't tend to mess with compiled code. (Or compiled cods, as the case may be... If you are already an advanced coder and annoyed by me talking about compilation), may I remind you to go read Shawn Hargreaves. :)

I'm going to break this down simply (again, probably far too colloquially to be accurate), but you can always refer to real coding books or classes if you want to get the ground-level view of all this.

Essentially, the computer is not the living, breathing thing you curse at every day when your Web page fails to load. It is a boring hunk of intricate metal parts that talks in 0s and 1s. The way a computer fakes being interesting to humans, is that it has been designed to use those 1s and 0s in ways that mimic more interesting creatures' brains.

The computer speaks to itself in machine language, or assembly language. This is one of the "lowest levels" of coding there is, and it takes a very special and precise kind of person to code in assembler. Most people don't - instead, they use compilers to translate their more common programming language code into one that is "native" (read:optimized) to the machine they want to run the code on.

To make your "higher level language" (in this case C#) even vaguely understandable to the target computer or Xbox, you need to compile it. Visual Studio/Game Studio has a compiler built into it, and in Game Studio you specify the kind of project you want (Windows or Xbox) so that Game Studio knows what platform you want to compile it for. A game compiled for the Xbox won't run on a Windows PC very well, or vice versa.

The other element to this which you probably know about already is the XNA Framework, which is what makes a lot of your game code a lot easier to write, but also a lot easier for the code to get at lower level game interfaces like Direct X. Game Studio compiles your C# files into Microsoft Intermediate Language (MSIL), and when its time to run the code on the Xbox or PC, the XNA Framework's just-in-time compiler takes your code all the way to something natively understandable by the target machine. See this MSDN article for more about that....

https://msdn2.microsoft.com/en-us/library/c5tkafs1.aspx

 

So when you "build" your game, that's what you are really doing...compiling it into a more usable state. To put the game into a share-able format that does not expose source code or assets, you need to convert it to a ccgame package. There's a great article about that here: https://msdn2.microsoft.com/en-us/library/bb417502.aspx . For Windows users/games, you can also use other installation package tools to wrap up your build files with the redistributables they need to run the game if they do not have XNA Game Studio.

Myself, I have just beamed my cod game to my Xbox, as per this article, and can play it to the annoyance of all my coworkers. :)