| 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 several changes:
Internally, we've refactored some code so that instead of initializing all the 'statistics' (number of bounces, number of misses, etc) in InitializeWorld, the code will now call a separate function to do that.
There are several blocks in the middle of the screen. Each block is labeled with a letter and does not move. If the soccer ball hits one of these blocks, the ball will bounce off the block, and we'll change the message at the top of the screen to remind the player what the last impacted block is.. The blocks are positioned randomly, so that each game will be a little bit different.
Let's examine the source code, change by change
Let's start with the code refactoring. Code refactoring refers to taking some existing code, and changing it in a way that doesn't change the functionality the code effects, but does change how the code is organized. In this case, we'll take several lines from the InitializeWorld routine, and move those lines into their own function. On an immediate level, this will improve the readability of the InitializeWorld function, as it will now be clear that those lines initialize the 'statistics' of the game (i.e., the number of times the ball has bounced off a paddle, the number of times the ball was missed, etc). On a longer-term level, having a function that sets all the statistics to their initial, starting, level will be useful when we allow the user to restart the game. Eventually, when the player restarts the game, we will call this new function to re-set the statistics to their starting levels.
In order to do this, we will first need to replace the code in
InitializeWorld with a call to the new function. The second thing that
we'll do is define the function.
InitializeWorld():
|
protected
override
void
InitializeWorld() { World.SetWorldCoordinate(new Vector2(0,0), 100.0f); // initialize 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);
#region initialize the blocks
// (This region is left folded, for now)
// initialize statistics InitializeStats(); } |
m_NumBounces = 0;
m_SkillLevel =
"Novice";m_BallsMissed = 0;
|
///
<summary> /// Initialize the statistics information /// </summary> private void InitializeStats() { m_BallsMissed = 0; m_NumBounces = 0; m_SkillLevel = "Novice";} |
2. Adding Obstacles: Putting (Labeled) Blocks In The Middle Of The Screen
Now that we've refactored the initialization code, let's add a new feature: we will put a bunch of (labeled) blocks in the middle of the screen. Since we haven't covered the programming topic of "an array of objects" yet, we will need to keep track of each block individually, which we will do using instance variables. We will place the blocks a specific distance apart on the X axis, but randomly choose the height (on the Y axis); in order to make it easy to adjust those values, we'll use constants to define those numbers. Once we've got the instance variables and named constants for the new feature, we'll need to initialize (which involves both creating and placing) all of the blocks. Finally, we'll create a new function to check for a collision between the ball and any of the blocks, which we will call from UpdateWorld.
Let's examine each of those steps in detail:
We need to declare (to define, really) our instance variables before we can use them.
|
<code above this point
omitted for clarity> private XNACS1Rectangle m_LeftPaddle; private XNACS1Rectangle m_RightPaddle; #region random blocks and their positionsprivate XNACS1Rectangle m_ABlock; private XNACS1Rectangle m_BBlock; private XNACS1Rectangle m_CBlock; private const float BLOCK_A_X_POSITION = 40.0f; private const float BLOCK_X_OFFSET = 10.0f; private const float BLOCK_POSITION_MIN_Y = 10.0f; private const float BLOCK_POSITION_MAX_Y = 55.0f; private const float BLOCK_WIDTH = PADDLE_WIDTH; private const float BLOCK_HEIGHT = PADDLE_HEIGHT / 1.5f; #endregion // collect statistics private int m_BallsMissed; <code below this point omitted for clarity> |
InitializeWorld():
|
protected
override
void
InitializeWorld() { // initialize 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); #region initialize the blocks// initilaize the random blocks along the way float blockXPos = BLOCK_A_X_POSITION; m_ABlock = InitializeRectangle(blockXPos, RandomFloat(BLOCK_POSITION_MIN_Y, BLOCK_POSITION_MAX_Y), BLOCK_WIDTH, BLOCK_HEIGHT); m_ABlock.Label = "A"; blockXPos = blockXPos + BLOCK_X_OFFSET; m_BBlock = InitializeRectangle(blockXPos, RandomFloat(BLOCK_POSITION_MIN_Y, BLOCK_POSITION_MAX_Y), BLOCK_WIDTH, BLOCK_HEIGHT); m_BBlock.Label = "B"; blockXPos = blockXPos + BLOCK_X_OFFSET; m_CBlock = InitializeRectangle(blockXPos, RandomFloat(BLOCK_POSITION_MIN_Y, BLOCK_POSITION_MAX_Y), BLOCK_WIDTH, BLOCK_HEIGHT); m_CBlock.Label = "C"; #endregion // initialize statistics InitializeStats(); } |
BLOCK_WIDTH, BLOCK_HEIGHT);
m_ABlock.Label = "A";
|
<code above this point
omitted for clarity> // move the soccer center by velocity m_TheSoccer.Center = m_TheSoccer.Center + m_BallVelocity; #endregion // Check for collision with the blocks BounceOffBlocks(); // simple if tests for world bound CheckWorldBound(); // paddle colliding with the soccer BounceOffPaddles(); <code below this point omitted for clarity> |
|
///
<summary> /// Test for collision with the blocks, if collision occurs, /// flip the sign of the x-component of the velocity. /// </summary> private void BounceOffBlocks() { if (m_ABlock.Collided(m_TheSoccer)) { m_TheSoccer.VelocityX = -m_TheSoccer.VelocityX; EchoToTopStatus( "Last block collision: A-Block");PlayACue( "Break");} else if (m_BBlock.Collided(m_TheSoccer)) { m_TheSoccer.VelocityX = -m_TheSoccer.VelocityX; EchoToTopStatus( "Last block collision: B-Block");PlayACue( "Break");} else if (m_CBlock.Collided(m_TheSoccer)) { m_TheSoccer.VelocityX = -m_TheSoccer.VelocityX; EchoToTopStatus( "Last block collision: C-Block");PlayACue( "Break");} } |
FURTHER EXERCISES: