Implementing the Level
Before you can start implementing the level, you need to add a new code file to your project to hold the level's code. You can call this file level.cs and add it to your project now. The initial implementation of your level class appears in Listing 8.1.
Listing 8.1. The Level Class
public class Level
{
private const float Spacing = 3.35f;
private const int SquareSize = 13;
private const float StartingLocation = (Spacing * SquareSize) / 2.0f;
// The list of blocks that comprise the level
private Block[] blocks = null;
// The final color to win
private BlockColor finalColor;
// Store the player
private Player currentPlayer = null;
// Block index of the player
private int playerIndex;
// Total number of moves made so far
private int totalMoves = 0;
// Maximum moves allowed
private int maxMoves = 0;
// Maximum time allowed
private float maxTime = 0.0f;
// The amount of time that has currently elapsed
private float elapsedTime = 0.0f;
// Amount of time remaining
private TimeSpan timeLeft;
// Is the game over for any reason?
private bool isGameOver = false;
// Did you win the game once it was over?
private bool isGameWon = false;
// Is this the first game over?
private bool isFirstGameOver = true;
// Number of blocks (total)
private int numberBlocks = 0;
// Number of blocks that are currently correct
private int numberCorrectBlocks = 0;
}
The game uses the constants here when determining the location of the blocks within the level. Each level contains a maximum of 13 rows of blocks and a maximum of 13 columns in each row. Not every block in this square needs to be filled, but this number is used as the maximum size of the game board. Also, the blocks are evenly spaced, 3.35 units apart. Considering that the cubes themselves are 3.0 units in size, this spacing makes sense because the blocks do not touch. The starting location constant marks where the beginning block is in world space (that is, blocks in row or column zero).
This list contains all the class variables you need for the entire level class. You will not need to add any more later in the chapter. Obviously, you need an array of blocks (which was originally going to be a class of itself before) and the "final" color each block must be turned to to win the level. You also need to know the player object for the game and the index of the block the player is currently standing on.
Because you need to know the total number of moves the player has made, as well as the maximum number of moves allowed on this level, you need to store them as well. Along with the moves, each level also has a strict time limit, which you also need to store, just as you do the amount of time left.
Because you want the blocks to explode when the game is over, and you want to notify the player about the game status, you need to know whether the level is overand if it is over, whether the level was won or lost. If the game was lost, the first time you want to assign velocities to the blocks to allow them to explode. Finally, you need to store the number of blocks in the level and the number of those that are correct so you can inform the player.
As mentioned in an earlier chapter, the levels themselves are loaded from a file (or the resources of the executableeither is acceptable). Add the constructor in Listing 8.2 to your level class now.
Listing 8.2. Creating an Instance of the Level Class
public unsafe Level(int level, Device device, Player player)
{
// Check the params
if (player == null)
{
throw new ArgumentNullException("player");
}
// Store the current player object
currentPlayer = player;
// First we need to read in the level file. Opening the file will throw an
// exception if the file doesn't exist.
using(FileStream levelFile = File.OpenRead(GameEngine.MediaPath +
string.Format("Level{0}.lvl", level)))
{
// With the file open, read in the level data. First, the maximum
// amount of time you will have to complete the level
byte[] maxTimeData = new byte[sizeof(float)];
levelFile.Read(maxTimeData, 0, maxTimeData.Length);
maxTime = BitConverter.ToSingle(maxTimeData, 0);
// Now read in the maximum moves item
byte[] maxMoveData = new byte[sizeof(int)];
levelFile.Read(maxMoveData, 0, maxMoveData.Length);
maxMoves = BitConverter.ToInt32(maxMoveData, 0);
// Determine how many colors will be used.
byte[] numColorsData = new byte[sizeof(int)];
levelFile.Read(numColorsData, 0, numColorsData.Length);
int numColors = BitConverter.ToInt32(numColorsData, 0);
// Create the block of colors now
BlockColor[] colors = new BlockColor[numColors];
for (int i = 0; i < colors.Length; i++)
{
colors[i] = (BlockColor)levelFile.ReadByte();
}s
// Get the final color now
finalColor = (BlockColor)levelFile.ReadByte();
// Determine if the colors will rotate.
byte[] rotateColorData = new byte[sizeof(bool)];
levelFile.Read(rotateColorData, 0, rotateColorData.Length);
bool rotateColor = BitConverter.ToBoolean(rotateColorData, 0);
// Create the array of blocks now to form the level
blocks = new Block[SquareSize * SquareSize];
int blockIndex = 0;
for (int j = 0; j < SquareSize; j++)
{
for (int i = 0; i < SquareSize; i++)
{
byte tempBlockColor = (byte)levelFile.ReadByte();
if (tempBlockColor != 0xff)
{
Block b = new Block(device, colors, tempBlockColor,
rotateColor);
b.Position = new Vector3(-StartingLocation +
(Spacing * i), -1.5f,
-StartingLocation + (Spacing * j));
blocks[blockIndex++] = b;
}
else
{
blockIndex++;
}
}
}
// Get the default player starting position
byte[] playerIndexData = new byte[sizeof(int)];
levelFile.Read(playerIndexData, 0, playerIndexData.Length);
playerIndex = BitConverter.ToInt32(playerIndexData, 0);
}
UpdatePlayerAndBlock(true, playerIndex);
}s
One of the first things you should notice is the use of the unsafe keyword in the definition of the constructor. This keyword allows you to use things deemed unsafe in C#, such as the sizeof keyword. Soon you will see why this keyword is necessary. After a quick parameter check, and storing the current player object, you actually open a file that will hold the level data, based on the level number. The using keyword here automatically closes the file when you're done with it so you don't need to worry about doing that on your own.
|