Chapter 4. User Input and Collision Detection
As cool as it was to see a nice-looking set of rings spinning around and realize that you’d made that happen yourself, there’s a long way to go with XNA. Although the animated object looked nice, it didn’t do anything, and you had no control over its movement. What fun is a game where there’s no interaction on the part of the player? In this chapter, we explore user input and collision detection as ways to make your game actually do something besides look nice and pretty.
This chapter uses the code that you built at the end of Chapter 3 (the animated three rings sprite). Open that project and make the changes discussed in this chapter there.
More Sprites
If you’re going to have a user-controlled object and build in some collision detection against other objects, you’re going to need at least one more object on the screen. Let’s add another animated sprite to your project.
Instead of using the same three rings image, we’ll use a different image for the second animated sprite. Along with the source code for this book, you’ll find the code for this chapter. In the AnimatedSprites\AnimatedSprites\AnimatedSpritesContent\Images folder, you’ll find an image called skullball.png. Add that image file to the project in the same way you’ve added previous image files (right-click the Content\Images folder in Solution Explorer, select Add→Existing Item, and then browse to the skullball.png image and add it to the solution).
Next, you’ll need to create a number of variables that will allow you to draw and animate the skull ball
sprite. These variables should look somewhat familiar to you, as they
are very similar to the ones you used in Chapter 3 to draw and animate the three rings
sprite. Add the following class-level variables at the top of your
Game1
class:
Texture2D skullTexture; Point skullFrameSize = new Point(75, 75); Point skullCurrentFrame = new Point(0, 0); Point skullSheetSize = new Point(6, 8); int skullTimeSinceLastFrame = 0; const int skullMillisecondsPerFrame = 50;
The skull ball image frames are 75×75 pixels, and there are six columns and eight rows in the sprite sheet. You’ll want to change the names for the variables you’re using in this game to draw and animate the three rings now, to avoid confusion due to having multiple sprites in your game. Add the word “rings” at the beginning of each variable name, and change all the references to those variables; this will help you keep things straight as you move through this chapter. The rings variables should now be declared as:
Texture2D ringsTexture; Point ringsFrameSize = new Point(75, 75); Point ringsCurrentFrame = new Point(0, 0); Point ringsSheetSize = new Point(6, 8); int ringsTimeSinceLastFrame = 0; int ringsMillisecondsPerFrame = 50;
Compile the project and make sure that you don’t have any compilation errors due to the renaming of these variables. If you do, remember that the variable names should be the same as in the previous project; you’ve just added the word “rings” to the beginning of each name. Fix any errors until the game compiles properly.
Chapter 5 will walk you through some basic object-oriented design principles that will make adding new sprites much easier. For now, you just want to get to some user input and collision detection, so let’s add the code for the skull ball animation.
Load your skull ball image into the skullTexture
variable in the LoadContent
method in the same way you loaded
your three rings image:
skullTexture = Content.Load<Texture2D>(@"Images\skullball");
Next, add the code that will move the current frame through the
sequence of frames on the sprite sheet. Remember, this is done in the
Update
method. Because you’re already doing this with the three rings
sprite, you can just copy the code for the three rings animation and
rename the variables to make it work:
skullTimeSinceLastFrame += gameTime.ElapsedGameTime.Milliseconds; if (skullTimeSinceLastFrame > skullMillisecondsPerFrame) { skullTimeSinceLastFrame −= skullMillisecondsPerFrame; ++skullCurrentFrame.X; if (skullCurrentFrame.X >= skullSheetSize.X) { skullCurrentFrame.X = 0; ++skullCurrentFrame.Y; if (skullCurrentFrame.Y >= skullSheetSize.Y) skullCurrentFrame.Y = 0; } }
Finally, you need to draw the sprite on the screen. Remember that
all drawing takes place in the Draw
method. Once again, you already have code in that method that draws the
three rings sprite, and you can just copy that code and change the
variable names to draw the skull ball image. So that the two sprites
aren’t drawn on top of each other, change the second parameter of the
skull ball’s Draw
call to draw the
image at (100, 100) rather than at (0, 0). Your skull ball Draw
call should look like this:
spriteBatch.Draw(skullTexture, new Vector2(100, 100), new Rectangle(skullCurrentFrame.X * skullFrameSize.X, skullCurrentFrame.Y * skullFrameSize.Y, skullFrameSize.X, skullFrameSize.Y), Color.White, 0, Vector2.Zero, 1, SpriteEffects.None, 0);
Compile and run the application at this point and you’ll see both images animating, as shown in Figure 4-1.
Not bad! In only a few moments, you’ve added a completely new animated sprite to your game. Software development often yields moments of excitement and accomplishment, but that feeling seems to be amplified with game development because of the added visual (and later auditory) senses involved. These two animating objects look pretty cool, but things are only just getting interesting. Now you’ll learn how to control the objects on the screen and give your application some user interaction.
User input in XNA is done via a combination of multiple device options: the keyboard, the mouse, the Xbox 360 controller, Xbox 360 peripherals, and the Windows Phone 7 Series touch screen and accelerometer. Mouse input is never available on the Xbox 360 or Windows Phone 7.
In this chapter, you’ll add support for the keyboard, the mouse, and the Xbox 360 controller into your game.
In the previous chapter, we discussed polling versus registering for events. The difference between the two strategies is really never more visible than when dealing with input devices. Traditional Windows programmers are used to registering for events such as key-down events or mouse-move events. With this programming model, the application performs some actions and then, when the application has idle time, the messages are pumped into the application and the events are processed.
In game development, there is no idle time, so it would be too expensive to enable developers to register for events. Instead, it is up to you as the developer to constantly poll input devices asking whether the player has performed any actions on those devices.
That’s a slightly different way of looking at input and other messages and events, but once you figure it out, game development will make a lot more sense.
Keyboard Input
Keyboard input is handled via the Keyboard
class, which is in the Microsoft.XNA.Framework.Input
namespace. The
Keyboard
class has a static method
called GetState
that retrieves the
current state of the keyboard in the form of a KeyboardState
structure.
The KeyboardState
structure contains three key methods that will give you
most of the functionality you’ll need, as shown in Table 4-1.
As an example of the use of the Keyboard
class, if you wanted to check to see
whether the “A” key was pressed, you’d use the following line of
code:
if(Keyboard.GetState( ).IsKeyDown(Keys.A)) // BAM!!! A is pressed!
In this game, you’ll modify the code to allow the user to control the three rings sprite, moving it up, down, left, or right with the arrow keys.
The three rings sprite is currently hardcoded to be drawn at (0,
0), but to move it around the screen, you need to be able to change the
position at which you draw the sprite. You’ll need to use a Vector2
variable to represent the current
position at which to draw the three rings sprite. You’ll also want to
add a variable to represent the speed at which the three rings sprite
will move. Because the speed at which you move the three rings sprite
won’t change during the course of the game, you can make that variable a
constant. Add these class-level variables at the top of your
class:
Vector2 ringsPosition = Vector2.Zero; const float ringsSpeed = 6;
Also, make sure that you change the second parameter of the
SpriteBatch.Draw
method you’re using
to draw the three rings sprite to use your new ringsPosition
variable rather than the
hardcoded position indicated by Vector2.Zero
. The second parameter of that
Draw
method represents the position
at which to draw the sprite.
Now you’ll need to add code to check whether the up, down, left,
or right arrow keys are pressed and, if any of them are, move the
sprite’s position by changing the value of the ringsPosition
variable.
Where should you put the code to check for input? Remember that
there are only two methods to choose from when determining where to put
certain logic into an XNA game loop: Draw
, which is for drawing objects, and
Update
, which is essentially for everything else (keeping score,
moving objects, checking collisions, etc.). So, go ahead and add the
following code to the end of your Update
method, just before the call to
base.Update
:
KeyboardState keyboardState = Keyboard.GetState( ); if (keyboardState.IsKeyDown(Keys.Left)) ringsPosition.X −= ringsSpeed; if (keyboardState.IsKeyDown(Keys.Right)) ringsPosition.X += ringsSpeed; if (keyboardState.IsKeyDown(Keys.Up)) ringsPosition.Y −= ringsSpeed; if (keyboardState.IsKeyDown(Keys.Down)) ringsPosition.Y += ringsSpeed;
What about the code itself? Wouldn’t an
if
/else
statement be more efficient than four if
statements? Well, yes, it would. But an
if
/else
statement would only allow
you to move in one direction at a time, whereas using four separate
if
statements allows you to move
diagonally as well (for example, combining the up and left key
inputs).
Also note that instead of calling the GetState
method of the Keyboard
class in each if
statement, you’re calling it only once and
then reusing the result from that call. This is because the call to
GetState
is fairly expensive, and
this approach reduces the number of times you have to make that
call.
Compile the project and run it. You should see that now you can move your three rings sprite object around the screen, as shown in Figure 4-2.
Mouse Input
XNA provides a Mouse
class to interact with the mouse that behaves very similarly to
the Keyboard
class. The Mouse
class also has a GetState
method that you can use to get data from the mouse in the form of
a MouseState struct
. The Mouse
class also has another method worthy of
note: void SetPosition
(int x
, int
y
). This method will—you guessed it—allow you to set the
position of the mouse. This position is relative to the upper-left
corner of the game window.
The MouseState struct
has several properties that will help you understand what
is happening with the mouse at the particular moment in time when you
called GetState
. These properties are
detailed in Table 4-2.
Property | Type | Description |
| Returns the state of the left mouse button. | |
| Returns the state of the middle mouse button. | |
| Returns the state of the right mouse button. | |
| Returns the total accumulated movement of the scroll wheel since the game started. To find out how much the scroll wheel has moved, compare this value to the previous frame’s scroll wheel value. | |
| Returns the value of the horizontal position of the mouse in relation to the upper-left corner of the game window. If the mouse is to the left of the game window, the value is negative. If the mouse is to the right of the game window, the value is greater than the width of the game window. | |
| Returns the state of additional buttons on some mice. | |
| | Returns the state of additional buttons on some mice. |
| Returns the value of the vertical position of the mouse in relation to the upper-left corner of the game window. If the mouse is above the game window, the value is negative. If the mouse is below the game window, the value is greater than the height of the game window. |
You may have noticed that by default the mouse cursor is hidden
when the mouse is dragged over an XNA game window. If you want to
display the cursor in an XNA window, you can do so by setting the
IsMouseVisible
property of the Game
class to
true
.
Regardless of whether or not the mouse is visible, the MouseState struct
returned from a call to
GetState
will always hold the current
state of the mouse device.
Let’s make the movement of the mouse control the three rings sprite’s movement around the game window. Leave the keyboard controls added in the previous section in place, and you’ll end up with multiple ways to control the sprite.
Because the MouseState
’s
X
and Y
properties tell you the current position of
the mouse cursor, you can just assign the position of the three rings
sprite to the current position of the mouse.
However, because you’re allowing the player to use the keyboard as well, you can’t always just set the three rings sprite’s position to the position of the mouse. If you did, the three rings sprite would stay where the mouse is, regardless of whether the player moved the mouse.
In order to determine whether the mouse has moved, add a
class-level MouseState
variable at
the top of your class:
MouseState prevMouseState;
This variable will keep track of the mouse state from the previous
frame. You’ll use it to compare the previous state to the current state
of the mouse in each frame. If the values of the X
and/or Y
properties are different, you know the player has moved the mouse and
you can move the three rings sprite to the new mouse position.
Add the following code to the end of your Update
method, just before the call to
base.Update
:
MouseState mouseState = Mouse.GetState( ); if(mouseState.X != prevMouseState.X || mouseState.Y != prevMouseState.Y) ringsPosition = new Vector2(mouseState.X, mouseState.Y); prevMouseState = mouseState;
This code will move the three rings sprite to the position of the mouse, but only if the mouse has been moved. If you compile and run at this point, you should see that you are now able to control the rings sprite with the mouse or the keyboard.
Gamepad Input
If you’re developing a game for Windows, you can still program for an Xbox 360 controller. You’ll have to have a wired controller, or you can purchase an Xbox 360 Wireless Receiver for around $20, which will allow you to connect up to four wireless controllers to a PC.
Tip
The wireless Xbox 360 controller actually does come with a wire if you buy the charge pack for that controller. However, there is no data transfer over that cable, so even when it’s plugged in, it’s still a wireless controller. The cable on the charge pack transfers electricity for the charge and nothing more.
Just as XNA provides a Mouse
class for mouse input and a Keyboard
class for keyboard input, it provides a GamePad
class for reading input from an Xbox 360 gamepad. And yes,
that’s right, there’s a GetState
method for the GamePad
class, just as
there is for the other devices. There’s something to be said for
standards, and Microsoft’s XNA Framework is, for the most part, a superb
example of how standardization across a large-scale system (in this
case, a framework and API) can be of such great benefit. Most of the
time, you can tell how to use an object just by understanding the type
of the object and knowing how similarly typed objects function. That’s a
tribute to a great design by the XNA team—kudos to them.
The GetState
method for the GamePad
class
accepts an enum parameter called PlayerIndex
that indicates which player’s controller you want to
access, and it returns a GamePadState
struct
that you can use to get data from the selected
controller. Key properties of the GamePadState
struct
are listed in Table 4-3.
Property | Type | Description |
| Returns a | |
| Returns a | |
| Indicates whether the controller is currently connected to the Xbox 360. | |
| Returns a | |
| Returns a |
The GamePadState struct
contains two methods that will give you most of the functionality you
need. These methods are listed in Table 4-4.
Looking at the properties in Table 4-3, you’ll notice that some of the controls are represented by Boolean or two-state values (either on or off), and others are represented by values that fluctuate between a range of numbers (0 to 1, or −1 to 1). These ranged properties are referred to as analog controls, and because they don’t have a simple on or off value, they offer more accuracy and more precision in a gaming control. You might have noticed that in some games on an Xbox 360 you can move at different speeds with the triggers or thumbsticks. This is because as you press either button in a given direction, the controller will send a signal to the application in varying strengths. This is an important concept to remember when programming against an Xbox 360 controller and a feature that you’ll want to incorporate into games that you develop. We cover how to do that in this section.
All right, let’s add some code that will let you control your sprite with your Xbox 360 gamepad. Just as before, leave the code for the mouse and keyboard there, too, and you’ll have three ways to control your sprite.
Because the thumbsticks can contain X
and
Y
values ranging from −1
to 1
,
you’ll want to multiply those values of the ThumbSticks
property by the ringsSpeed
variable. That way, if the
thumbstick is pressed all the way in one direction, the sprite will move
at full speed in that direction; if the thumbstick is only slightly
pushed in one direction, it will move more slowly in that
direction.
The following code will adjust your sprite’s position according to
how much and in which direction the left thumbstick on player one’s
controller is pressed. Add this code to the Update
method, just below the code for the
keyboard and mouse input:
GamePadState gamepadState = GamePad.GetState(PlayerIndex.One); ringsPosition.X += ringsSpeed * gamepadState.ThumbSticks.Left.X; ringsPosition.Y −= ringsSpeed * gamepadState.ThumbSticks.Left.Y;
Compile and run the application now, and you’ll have full control of your three rings sprite using your Xbox 360 controller.
Let’s spice things up a bit. Using an Xbox 360 controller should be a bit more fun than it currently is. Let’s add a turbo functionality that doubles your movement speed when active. Of course, when moving so rapidly around the screen in turbo mode, you should feel some vibration in your controller due to the dangerous velocity at which you’ll be moving your sprite. You’ve probably felt the vibrations in an Xbox 360 controller before. This type of mechanism is referred to as force feedback, and it can greatly enhance the gameplay experience because it adds yet another sense that pulls the user into the game.
The method SetVibration
will set vibration motor speeds for a controller. The
method returns a Boolean value indicating whether it was successful
(false
means that either the
controller is disconnected or there is some other problem). The method
accepts a player index, and a float value (from 0 to 1) for the left and
right motors of the controller. Set the values to zero to stop the
controller from vibrating. Anything above zero will vibrate the
controller at varying speeds. Modify the code you just added to move the sprite with
the Xbox 360 controller to include the following:
GamePadState gamepadState = GamePad.GetState(PlayerIndex.One); if (gamepadState.Buttons.A == ButtonState.Pressed) { ringsPosition.X += ringsSpeed * 2 * gamepadState.ThumbSticks.Left.X; ringsPosition.Y −= ringsSpeed * 2 * gamepadState.ThumbSticks.Left.Y; GamePad.SetVibration(PlayerIndex.One, 1f, 1f); } else { ringsPosition.X += ringsSpeed * gamepadState.ThumbSticks.Left.X; ringsPosition.Y −= ringsSpeed * gamepadState.ThumbSticks.Left.Y; GamePad.SetVibration(PlayerIndex.One, 0, 0); }
The code first checks to see if the A button on the controller is pressed. If it is, turbo mode is activated, which means that you’ll move the sprite at twice the normal speed and activate the vibration mechanism on the controller. If A is not pressed, you deactivate the vibration and move at normal speed.
Compile and run the game to get a sense of how it works.
As you can see, the gamepad adds a different dimension of input and gives a different feel to the game itself. It’s a powerful tool, but it won’t work well with all game types. Make sure you think about what type of input device is best for the type of game you are creating, because the input mechanism can go a long way toward determining how fun your game is to play.
Keeping the Sprite in the Game Window
You have probably noticed that the rings sprite will
disappear off the edge of the screen if you move it far enough. It’s
never a good idea to have the player controlling an object that is
offscreen and unseen. To rectify this, update the position of the sprite
at the end of the Update
method. If
the sprite has moved too far to the left or the right or too far up or
down, correct its position to keep it in the game window. Add the
following code at the end of the Update
method, just before the call to
base.Update
:
if (ringsPosition.X < 0) ringsPosition.X = 0; if (ringsPosition.Y < 0) ringsPosition.Y = 0; if (ringsPosition.X > Window.ClientBounds.Width - ringsFrameSize.X) ringsPosition.X = Window.ClientBounds.Width - ringsFrameSize.X; if (ringsPosition.Y > Window.ClientBounds.Height - ringsFrameSize.Y) ringsPosition.Y = Window.ClientBounds.Height - ringsFrameSize.Y;
Compile and run the game at this point, and you should be able to move the rings sprite around the screen just as before; however, it should always stay within the game window rather than disappearing off the edge of the screen.
Collision Detection
So, you have a pretty good thing going thus far. Players can interact with your game and move the three rings around the screen—but still there’s not a lot to do. You need to add some collision detection in order to take the next step.
Collision detection is a critical component of almost any game. Have you ever played a shooter game where you seem to hit your target but nothing happens? Or a racing game where you seem to be far away from a wall but you hit it anyway? This kind of gameplay is infuriating to players, and it’s a result of poorly implemented collision detection.
Collision detection can definitely make or break a gameplay experience. The reason it’s such a make-or-break issue is because the more precise and accurate you make your collision-detection algorithms, the slower your gameplay becomes. There is a clear trade-off between accuracy and performance when it comes to collision detection.
One of the simplest and fastest ways to implement collision detection is through the bounding-box algorithm. Essentially, when using a bounding-box algorithm, you “draw” a box around each object on the screen and then check to see whether the boxes themselves intersect. If they do, you have a collision. Figure 4-3 shows the three rings and skull ball sprites with these invisible boxes surrounding the two objects.
To implement the bounding-box algorithm in the current game, you’ll need to create a rectangle for each sprite based on the position of the sprite and the width and height of the frames for that sprite. The code will make more sense if you change the position of the skull ball sprite to a variable, as you’ve done with the rings sprite. Add the following class-level variable, which will be used to hold the position of the skull ball sprite. Also, initialize the variable to the value that you’re currently setting as the position of the sprite when you draw it, (100, 100):
Vector2 skullPosition = new Vector2(100, 100);
Next, pass the skullPosition
variable as the second parameter to the spriteBatch.Draw
call where you actually draw
the skull ball.
OK, now that you have a variable representing the position of the skull ball sprite, you can create a rectangle using that variable and the size of the skull ball frame and check to see whether it intersects with a similarly created rectangle for the rings sprite.
Add the following method to your Game1
class, which will create rectangles for
each sprite using the XNA Framework Rectangle
struct
. The Rectangle struct
has a method called Intersects
that can be used to determine whether two rectangles
intersect:
protected bool Collide( ) { Rectangle ringsRect = new Rectangle((int)ringsPosition.X, (int)ringsPosition.Y, ringsFrameSize.X, ringsFrameSize.Y); Rectangle skullRect = new Rectangle((int)skullPosition.X, (int)skullPosition.Y, skullFrameSize.X, skullFrameSize.Y); return ringsRect.Intersects(skullRect); }
Next, you need to use the new Collide
method to determine whether the objects have collided. If so,
you’ll want to perform some action. In this case, you’re just going to
close down the game by calling the Exit
method if the sprites collide. Obviously, this isn’t something
you’d want to do in a real game, because just quitting the game when
something like a collision occurs will seem like a bug to a player. But
because we just want to see collision detection in action, this will
work for now.
Add the following code to the end of your Update
method, just before the call to
base.Update
:
if (Collide( )) Exit( );
Compile and run the game. If you move your rings object too close to the ball, the application will shut down.
You may notice that the ball and the rings never actually touch. Any idea why this is? If you look at the sprite sheet for the rings (see Figure 4-4), you’ll see that there’s a fair amount of space between the images of each frame. The distance is compounded even further when the large ring rotates horizontally. All that whitespace gets added to the collision check because you’re using your frame size variable as the size of the object when building the rectangle for your collision check.
One way to rectify this is to adjust your sprite sheet to not have so much whitespace. Another way is to create a smaller rectangle for use in the collision detection. This smaller rectangle must be centered on the sprite and therefore needs to be offset slightly from each edge of the actual frame.
To create a smaller rectangle, define an offset variable for each sprite, which will indicate how much smaller in each direction the collision check rectangle is than the overall frame. Add these two class-level variables to your project:
int ringsCollisionRectOffset = 10; int skullCollisionRectOffset = 10;
Next, you’ll use these variables to construct a rectangle that is
slightly smaller than the actual frame size. Adjust your Collide
method as shown here, and you’ll have
more accurate collision detection:
protected bool Collide( ) { Rectangle ringsRect = new Rectangle( (int)ringsPosition.X + ringsCollisionRectOffset, (int)ringsPosition.Y + ringsCollisionRectOffset, ringsFrameSize.X − (ringsCollisionRectOffset * 2), ringsFrameSize.Y − (ringsCollisionRectOffset * 2)); Rectangle skullRect = new Rectangle( (int)skullPosition.X + skullCollisionRectOffset, (int)skullPosition.Y + skullCollisionRectOffset, skullFrameSize.X − (skullCollisionRectOffset * 2), skullFrameSize.Y − (skullCollisionRectOffset * 2)); return ringsRect.Intersects(skullRect); }
Compile and run the game to try out the new collision detection. It should be much more accurate using this method.
Tip
There is a closely related algorithm that uses a sphere instead of a box. You could use that here as well, especially given that your current objects are circular; however, you’ll be using some noncircular objects in future chapters, so stick with the bounding-box method for now.
Even now that you’ve fine-tuned the algorithm a bit, running the application will show that the collision detection is not 100% accurate. In this limited test, the deficiencies are easy to see. The goal in any game, however, is not necessarily to get collision detection 100% accurate, but rather to get it accurate to the point where the player won’t know the difference.
This may sound like cheating, but in reality, it boils down to a performance issue. For example, let’s say you’re working with a sprite that’s not circular, such as an airplane. Drawing a single box around an airplane will yield some very inaccurate collision detection. You can get around that by adding multiple, smaller boxes to your airplane and checking for collisions between each of these smaller boxes and any other object in the game. Such a bounding-box layout is shown in Figure 4-5.
The example on the left will be fairly inaccurate, whereas the one on the right will greatly improve the accuracy of the algorithm. But what problem will you run into? Let’s say you have two planes in your game and you want to see whether they collide. Instead of one set of calculations for the two planes, you now have to compare each box in each plane against each box in the opposite plane. That’s 25 sets of calculations to compare two planes! If you added more planes to your code, the calculations required would go up exponentially and could eventually affect the speed of your game.
There is a way to improve performance by merging the two methods. That is, you can first check for collisions against objects using a box surrounding the entire object, such as the one shown on the left in Figure 4-5. Then, if that collision check comes back as a potential collision, you can dig deeper into the subboxes like the ones on the right in Figure 4-5 and compare those boxes for collisions.
You can tell that collision detection really is a balancing act between performance and accuracy. In spite of all the extra effort, even collision checks made with all the boxes surrounding the plane on the right side of Figure 4-5 won’t be 100% accurate. The goal once again is to make the collision-detection close enough to not adversely affect gameplay or performance.
There is yet another way to speed up collision detection that I should mention. Dividing the game window into a grid-based coordinate system allows you to do a very simple check to determine whether two objects are even close enough to warrant running a collision check. If you keep track of the current grid cell in which each object is positioned, you can check to make sure one object is in the same grid cell as another object before running the collision-detection algorithm on those two objects. This method will save a good number of calculations in each frame and can also positively affect the speed of the game.
What You Just Did
Great job! You have some cool animation, and now you’re checking for collisions while moving the sprites around the screen. Very impressive. Here’s a recap of what you did in this chapter:
You implemented a sprite controlled by a user via a keyboard, a mouse, and an Xbox 360 gamepad.
You implemented force feedback using the Xbox 360 controller.
You implemented collision detection for two animated sprites.
You learned about the balance between accuracy and performance in collision detection.
Summary
Input devices supported in XNA include the keyboard, mouse, and Xbox 360 controller.
The Xbox 360 has several analog inputs that allow for varying degrees of input from a single button.
Collision detection is a constant balance between performance and accuracy. The more accurate an algorithm is, the more of a performance hit is usually incurred.
The bounding-box algorithm for collision detection is one of the simplest and most straightforward algorithms. If you “draw” an imaginary box around each object, you can easily tell which box is colliding with another box.
You can speed up collision detection while improving accuracy by combining methods. Use a large box to determine whether it’s worth the time to check the smaller boxes surrounding sections of an object, or implement a grid-based system to avoid unnecessary collision checks between objects that are not close together.
Do you notice anything that tyrannical dictators, drug lords, and gangbangers have in common? That’s right, typically they don’t use XNA. That’s proof right there that XNA spreads world peace. Make XNA, not war.
Test Your Knowledge: Quiz
What object is used to read input from a mouse?
Fact or fiction: the X and Y coordinates from a mouse as read in an XNA application represent how much the mouse has moved since the previous frame.
What is the difference between an analog input control and a digital input control?
Describe the bounding-box collision-detection algorithm.
Describe the pros and cons of the bounding-box collision-detection algorithm.
What is the ratio of unicorns to leprechauns?
Test Your Knowledge: Exercise
Let’s combine some aspects of this chapter and the previous one. Take the code where we left off at the end of this chapter and modify it to include another nonuser-controlled sprite (use the plus.png image, which is located with the source code for this chapter in the AnimatedSprites\AnimatedSprites\AnimatedSpritesContent\Images folder). Add movement to both nonuser-controlled sprites, as you did in Chapter 3, so that each sprite moves in both the X and Y directions and bounces off the edges of the screen. Add collision detection to the newly added sprite as well. The end result will be a game where you try to avoid two moving sprites. When you hit either sprite, the game ends.
For clarity in working with the plus.png image, the frame size of the sprite sheet is 75×75 pixels, and it has six columns and four rows (note that the rings and skull ball sprite sheets each had six columns and eight rows).
Get Learning XNA 4.0 now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.