Building Breakernoid in MonoGame, Part 3
This is the third article of a series in which you build a clone of classic brick-breaking games called Breakernoid. The end of Part 2 ended with a ball that bounces off the walls and paddle, as well as one row of blocks that can be destroyed.
In this article, you will add several rows of blocks with different colors, some basic sound effects, and power-ups that add some variety to the gameplay.
Catch up on the other articles in this series:
Multicolor Blocks
Right now you have only red blocks, but there are textures for several different colors. The problem is that you need a way to know which texture to load when constructing the block.
So what you should do is add an enum to Block.cs that specifies the color of the block. You'll require this enum to be passed to the constructor of Block, so you can then set the texture as appropriate.
To declare the enum, open up Block.cs and add the following code inside the namespace Breakanoid braces, but before the class declaration:
public enum BlockColor
public enum BlockColor { Red = 0, Yellow, Blue, Green, Purple, GreyHi, Grey }
There are two grey colors because the grey blocks will take two hits to destroy. So the grey blocks will start out as GreyHi, and then when they get hit the first time, they'll switch to regular Grey.
And because you specify that Red = 0, it will automatically assign Yellow = 1, Blue = 2, and so on. This will be useful because you can have an array of integers to specify the block layout that can easily be converted into the enum.
Next, change the constructor of Block so it takes in a BlockColor as its first parameter. Then set textureName based on this BlockColor.
So if it's BlockColor.Red, textureName should be "block_red"; if it's BlockColor.Yellow, it should be "block_yellow," and so on. Watch out for the fact that BlockColor.GreyHi corresponds to "block_grey_hi."
Go back to where you create the blocks in Game1.cs and pass in something for the color. Try a few colors other than red to make sure that it is working properly.
Now that the code supports colors, you will make a 2D array in Game1 that specifies the layout of several rows of blocks. At each element is an integer that specifies the color of block at that location. The syntax for declaring a multidimensional array is a little different in C# from other languages.
Here is the declaration with the data you want to use for now:
int[,] blockLayout = new int[,]{ {5,5,5,5,5,5,5,5,5,5,5,5,5,5,5}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2}, {3,3,3,3,3,3,3,3,3,3,3,3,3,3,3}, {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4}, };
Now that you have this block layout, you should change the block creation loop to be a double loop. To get the length of the array in a specific dimension, you can use blockLayout.GetLength(0) for the number of rows and GetLength(1) for the number of columns.
Start with the top-left element at the position (64, 100) and add 64 to X for every column and 32 to Y for every row. You can take the value at a specific index using blockLayout[row,col]. For example, blockLayout[1,2] would give the element at the second row and third column.
You can then convert (or cast) that value to BlockColor. With the provided array, if you wrote (BlockColor)blockLayout[1,2], it would convert 0 to BlockColor.Red.
Once you've implemented the block creation from this array, you should have a rainbow of blocks that looks like the following figure:
If you play the game now, you might notice that it takes a really long time to clear out the level. Some of this will be fixed when power-ups are added, but in my game I increased the speed of the ball from 350 to 400. Later, you'll also add code that increases the speed as you progress through additional levels.
Now it is time to add support for a GreyHi block to change into a regular Grey block when it gets hit. First, you need to add a new member variable in Block that tracks the color of the block.
Next, create a new public function in Block called OnHit that returns a bool. What you'll do is call this function when you detect a collision between the ball and a block.
For most blocks, it just returns true, which means that the block should be removed. If it's a GreyHi block, however, you'll change the color to regular Grey, load in that new texture, and return false.
Then change the block collision code so it calls OnHit and remove the block only if the return value is true.
Sound Effects
Now you will add some basic sound effects. For now, there are three sounds:
- ball_bounce: When the ball bounces off of the walls or paddle
- ball_hit: When the ball hits a block
- death: When the player loses a life and the ball respawns
In order to use sound effects, you need to add a using Microsoft.Xna.Framework.Audio statement at the top of Game1.cs. Inside the Game1 class, create three SoundEffect member variables (I called them ballBounceSFX, ballHitSFX, and deathSFX).
Next, in Game1.LoadContent, add lines to load the sound effects like so:
ballBounceSFX = Content.Load<SoundEffect>("ball_bounce");
Finally, you should play the sound effect at the appropriate time using the Play function:
ballBounceSFX.Play();
When you run the game, it should now play sound effects when you expect it to, which adds quite a bit to the game.