| 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:

This tutorial adds a new type of block to the game: the BreakableBlock. The BreakableBlock will be deactivated after it's been hit enough times with the soccer ball.
The new class will show us another example of method overriding.
1. SoccerBall.cs, Paddle.cs
These are effectively identical to what was presented in the prior tutorial.
2. Block.cs
The contents of this file are also unchanged from what was presented in the prior tutorial. The file itself has been moved to into a subfolder (named "Blocks"), so that we can keep this basic Block, and the BreakableBlock, together in one easy to find location.
It's worth noting that the Paddle class is NOT being moved to this folder, and yet the Paddle class still inherits from the Block class. From this, you can see that any class can inherit from any other class in the same project, regardless of which files the two classes are in.
3. BreakableBlock.cs
As you can see, this file contains the BreakableBlock class, which inherits from Block. At this point you can read through the constants and instance variables on your own. We will examine each of the three methods that this class has here:
|
///
<summary>
///
Create the nth (out of totalBlocks) rectangle block. Calls the
parent's constructor for the actual construction.
///
</summary>
///
<param name="xPos">X-Position
of the block.</param>
public BreakableBlock(float
xPos)
: base(xPos)
{
m_HitLeft = XNACS1Base.RandomInt(MIN_HIT_TO_BREAK, MAX_HIT_TO_BREAK);
Label = m_HitLeft + ":to
deactivate";
OutsideColor = Color.LightGray;
CenterColor = Color.LightGray;
Texture = "BreakableBlock"; }
|
Note that the constructor starts with a call to the base class's
constructor, which will ensure that all the Block-specific
initialization is done before this method begins to run.
public BreakableBlock(float
xPos)
: base(xPos)
Next, the constructor randomly chooses how many times this particular
block will need to be hit (with the soccer ball) before it's
deactivated:
m_HitLeft = XNACS1Base.RandomInt(MIN_HIT_TO_BREAK, MAX_HIT_TO_BREAK);
Finally, the constructor sets up the texture (which will be displayed immediately) and the color of rectangle. When the BreakableBlock is deactivated the texture will be removed, thus revealing the gray color.
|
///
<summary>
///
Override the audio cue for our specialize cue.
///
</summary>
///
<returns></returns>
override
protected
void PlayCollisionCue()
{
XNACS1Base.PlayACue("Breakable");
} |
This method is implemented in the same way that we've done for both Block and Paddle in the prior tutorial. Note that this version of the method plays a different sound ("Breakable")
|
///
<summary>
///
Override the base class, if the block is currently active, a
collision
///
may cause the block to become inactive.
///
</summary>
///
<param name="theSoccer">The
soccer ball to bounce with.</param>
///
<returns>true
if collided, otherwise returns false.</returns>
override
public bool
CollideWithSoccer(SoccerBall
theSoccer)
{
bool hit =
base.CollideWithSoccer(theSoccer);
if (hit)
{
m_HitLeft--;
if (m_HitLeft > 0)
{
Label = m_HitLeft + ": to
deactivate";
}
else
{
m_IsActive = false;
Label = "Deactivated";
Texture
= null;
}
}
return hit; }
|
What we'd like to do is have the BreakableBlock override the CollideWithSoccer method, and by so doing, replace the implementation in the base class with a more specialized version. Specially, this more specialized version should decrement the 'hit counter' when the soccer ball collides with (hits) the breakable block.
At the same time, we don't want to have to re-write all the code that
we wrote for the CollideWithSoccer method on the Block class. Instead,
we will call the base class's version using the line:
bool hit = base.CollideWithSoccer(theSoccer);
Notice that we call the method and then capture the return value, which we then use in this method.
If the soccer ball has hit this breakable block, then do several
things. Firstly, we'll decrement the hit counter. If the hit
counter is still greater than zero, then we'll update the rectangle's
label and then be done. If m_HitLeft just got decremented to zero,
then we'll set the status of this (breakable) block to being inactive,
change the label to "Deactivated", and then remove the texture (thus
revealing the gray rectangle underneath):
m_IsActive =
false;
Label =
"Deactivated";
Texture =
null;
2. Game.cs
This file is very similar to what was presented in the prior tutorials. The only change is that we want to put several BreakableBlocks onto the screen. Let's examine how this is done:
|
// the blocker
private
Block m_TheBlocker;
// The two breakable blocks.
private
Block m_BB1;
// NOTE: WE DECLARE THE VARIABLES
HERE TO BE BLOCKS private Block m_BB2; // WE WILL CREATE THE OBJECTS TO BE BREAKABLEBLOCKS!!
|
Notice that we declare the m_TheBlocker variable to be a reference to a Block, as we have been doing through many of the previous tutorials.
Notice that we're also declaring the m_BB1 and m_BB2 variables to be references to Blocks, even though we're going to create BreakableBlocks, instead. We can do this because a variable that refers to a Block can either refer to a Block, or any subclass of Block, including the BreakableBlock!
|
m_LeftPaddle = new
Paddle(LEFT_PADDLE_X);
m_RightPaddle = new
Paddle(RIGHT_PADDLE_X);
m_TheBlocker = new
Block(BLOCK_X);
m_BB1 = new
BreakableBlock(BLOCK_X * 0.5f);
m_BB2 = new
BreakableBlock(BLOCK_X
* 1.5f);
}
|
Notice that we create a normal Block object, and then assign it to m_TheBlocker, as we have been doing.
Notice that we also create a BreakableBlock object, and assign it to m_BB1. This will compile and run just fine, because BreakableBlock inherits from Block, and therefore it's ok for m_BB1 (which is a reference to a Block) to refer to any subclass of Block, including BreakableBlock.
We then do the same thing with m_BB2.
|
m_LeftPaddle.CollideWithSoccer(m_TheBall);
m_RightPaddle.CollideWithSoccer(m_TheBall);
m_TheBlocker.CollideWithSoccer(m_TheBall);
m_BB1.CollideWithSoccer(m_TheBall);
m_BB2.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);
|
Notice that we can call the CollideWithSoccer method on all three Block / BreakableBlock objects. This is a safe, valid operation because the CollideWithSoccer is defined in the Block class. Since we know that each of the variables refer to either a Block object, or an object that is a subclass of the Block class, we know that every single object that these variables might possibly refer to either has an overridden (more specialized) version of CollideWithSoccer, or else inherits the version that Block provides.
5. What happens when UpdateWorld is run:
Let's examine what happens when UpdateWorld is run.
The framework (XNA, and the XNACS1Lib library) call the UpdateWorld method in the Game1.cs file.
The program executes all the statements sequentially (including
m_TheBlocker.CollideWithSoccer(m_TheBall);
m_BB1.CollideWithSoccer(m_TheBall);
At this point it calls the CollideWithSoccer method on the
m_BB1 object, which is BreakableBlock
object. Because of this, C# will execute the
CollideWithSoccer method defined on the
BreakableBlock class, even though the m_BB1 variable
is a Block. In other words, it doesn't matter what m_BB1 is (in this
case, a reference to a Block),
what matters is what it refers to (in this case, a
BreakableBlock
object).
The CollideWithSoccer method (on the
BreakableBlock class) is executed sequentially from top to bottom, until it gets to:
PlayCollisionCue();
Instead of just calling the method, C# notices that the method is declared to be virtual on the Block class. At that point, C# asks "Is there a more specialized version that could be used? In particular, is there a more specialized version of the BreakableBlock class?" It examine the BreakableBlock class because this method was called on m_LeftBreakableBlock back in UpdateWorld. C# finds the more specialized version of the PlayCollisionCue on the BreakableBlock class, and calls that version (thus playing the "Breakable" sound)
FURTHER EXERCISES: