To make the most use of this and its previous post, you should download the XNA  2D Collision detection sample and look over its code and documentation in Game Studio 2.0.

As mentioned before, I have no doubt that experienced  programmers will be bored or appalled by all this, so if that's you, you can always move on to anything blogged by Shawn Hargreaves.

And ya, after having these epiphanies with our sample, I did make an XNA game  (with music by Mad Malcolm!) called "When Cods Collide." The end of this blog series will have me talking about what I did to make that game.

Intro

In the previous post dissecting the XNA  2D Collision detection sample I talked about getting images into your sprites, drawing your sprites, and general ideas around how the 2d game universe is set up. In this post I’m going to take you through the concepts involved in moving the person around on the screen, setting up random blocks to fall on his head, and what the heck collision actually means. (I had to learn all of this, in order to eventually come up with my When Cods Collide game.)

-          How does the game know what the player is doing?

-          How to let people quit your game

-          How the player input can move the “Person” sprite around

-          How the game code defines when blocks are spawned

-          How the game code defines what “collision” means between Person and Blocks

How Does the Game Know What the Player is Doing?

Where we last left  the Rectangle Collision code, the lower part of the sample was starting to make sense, at least the SpriteBatch part . We had dissected the draw method that allows the game to present the images to the user.

But it’s not enough to show up in life, you have to have some pizzazz. Let’s go through a section higher up in the sample around how we make that person move around.  My reference for this section is the Input Overview found here: http://msdn2.microsoft.com/en-us/library/bb203903.aspx

 

 

// Get input

            KeyboardState keyboard = Keyboard.GetState();

            GamePadState gamePad = GamePad.GetState(PlayerIndex.One);

 

KeyboardState is a structure. Structures or structs tend to be a value type while regular classes are a reference type.  "Arrgh!" you say. "What does that sentence mean in English?"

 

Reference types tend to hold references to where data is (think about texture2d for example) whereas a structure or struct tends to hold related variables together as one unit, such as such as the coordinates of a rectangle or the characteristics of an item in an inventory.

 

It might be easier if I called the keyboard structure in the preceding code snippet "Fred" instead of "keyboard" so that you see more of what is happening. As used above, the word “keyboard”  is our new instance of something, not a preset class tied to the XNA Framework.

KeyboardState Fred= Keyboard.GetState();

So here, I am asking the framework to create a KeyboardState structure, which is a pre-determined one in the XNA Framework. I am calling that structure Fred. Fred’s value has been set to the values returned when I do Keyboard.GetState() .  Or as MSDN put it in the Input Overview , I called static GetState method from the Keyboard class.

 

Our input code in the sample also covers the Xbox controller (GamePad). However, it’s also required for the Xbox that you indicate which player’s controller is providing the input. This is called the index of the player you wish to query. The player associated with a controller is visible on the Ring of Light on the controller.

 

How to Make the Game Quit

 

Our sample contains a courteous game – it lets people quit if they want to, and this is good practice for all the XNA games you create, that you allow peopleto quit (people have to eat sometime!).

 

Here the code is condensed for both Windows or an Xbox input - so that if the back button is pressed or the escape key is down in Windows, the game exists (this game, exits).

 

 

            // Allows the game to exit

            if (gamePad.Buttons.Back == ButtonState.Pressed ||

                keyboard.IsKeyDown(Keys.Escape))

            {

                this.Exit();

            }

 

 

How Do We Get the “Person” to Move?

Ok, so we’ve asked our various technical bits what their state is in order to move the person around. Now, how do we get the person to move?

 

Our person in this game really only goes left and right here or along the X-axis. That makes it a little easier. Below you will see two “if” statements.

Think of them as working like this:

 

If (what’s in the parentheses is true) do the stuff in the { curly braces }.

 

If you see any strange math symbols, it helps to have the MSDN C# list of operators handy.

Ok so here’s the code….

 

            // Move the player left and right with arrow keys or d-pad

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

                gamePad.DPad.Left == ButtonState.Pressed)

            {

                personPosition.X -= PersonMoveSpeed;

            }

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

                gamePad.DPad.Right == ButtonState.Pressed)

            {

                personPosition.X += PersonMoveSpeed;

            }

 

So in the first if statement, what it’s saying in English is -  IF  the keyboard left key, or the gamepad left button is pressed, THEN the state of the controller (whichever kind) is pressed. More than that, it returns a sense of “true” to the If statement, so the statement then can do the stuff inside the  { } . To be more clear, let’s look at those curly braces…

            {

                personPosition.X -= PersonMoveSpeed;

            }

The PersonMoveSpeed is a constant integer and is set previously to 5 elsewhere in the sample code.  So doing stuff in the curly  braces means hitting the left button will take the Person’s initial position value (personPosition.X), subtract PersonMoveSpeed from it, and then call that new value personPosition.X. If we ever do something to the Person’s position again, the new value will be what the game, not the initial starting position.  In C#, x-=y translates to new value of x=x-y, where the x-y is done once in order to get the new x  (it doesn’t keep checking itself).

You can now tell easily that the right button on the keyboard or controller increases the value of the Person’s X coordinate – that is, sets it further to the right in Cartesian coordinates.

Big Blocks are Falling on My Head

Ok enough about the Person. Let’s get some blocks to fall on his head. I’m skipping to the top of the sample game.cs file where variables and classes are setup, in order to make them available to the game, in order to talk about some other concepts.

// Blocks

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

        float BlockSpawnProbability = 0.01f;

        const int BlockFallSpeed = 2;

 

        Random random = new Random();

This snippet has a bunch of new concepts for us, so we will take it line by line.

The first line creates a new List<Vector2> class called blockPositions. It sets up a float variable called BlockSpawnProbability that holds only floating number values, or since it’s a pain to discuss, think of it as a fractional amount between 0 and 1, which can have varying amount of decimal places after it .

The line then sets BlockSpawnProbability to 0.01f, or in normal math terms, .01. (Later in the code from the above excerpt, you will see that the random floating point number generated isn’t bigger than the BlockSpawnProbability, no block shows up. If the random floating point number IS bigger than BlockSpawnProbability, then the new block shows up in a location on screen related to the value of the new random number. )

In this code,

  const int BlockFallSpeed = 2;

The blocks always fall at the constant speed of 2.  The word const is a keyword modifier, as is int , which means type of value is an integer. These two keywords apply to the new variable BlockFallSpeed.

The Random class has always annoyed me. Computers don’t really magic up random numbers. Instead, they fake it by having a set range that it tries to pick random numbers from. But random numbers come in handy – we want the player not to know when the blocks are going to appear or where they are going to appear.

So traipsing down to the section of the code commented, “Spawn Falling Blocks.”

// Spawn new falling blocks

            if (random.NextDouble() < BlockSpawnProbability)

            {

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

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

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

            }


Spawning Blocks? Make Mine a Double!

Ok, this code nugget has some other concepts to explore. Bear with us, as it’s doing some complex things underneath the hood.

A  float, or floating point number is a fraction between 0 and 1. The reason we talk about kinds of numbers in ways that normal people don’t care about, is because the computer allocates certain amount of memory – physical space on the hard drive – to store certain kinds of numbers and more space for other kinds.

Float x here is setting up a variable, that we know will contain a floating point number, and the variable will be called x.  We will use this value x in setting up where the new block appears on the x axis). It is stored on 32 bits of memory.

A double is a number stored on 64 bits of memory rather than just 32.  In the example,  random.NextDouble () is calling the NextDouble method of the random class. (Remember from the first post – classes are the bucket, and methods are what you do TO the bucket. In this case the bucket is the random class that holds a random number).  Calling this method gets you a double – that is, a double-precision floating point number greater than or equal to 0.0, and less than 1.0.

Remember what the basics of an if statement are?  If (the stuff in here is true) then {do the stuff in the curly braces}? Here, IF the next random number you get is less than BlockSpawnProbability (remember the value was set before to 0.01) THEN  you get to do all the stuff in the curly braces.

            if (random.NextDouble() < BlockSpawnProbability)

            {

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

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

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

            }

Random.NextDouble is the random value generated by the code, somewhere between 0 and 1.  But to be effective in calculating the position of the new block, we need a float (which gives you less digits after the first nonzero digit).  To convert the double to a float, we use (float) random.NextDouble() in order to convert that value into a floating point number. (The Window class and Random classe made different decisions about what the coin of the realm should be, but fortunately the money changer is just a pair of parentheses away.)

Next, the random value is now multiplied by the amount of horizontal screen space you have left after you take out the unsafe zone of Window.ClientBounds.Width. That new horizontal coordinate, called x, is added to a new vector2 class in the list called BlockPositions.  The vertical coordinate becomes the negative value of the height of the block texture – meaning the top of the block starts at zero position and is not off the screen.

Bounding along with the Boundary Rectangle


     // Get the bounding rectangle of the person

            Rectangle personRectangle =

                new Rectangle((int)personPosition.X, (int)personPosition.Y,

                personTexture.Width, personTexture.Height);

What this does is create a Rectangle class called personRectangle. Rectangle classes take 4 values to describe the rectangle – x, y, width and height. This  personRectangle effectively draws a box around the person. (even if the visual texture shows a stick figure, there is now an invisible rectangle around the texture image on screen.

The i's have it

I think the easiest way of getting you through this next section of updating the blocks positions is to add comments to the code that are not there in the sample. Here goes.

// Update each block

 // set the whether the person is hit

//or not statement to false so you can start from scratch

 

 

   personHit = false;

            for (int i = 0; i < blockPositions.Count; i++)

 

//BlockPositons will have x items in its list,

//our counter will go through them all)

 

            {

                // Animate this block falling

                blockPositions[i] =

                    new Vector2(blockPositions[i].X,

                                blockPositions[i].Y + BlockFallSpeed);

 

//The Block falls, the number value of Y goes up.

//That’s because we count from the top of the screen. See MSDN

//http://msdn2.microsoft.com/en-us/library/Bb203919.screenspace(en-//us,MSDN.10).png

 

                // Get the bounding rectangle of this block

                Rectangle blockRectangle =

                    new Rectangle((int)blockPositions[i].X, (int)blockPositions[i].Y,

                    blockTexture.Width, blockTexture.Height);

 

//Ok so we did to the block what we did to the person

// we setup the rectangle around the block that marks boundary

 

                // Check collision with person

                if (personRectangle.Intersects(blockRectangle))

                    personHit = true;

 

// You can see more about the intersects method on MSDN //http://msdn2.microsoft.com/en-//us/library/microsoft.xna.framework.rectangle.intersects.aspx

 

                // Remove this block if it have fallen off the screen

                if (blockPositions[i].Y > Window.ClientBounds.Height)

                {

                    blockPositions.RemoveAt(i);

 

//The remove the block section is very clever. It takes the Y position and if //the screen’s gone, it takes that position off the list

// http://msdn2.microsoft.com/en-us/library/5cw9x18z(VS.80).aspx

 

 

                    // When removing a block, the next block will have the same index

                    // as the current block. Decrement i to prevent skipping a block.

 

//remember i++? This is the opposite. J

                    i--;

                }

            }

 

What’s hard for me to keep in my head is exactly how the i’s count, but this pattern is pretty standard for counters so if you look upon at it as a template, you can use it for other things.  The code is simply going through the list of positions to draw a block at, and when the block is no longer visible it gets chopped off the list. By the same token, if the new random number beats the probability value, then a new block is generated and its position is added to the list.

To take stock of where we've been: we’ve looked at how the dynamic parts of the game work (the person, the blocks) and the logic that govern their motion.  These are the basic components that make the Rectangle Collision Sample work.  These are the basic components I modified to make my cod game work.

The next step is to post the code highlights of making "When Cods Collide."  In that game, I modified the motion of the player’s sprite (the cod),as well as the falling blocks (they became martinis), added a crude score tracker, and sound.  It will be a bit tricky to organize since unlike the rectangle sample, I can't point you to tutorial text or a code sample online - yet the game.cs is hideously long by this point.  More to come....