9780735651579fWe’re excited to announce that Microsoft XNA Game Studio 4.0: Learn Programming Now!, by Rob Miles (ISBN 9780735651579; 464 pages) is now available for purchase!   image

In today’s post, please read an excerpt from Chapter 6, “Creating a Multi-Player Game,” which describes how to detect and use individual button-press events in a game and learn how to create and debug a complex program.

Chapter 6 - Creating a Multi-Player Game

In this chapter, you will

  • Discover how to detect and use individual button-press events in a game.
  • Learn how to create and debug a complex program.
  • Write one of the only 16-player games for the Xbox in the world.

Introduction

Now that you can write programs that process data, read input from the gamepad, and display text and graphics, you can move on to create some proper games. The first games that you are going to create are simple to use and play, but are great fun, particularly if you have large numbers of people around to play them. While you create the behaviors for the games, you also learn some more C# constructions that can be used in later games.

Game Idea: Button-Bashing Mob

One very popular and easy-to-create game is one where a player has to repeatedly press a button as quickly as possible. Players compete against each other, and the winner is the one who can press their button the most in a given time. Because each gamepad has four buttons and the Xbox can support four gamepads, up to 16 players can take part, for maximum button-bashing fun.

Creating the Button-Bash Game

To get started, you need to create an empty project called ButtonBash. This project needs to be able to display text. The best way to do this is to create a new project and then initialize and load the font as for the Big Clock application in Chapter 5, “Writing Text.” Set the size of the font to 30 in the SpriteFont1.spritefont file that you create.

To create the game, you first build a program that counts and displays the presses of a single button on the gamepad. Then you can scale up the program and use more buttons. This is a very common programming technique. “Make a button-bashing game for 16 players” sounds a bit daunting, but “Make a program that counts how many times the B button on gamepad 1 is pressed” is something you can probably do.

Note To complete this program, you are going to take two things that you already know how to do (read buttons on the gamepad and display messages on the screen) and use these abilities to create a game called Button Bash. You do this sort of combining a lot in programming; in fact, you can think of writing programs as stringing a set of behaviors together to get the required result.

Button-Bash Game Data

Your program needs to keep track of the number of times the button has been pressed. You can use an integer to hold the value as follows:

// Game World
int count;

The range of an int variable in C# can go over 2,000,000,000. It’s unlikely that anyone who was not Superman could press a button that number of times in a minute.

Starting the Button-Bash Game

The game is started by the player pressing the Start button on the gamepad to zero the counter. The program handles this in the Update method:

protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
GamePadState pad1 = GamePad.GetState(PlayerIndex.One);
if (pad1.Buttons.Start == ButtonState.Pressed)
{
count = 0;
}
base.Update(gameTime);
}

This program builds on the gamepad reading code that you wrote in Chapter 3, “Getting Player Input.” It creates a GamePadState variable called pad1 and then tests to see if the Start button has been pressed on it. When the Start button is pressed, the conditional statement in the Update method sets count to 0.

Displaying the Button-Bash Count Value

As the game is being played, it must display the current number of presses on the screen for the player to see. You can use a variant of the Draw method in the Big Clock program to display the value in count:

protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
string countString = count.ToString();
Vector2 countVector = new Vector2(50, 400);
spriteBatch.Begin();
spriteBatch.DrawString(font, countString, countVector, Color.Red);
spriteBatch.End();
base.Draw(gameTime);
}

Running this program gives you what you expect: the value 0 displayed on the screen.

Counting Button Presses

Now you need to add the statements to the Update method that count the number of times that the B button has been pressed:

if (pad1.Buttons.B == ButtonState.Pressed)
{
count++;
}

This seems to be what you want; if the condition is true because the button has been pressed, the counter is incremented.

Sample Code: Broken Button Bash All the sample projects can be obtained from the Web resources for this text, which can be found at http://oreilly.com/catalog/9780735651579. The sample project in the 01 Broken Button Bash directory in the resources for this chapter contains a Microsoft XNA Game Studio solution that contains a program that uses the Update method described in this section to implement a test button-bashing program.

You might have gathered from the example title “01 Broken Button Bash” that this won’t work. This is because the Update method is called 60 times a second. If you hold down the button, you find that each time Update is called, the value of count gets one bigger, so the score goes up at a rate of 60 times a second. This is impressive (and might be the basis of other games in the future), but it won’t give you the game you want.

Detecting Changes in the Button Position

You need to find a way of detecting when the state of the button changes from the up to the down position. Your program must increase count only when this happens, not when the button is simply being held down. Figure 6-1 shows the sequence of events when the button is pressed. The Update method is being called at regular intervals. At some point, the B button is pressed. This means that when Update is called the first time in the illustration, it detects that B is up, and the second time it is called, it detects that B has been pressed.

clip_image002

Figure 6-1 Time line for Update calls and the B button

This means that the Update method must perform a test along the lines of “If the button was up last time and is down this time, now the counter must be increased.” The Update method needs to know the state of the button the last time Update was called. It can then test to see if the button state has changed since it was called the last time. You can declare a GamePadState variable to hold this value and create an Update method as follows:

GamePadState oldpad1;
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
GamePadState pad1 = GamePad.GetState(PlayerIndex.One);
if (pad1.Buttons.Start == ButtonState.Pressed)
{
count = 0;
oldpad1 = pad1;
}
if ( oldpad1.Buttons.B == ButtonState.Released &&
pad1.Buttons.B == ButtonState.Pressed )
{
count++;
}
oldpad1 = pad1;
base.Update(gameTime);
}

The variable oldpad1 holds the previous state of the gamepad; at the end of the method, you store the current pad state in it. The test for the change makes use of the AND (&&) logical operator. Only if the previous state of the button was up AND the current state is down is the count value increased. You’ve already seen the OR (||) logical operator, which causes a condition to be true if one or the other condition is true (or both are true). The AND operator is used in the same way but produces a true result if the conditions on each side of it are both true. When the player presses Start to begin the game, the value of oldPad1 is set to the current pad state, so that only changes to the gamepad after Start was pressed are registered.

Note This code is quite simple, but you need to understand exactly how it works. Make sure that you can follow what is going on: the way that Update is called 60 times a second and the way the method makes a copy of the previous gamepad settings at the end of each call.

If you had a really fast player who could press and release a button in less than 1/60 of a second, your program would not detect this, as the up-and-down changes would occur between two calls to the Draw method.

Level and Edge Detectors

The code in the previous section is an edge detector in that it detects a change from one state to another. This is the kind of code that you would use to detect when a game player selects an option or presses a switch. Up until now, you have used the buttons as level detectors in that only whether a button is up or down has been significant. When you design the controls for a game, you need to decide what kind of input you’re using for the control. If you’re creating a driving game, you’d use a level-based signal to control whether the accelerator was pressed and perhaps an edge-triggered signal to control the gear selections made by the player.

Sample Code: Working Button Bash The sample project in the 02 Working Button Bash directory in the resources for this chapter contains an XNA Game Studio solution that contains a program that counts the presses for button B.

Constructing the Complete Game

Now that you know how to make edge detectors, you can go on and create the button-counting code for all 16 buttons in the game. The best way to organize these is to track and examine each controller in turn. For each controller, you need some variables to hold information about the gamepad and the buttons:

// Gamepad 1
GamePadState pad1;
GamePadState oldpad1;
int acount1;
int bcount1;
int xcount1;
int ycount1;
Vector2 apos1 = new Vector2(150, 250);
Vector2 bpos1 = new Vector2(200, 200);
Vector2 xpos1 = new Vector2(100, 200);
Vector2 ypos1 = new Vector2(150, 150);

The top two variables hold the gamepad states. The pad1 variable holds the state of the gamepad during a call of Update. The oldPad1 variable holds the value from the previous call of Update. Then there are counters for each of the buttons on the gamepad. Finally, there are four vectors that position the counters on the screen. The code that runs in the Update method is a variation on the edge detector that you saw previously but is extended to handle all the buttons on the gamepad:

pad1 = GamePad.GetState(PlayerIndex.One);
if (pad1.IsConnected)
{
if (pad1.Buttons.Start == ButtonState.Pressed)
{
acount1 = 0;
bcount1 = 0;
xcount1 = 0;
ycount1 = 0;
// repeat for the other three gamepads
}
if (oldpad1.Buttons.A == ButtonState.Released &&
pad1.Buttons.A == ButtonState.Pressed)
{
acount1++;
}
if (oldpad1.Buttons.B == ButtonState.Released &&
pad1.Buttons.B == ButtonState.Pressed)
{
bcount1++;
}
if (oldpad1.Buttons.X == ButtonState.Released &&
pad1.Buttons.X == ButtonState.Pressed)
{
xcount1++;
}
if (oldpad1.Buttons.Y == ButtonState.Released &&
pad1.Buttons.Y == ButtonState.Pressed)
{
ycount1++;
}
oldpad1 = pad1;
}

This code makes use of the IsConnected property of the GamePadState structure. This property is true only if the gamepad is active, meaning that the program updates the values for the gamepad only when it is connected. Now that you have the game behavior working, you need to add the display part of the game code in the Draw method: This is the code for gamepad 1. A similar sequence of statements will be required for the other three gamepads.

spriteBatch.Begin();
if (pad1.IsConnected)
{
spriteBatch.DrawString(font, acount1.ToString(), apos1, Color.Green);
spriteBatch.DrawString(font, bcount1.ToString(), bpos1, Color.Red);
spriteBatch.DrawString(font, xcount1.ToString(), xpos1, Color.Blue);
spriteBatch.DrawString(font, ycount1.ToString(), ypos1, Color.Yellow);
}
spriteBatch.End();

This code uses the vectors that were set up at the beginning of the program to position the count values in the correct place on the screen. The code also draws the counters only if that gamepad is connected.

Sample Code: Button Bash The sample project in the 03 Multi Player ButtonBash directory in the resources for this chapter contains an XNA Game Studio solution that contains a program that you can use to play 16-Player Button Bash.