XNA Game-Themed CS1 Examples (XGC1) Release 2.0 (XNA V3.1) 2/8/2010
Topic: Topic.7.ObjectsAndClasses
Example: Ex_11.ArrayOfBlocks

# Array of Blocks

References:

• Pre-requisite: it is assumed that you have read through the prior tutorials, and are familiar with the concepts covered in those tutorials.

Goals:

• In this tutorial, we will:
• Define one array to contain both Breakable and normal Blocks
• Notice:
• Notice the new m_Blocks array in Game1.cs

1. Obtain the example code

Download and unzip the zip file and you will see an ExampleProgram folder. Open the ExampleProgram folder, the EXE folder contains the compiled program and you can double click on the .sln file to work with the source 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.

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.

• Constants, Instance Variables: 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.

• InitializeWorld(): 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.

• UpdateWorld(): 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.

1. The framework (XNA, and the XNACS1Lib library) call the UpdateWorld method in the Game1.cs file.

2. 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.

3. 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.

4. 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.

5. The CollideWithSoccer method (on the BreakableBlock class) is executed sequentially from top to bottom, until it gets to:

PlayCollisionCue();

6. At which point the C# executes the version of the PlayCollisionCue on the BreakableBlock class, thus playing the "Breakable" sound.

FURTHER EXERCISES: