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

The game-play in this tutorial is very similar to prior tutorials, and almost identical to the 'Pong Game' that we've created in a previous chapter. What's new in this tutorial is that we will create an array of (normal, non-breakable) Blocks.
This tutorial also neatly illustrates the use of polymorphism - the idea that a reference (a variable, a parameter, an element of an array, etc) can refer to any of several (sub-) types of object, and that C# will figure out which method to call at run-time for us.
1. SoccerBall.cs, Paddle.cs, Block.cs, BreakableBlock.cs
These are effectively identical to what was presented in the prior tutorial.
2. Game.cs
We want to have many blocks on the screen, and we'd like to randomly pick whether each one should be a normal Block, or a BreakableBlock. In order to do this efficiently, we'll use an array. Each slot in the array will hold a reference to either a normal Block, or a BreakableBlock.
|
private
const float LEFT_PADDLE_X = 3f;
private
const float RIGHT_PADDLE_X = 97f;
private
const float
BLOCK_X = 20f;
private
const float
BLOCK_SPACE = 10f;
private
const int
NUM_BLOCKS = 7;
#endregion
//
private
SoccerBall m_TheBall;
// This is the instance to the soccer
ball.
// the paddle
private
Paddle m_LeftPaddle;
private Paddle
m_RightPaddle;
// the blockers private Block[] m_Blocks;
|
As you can see above, we define three new named constants. The last one, NUM_BLOCKS, defines how many blocks we should (randomly) create. BLOCK_X is the X part of the (X,Y) location of the first (left-most) block. BLOCK_SPACE specifies that we'll leave 10 units of space between each of the blocks.
The array that will contain references to each of the individual
block objects is declared below, using this line:
// the blockers
private Block[] m_Blocks;
You'll notice that we're choosing to declare a single array of references to Block objects. We do this because each slot in the array can be used to refer to a Block object, or any object that's a subclass of the Block class, just like we saw with individual reference variables in the prior tutorial.
|
m_Blocks = new
Block[NUM_BLOCKS];
float xPos = BLOCK_X;
for (int
i = 0; i < NUM_BLOCKS; i++)
{
if (XNACS1Base.RandomFloat()
> 0.5)
m_Blocks[i] = new
Block(xPos);
else
m_Blocks[i] = new
BreakableBlock(xPos);
xPos += BLOCK_SPACE; }
|
The above code is new to InitializeWorld, and located at the end of the method.
The above logic starts by allocating memory for an array of
references to Block (or BreakableBlock) objects, using the following
line:
m_Blocks = new
Block[NUM_BLOCKS];
Next, xPos is initialized to hold the value BLOCK_X, which is the X part of the (X,Y) location of the left-most block.
The remainder of the code consists of a loop the iterates NUM_BLOCKS
number of times, and for each iteration, randomly creates either a
Block, or a BreakableBlock.
if (XNACS1Base.RandomFloat()
> 0.5)
m_Blocks[i] = new
Block(xPos);
else
m_Blocks[i] = new
BreakableBlock(xPos);
You'll notice that if we create a new Block object, we assign it to slot i in the array. However, if we instead create a BreakableBlock, we still assign it to slot i in the array. As you saw in the prior tutorial, a variable (including an array slot) that refers to a Block can either refer to a Block, or any subclass of Block.
|
m_LeftPaddle.CollideWithSoccer(m_TheBall);
m_RightPaddle.CollideWithSoccer(m_TheBall);
for (int
i = 0; i < NUM_BLOCKS; i++)
m_Blocks[i].CollideWithSoccer(m_TheBall);
EchoToTopStatus("Left Thumb Stick to
move the left paddle, Right thumb stick to move the right paddle");
EchoToBottomStatus("Current ball
position:" + m_TheBall.Center); |
In order to make sure that the program can detect a collision between the soccer ball and ANY block (normal or breakable), we need to call the CollideWithSoccer method on each and every block object. We do that in the above code, using a loop to iterate through the array. For each slot in the array, we call the CollideWithSoccer method on whatever type of object we find in that slot. C# will notice that CollideWithSoccer is declared to be virtual, and will thus automatically call the version of the method that's specific to the type of the object..
4. An example of how this all works
Let's examine what happens when the UpdateWorld method is called. For now, let's assume that the first slot in the array refers to a Block object, and that the second slot in the array refers to a BreakableBlock object.
The framework (XNA, and the XNACS1Lib library) call the UpdateWorld method in the Game1.cs file.
The program executes all the statements in the method sequentially from
top to bottom, until it gets to:
for (int
i = 0; i < NUM_BLOCKS; i++)
m_Blocks[i].CollideWithSoccer(m_TheBall);
At this point it initializes i to be 0, checks that
i is less than NUM_BLOCKS ( seven ), and then calls the CollideWithSoccer method
on the first object in the array, which is Block object.
The CollideWithSoccer method in the Block
class is executed sequentially from top to bottom,
until it gets to:
PlayCollisionCue();
At
which point the C# executes the version of the PlayCollisionCue on the
Block class, thus
playing the "Block" sound.
i is incremented by the i++ part of the for loop, C# then checks that i (1) is less than NUM_BLOCKS (7), and then calls the CollideWithSoccer method in the BreakableBlock object.
The CollideWithSoccer method (on the
BreakableBlock
class) is executed sequentially from top to bottom, until it gets to:
PlayCollisionCue();
At which point the C# executes the version of the PlayCollisionCue on the BreakableBlock class, thus playing the "Breakable" sound.
FURTHER EXERCISES: