5. Making the Cheese Bounce off the Bat
Now that you can
detect when the cheese and the bread collide, you need to make the
cheese "bounce" off the bat. Because the bread is horizontal, it makes
sense to bounce the cheese up and down the screen so that whenever the
cheese hits the bat, it reverses its movement in the Y direction. The
code to achieve this is very simple; you do the same thing with the YSpeed as you do when the cheese hits the top or bottom of the game region:
if (cheese.SpriteRectangle.Intersects(bread.SpriteRectangle))
{
cheese.YSpeed = cheese.YSpeed * -1;
}
This code can be placed at the end of the Update method to cause the cheese to bounce off the bat.
6. Strange Bounce Behavior
When you run the game,
you find that it works well, and you can guide the cheese around the
screen successfully. However, you make the mistake of letting your
younger brother have a go, and he’s soon complaining that there’s a bug
in your game. Sometimes the cheese gets "stuck" on the bread. You ask
him to show you what happens, and it turns out that he’s right. It seems
to happen when the bread is moving when it hits the cheese. The cheese
travels along the bread, vibrating up and down as it moves. After some
thought, you work out what’s causing the problem. Figure 3 shows what’s happening.

When the cheese
rectangle and the bread rectangle intersect, the program reverses the
direction of movement of the cheese. Normally, this means that the next
time the position of the cheese is updated, it moves away from the
bread, and the rectangles no longer intersect. However, if the cheese is
moving down and the bread is moving up when they collide, the cheese
goes so far "in" to the bread that, even after the cheese has been
updated, the bread and cheese rectangles still intersect. If this is the
case, the Update method reverses
the vertical direction of movement of the cheese, causing the cheese to
move back into the bread. This continues as the cheese moves along the
bread, following the path shown in Figure 12-3, until it finally escapes off the end. There are a number of ways you can solve this problem:
When
the cheese collides with the bread, the program could stop detecting
collisions for a while, giving the cheese a chance to move clear of the
bread. To implement this, you need to add a variable to count a certain
number of ticks after the collision and not allow collisions until after
that number of ticks.
The
program could move the cheese away from the bread after a collision so
that the two sprite rectangles no longer intersect at the next update.
To implement this, you need to know which direction the cheese is moving
so that you can move it appropriately.
You
could change the rules of the game and tell the player about this
special trick shot where a skillful player can send the cheese in a
particular direction by making it stick to the bat in this way. This
would require no additional programming at all.
The important thing to
remember is that because you own the game universe, including what you
say the game is supposed to do, you can change the rules to suit what
your program does. The Great Programmer doesn’t have this freedom;
usually she’s paid a large sum of money to create a solution that does
what the customer wants. However, quite a few games have turned out the
way they are because of the way the programmer made them work or because
of a bug that turned out to make the game more fun. In this case, you
decide to use the third approach and tell your younger brother that the
game is meant to work like that, and he has found a secret feature.
7. Strange Edge Behavior
Your younger brother
is now very pleased with himself and with you. He is pleased with you
for making a game that rewards clever play and pleased with himself for
finding this new trick in the game. However, this doesn’t last long
because he soon comes back and tells you that he’s found a proper bug in
the game. He can make the cheese go right off the screen and not come
back. You ask him to show you, and sure enough, if he uses the bread to
chase the cheese right to the top of the screen, he can send the cheese
right off the screen. This is definitely a bug, and you can’t pass it
off as a feature.
7.1. Debugging a Running Program
One of the great things
about XNA Game Studio is that you can stop the game and take a look at
what’s happening. Once you’ve persuaded your younger brother to make the
problem happen, you can put a breakpoint into the program and stop it
so that you can look at the values of the variables. You can do this
even as the program is running, either on the Xbox, Zune or Windows PC
You can put a breakpoint in the Update
method by clicking next to the line at which you want it to stop. XNA
Game Studio indicates that a breakpoint has been set by highlighting the
line, as shown in Figure 4.

The
next time the program reaches this statement, it stops, and XNA Game
Studio enters debugging mode. You can then look at the values of the
variables to see what’s going wrong. When you take a look at the values in the cheese sprite, you find that
the X coordinate value is fine, but the Y coordinate is –50, which is very wrong. The cheese Y
coordinate should never get as low as this because the direction of the
cheese movement should reverse when it reaches an edge. You take
another look at the code that does this, and it looks sensible:
if (cheese.Y <= minDisplayY)
{
cheese.YSpeed = cheese.YSpeed * -1;
}
If the cheese Y
value becomes less than the minimum it’s allowed to have, the direction
of movement is reversed to bring it back onto the screen. The program
does this by multiplying the speed of the cheese by –1, which made
perfect sense when you wrote it. You take a look at the cheese YSpeed
and find that for the size of the screen you are using it has been
calculated as 4. This means that next time the cheese is updated, the Y position of the cheese will be changed to –46 (which is still much lower than it’s supposed to be). The result is that the same condition triggers again, reversing the direction of the YSpeed and sending the Y
position of the cheese back to –50. So the cheese remains forever off
the screen, dancing backward and forward just out of view. The problem
happens because the bread collision testing is performed after the
cheese has been made to bounce when it hits the edge of the screen, so
if the cheese repeatedly bounces off the bread when it’s on the edge of
the screen, it can be made to vanish like this.
There are a number of
ways you can fix this bug. You can stop the bread from going too close
to the edges so that it can’t harass the cheese like this, or you can
fix the bouncing problem of the cheese. You can’t really say that this
behavior is a feature, although you could create a completely different
game where the aim was to push all the objects off the screen, perhaps
something called "Herd the Cheese" or "Sweep the Table." However, you
decide to fix the problem.
The problem lies with
the use of multiplication by –1 to change the direction of movement. If
the next update brings the cheese back into the required range, then all
is well, but if by some mischance it doesn’t, you get the dancing
behavior that you’ve just uncovered.
The best way to fix this
is to set the direction of movement of the cheese explicitly to the one
in which you need it to go. Rather than bouncing, where you simply
reverse the sign of the speed value, you should say, "If the cheese Y
position is less than the limit, then make the movement positive so
that this always brings the cheese back onto the screen." Even if the
cheese Y position remains less
than the limit next time, the movement will still be correct and result
in the cheese heading in the right direction.
This turns out to be easy. You can use a method called Abs, which is provided by .NET. The Abs method is held in the Math
class and returns the absolute value or magnitude of a number. The
absolute value of a number is simply its value, if the number is zero or
positive, or the opposite of its value if the number is negative. For
example, the absolute value of –4 is 4. The Math class provides a number of static methods (which are always available) for use in your programs. The Math class is in the System namespace, so you can use it without having to add any using directives to your program. The code to deal with the Y position of the cheese ends up looking like this:
if (cheese.Y + cheese.SpriteRectangle.Height >= maxDisplayY)
{
cheese.YSpeed = Math.Abs(cheese.YSpeed) * -1;
}
if (cheese.Y <= minDisplayY)
{
cheese.YSpeed = Math.Abs(cheese.YSpeed) ;
}
If the cheese is too
high, you make it move downward. If the cheese is too low, you make it
move upward. Now there’s no way the cheese can get stuck off the screen.
Unfortunately
it is still possible to move the bat off the screen, To solve this, you
have to add code to limit the movement of the bread.