| XNA Game-Themed CS1 Examples (XGC1) | |
|
Release 2.0 (XNA V3.1) |
|
Need (library reference):
References:
Goals:
1. Obtain the example code
When the game starts, you'll see a screen that looks similar to this:

The game behaves in a manner that is identical to the what was described for the previous tutorial, with three new features:
The ball now bounces off all edges of the screen, so that the ball can never leave the screen
The ball now bounces off both paddles
The game now keeps track of the total number of times that the ball has bounced off a paddle (either left or right, it doesn't matter which). Once that total exceeds a certain threshold, then the game changes into Expert mode. At this point, the only thing that differentiates Novice mode and Expert mode is a message at the bottom of the screen, but we'll add more stuff in later on.
Let's examine the source code, feature by feature
Let's examine the C#
source code that produces the behavior we see on-screen. Since the code is
nearly identical to the program that was presented in the previous tutorial,
we'll leave out everything, except for code that has changed, or code that is
new. Further, we'll move through the changes feature by feature, staring
with the feature that makes the ball bounce off
all the edges of the screen. This will ensure that the ball will never
leave the screen. There are two changes that make this happen.
UpdateWorld():
|
protected
override
void
UpdateWorld() { <code omitted, for clarity> #endregion // simple if tests for world bound CheckWorldBound(); // paddle colliding with the soccer BounceOffPaddles(); } |
|
///
<summary> /// Check the center position of the soccer ball, if it is outside /// of the world bound, flip the ball velocity accordingly /// </summary> private void CheckWorldBound() { if (m_TheSoccer.CenterX > World.WorldMax.X)m_TheSoccer.VelocityX = -m_TheSoccer.VelocityX; if (m_TheSoccer.CenterX < World.WorldMin.X) m_TheSoccer.VelocityX = -m_TheSoccer.VelocityX; if (m_TheSoccer.CenterY > World.WorldMax.Y) m_TheSoccer.VelocityY = -m_TheSoccer.VelocityY; if (m_TheSoccer.CenterY < World.WorldMin.Y) m_TheSoccer.VelocityY = -m_TheSoccer.VelocityY; } |

m_TheSoccer.VelocityX = -m_TheSoccer.VelocityX;
asks if the X value of the coordinates of the soccer ball's center is less than the X value of the lower left corner. In other words, has the soccer ball gone off the left side of the screen. If so, then execute the code m_TheSoccer.VelocityX = -m_TheSoccer.VelocityX;, which will reverse the ball's horizontal direction. This will cause the ball to bounce off the left side of the screen.
3. Ball Bouncing Off The Paddles:
Let's move on to making the ball bounce of the paddles. This is an important step towards taking the game towards it's final form.
There are two changes that make this happen:
UpdateWorld():
|
protected
override
void
UpdateWorld() { <code omitted, for clarity> #endregion // simple if tests for world bound CheckWorldBound(); // paddle colliding with the soccer BounceOffPaddles(); // the if then else if (m_NumBounces > EXPERT_LEVEL_BOUNCES) EchoToBottomStatus("EXPERT: bounces=" + m_NumBounces); else EchoToBottomStatus("NOVICE: bounces=" + m_NumBounces); } |
|
///
<summary> /// Test collision between the soccer ball and the two paddles, /// if collide flip ball's velocity X-component, and keep track /// of number of bounces. /// </summary> private void BounceOffPaddles() { if (m_RightPaddle.Collided(m_TheSoccer)){ m_TheSoccer.VelocityX = -m_TheSoccer.VelocityX; PlayACue( "Bounce");m_NumBounces = m_NumBounces + 1; } if (m_LeftPaddle.Collided(m_TheSoccer)) { m_TheSoccer.VelocityX = -m_TheSoccer.VelocityX; PlayACue( "Bounce");m_NumBounces = m_NumBounces + 1; } } |
These lines aren't tough to understand, but since they're part of the 'count the bounces and go to expert mode' feature, so we'll leave them for later
4. Going To Expert Mode By Counting Bounces
Let's move on to keeping track of how many times the ball has bounced off the paddles, and once the player has bounced the ball at least 5 times, moving the game to expert mode. The basic strategy that we'll use is to create a variable to hold the current number of bounces, which starts at zero. Each time the ball hits a paddle, we will increase that number by 1. In the UpdateWorld function, we'll check to see if the current number of bounces is above or below the threshold for being in expert mode, and display the appropriate message to the user based on that. Let's go through the changes, one by one:
|
<code above this point
omitted for clarity> private XNACS1Rectangle m_LeftPaddle; private XNACS1Rectangle m_RightPaddle; // collect statistics private int m_NumBounces; public Game1() : base(new Vector2(0.0f, 0.0f), 100.0f)<code below this point omitted for clarity> |
|
protected
override
void
InitializeWorld() { World.SetWorldCoordinate(new Vector2(0,0), 100.0f); // initilaize the soccer ball InitializeSoccer(); // initialize the left and right paddles m_LeftPaddle = InitializeRectangle(LEFT_PADDLE_X, PADDLE_INIT_Y, PADDLE_WIDTH, PADDLE_HEIGHT); m_RightPaddle = InitializeRectangle(RIGHT_PADDLE_X, PADDLE_INIT_Y, PADDLE_WIDTH, PADDLE_HEIGHT); // initialize statistics m_NumBounces = 0; } |
Now that we've got a properly initialized instance variable with which to track the number of bounces, we actually need to increase that value, each time a bounce happens. Let's return to the BounceOffPaddles() function, and see where that happens:
|
private void
BounceOffPaddles() { if (m_RightPaddle.Collided(m_TheSoccer)){ m_TheSoccer.VelocityX = -m_TheSoccer.VelocityX; PlayACue( "Bounce");m_NumBounces = m_NumBounces + 1; } if (m_LeftPaddle.Collided(m_TheSoccer)) { m_TheSoccer.VelocityX = -m_TheSoccer.VelocityX; PlayACue( "Bounce");m_NumBounces = m_NumBounces + 1; }} |
we could instead write:
m_NumBounces++;
In this particular situation, these two different lines of code will produce the exact same effect - the value stored in m_NumBounces will go up by one
|
protected
override
void
UpdateWorld() { <code above this point omitted for clarity> // paddle colliding with the soccer BounceOffPaddles(); // the if then else if (m_NumBounces > 5) EchoToBottomStatus("EXPERT: bounces=" + m_NumBounces); else EchoToBottomStatus("NOVICE: bounces=" + m_NumBounces); } |
EchoToBottomStatus("EXPERT: bounces=" + m_NumBounces);
else
EchoToBottomStatus("NOVICE: bounces=" + m_NumBounces);
}
else
{
EchoToBottomStatus("NOVICE:
bounces=" + m_NumBounces);
}
Since we only have a single statement (the EchoToBottomStatus
command), it's not required that we put in the curly braces.
EchoToTopStatus("EXPERT: bounces=" + m_NumBounces);
}
else
{
EchoToBottomStatus("NOVICE:
bounces=" + m_NumBounces);
EchoToTopStatus("NOVICE: bounces=" + m_NumBounces);
}
Conceptually, what's happening is that the program arrives at the line
if (m_NumBounces
> 5), and asks "is the value of m_NumBounces greater than
five?" In other words, "Has the ball bounced off the paddles more
than five times?" If that's true, then the
program will execute the statement immediately underneath the if clause,
just like normal. In other words, if the ball has bounced off the
paddles more than five times, then we'll echo the message to the bottom
status bar telling the user that they're in expert mode
// the if then else
However, if
that's not true (i.e., m_NumBounces is five, or less than five), then the program will skip over the statement(s)
immediately beneath the if clause, and instead jump to the statement(s)
immediately beneath the else clause, and run those statement(s) instead.
We can represent this code:
if (m_NumBounces > 5)
EchoToBottomStatus("EXPERT: bounces=" + m_NumBounces);
else
EchoToBottomStatus("NOVICE: bounces=" + m_NumBounces);
Using this picture:

One implication of this is that EITHER the if clause (display "EXPERT"), OR the else clause (display "NOVICE") will be executed, BUT NOT BOTH. This means that we will only try to echo one or the other of the messages.
Another implication of this is that one of the two clauses MUST be executed. It is not possible to get to the line with the if, and then somehow skip both the if and the else. This means that we will echo one of the messages - it's impossible to run this code but not echo anything.
We need to declare (to define, really) named constant before we can use it.
|
public
class Game1 : XNACS1Base { <code above this point omitted for clarity>
private const float BALL_VELOCITY_MIN_Y = BALL_VELOCITY_MIN_X / 3.0f; private const float BALL_VELOCITY_MAX_Y = BALL_VELOCITY_MAX_X / 3.0f; // Bounces for Expert level private const int EXPERT_LEVEL_BOUNCES = 5; #endregion
|
|
protected
override
void
UpdateWorld() { <code above this point omitted for clarity> // paddle colliding with the soccer BounceOffPaddles(); // the if then else if (m_NumBounces > EXPERT_LEVEL_BOUNCES ) EchoToBottomStatus("EXPERT: bounces=" + m_NumBounces); else EchoToBottomStatus("NOVICE: bounces=" + m_NumBounces); } |
FURTHER EXERCISES::
|
private
void
CheckWorldBound() { if (m_TheSoccer.CenterX > World.WorldMax.X)m_TheSoccer.VelocityX = -m_TheSoccer.VelocityX; else if (m_TheSoccer.CenterX < World.WorldMin.X)m_TheSoccer.VelocityX = -m_TheSoccer.VelocityX; if (m_TheSoccer.CenterY > World.WorldMax.Y)< m_TheSoccer.VelocityY = -m_TheSoccer.VelocityY; if (m_TheSoccer.CenterY < World.WorldMin.Y) m_TheSoccer.VelocityY = -m_TheSoccer.VelocityY; } |
You'll notice that we've added in an extra else, which now connects the
first two if statements. Pictorially, we can represent these four
lines with the following chart:

|
private
void
CheckWorldBound() { if (m_TheSoccer.CenterX > World.WorldMax.X)m_TheSoccer.VelocityX = -m_TheSoccer.VelocityX; else if (m_TheSoccer.CenterX < World.WorldMin.X)m_TheSoccer.VelocityX = -m_TheSoccer.VelocityX; else if (m_TheSoccer.CenterY > World.WorldMax.Y)m_TheSoccer.VelocityY = -m_TheSoccer.VelocityY; else if (m_TheSoccer.CenterY < World.WorldMin.Y)m_TheSoccer.VelocityY = -m_TheSoccer.VelocityY; } |
|
// paddle colliding with the soccer BounceOffPaddles(); // the if then else if (m_NumBounces > EXPERT_LEVEL_BOUNCES) EchoToBottomStatus( "EXPERT: bounces=" + m_NumBounces);else EchoToBottomStatus( "NOVICE: bounces=" + m_NumBounces); |
The sequencing (the ordering) is important here because if the player
currently has bounced the ball, say, 5 times, and the ball is about to
bounce of a paddle, then by FIRST checking for bounces, then NEXT updating
the message, we will immediately update the message after detecting the
latest bounce.
What if we change the code to look like this:
|
// the if then else if (m_NumBounces > EXPERT_LEVEL_BOUNCES) EchoToBottomStatus( "EXPERT: bounces=" + m_NumBounces);else EchoToBottomStatus( "NOVICE: bounces=" + m_NumBounces);// paddle colliding with the soccer BounceOffPaddles();
|