Handling Game Level Updates


Handling Level Updates

What about the level itself? Currently there is no way to render the level, so you should add that now. Add the code from Listing 8.6.
Listing 8.6. Rendering the Level
public void Draw(Device device)
{
    // Render every block
    for each(Block b in blocks)
    {
        // It's possible the block may be null
        if (b != null)
        {
            b.Draw(device);
        }
    }
}

Wow, that's probably the easiest render method yet. You simply loop through every block in the level and, assuming it isn't null, call the Draw method on it. Because the work for rendering the blocks is encapsulated in the Block class, it makes the rendering of the entire level extremely simple. You're still currently missing one important method that the other classes had, namely a way to update the level state every frame. Add the Update method in Listing 8.7 to your class.
Listing 8.7. Updating Level Each Frame
public void Update(float time)
{
    // Update the currently running time
    elapsedTime += time;

    // Calculate the time remaining
    if (maxTime > elapsedTime)
    {
        timeLeft = new TimeSpan(0,0, (int)(maxTime - elapsedTime));
    }

    // Has the player won the game?
    if (GameWon(elapsedTime))
    {
        // Set the variables and return
        isGameOver = true;
        isGameWon = true;
    }
    else if ((elapsedTime >= maxTime) || (totalMoves > maxMoves))
    {
        // Set the variables and quit
        isGameOver = true;
        isGameWon = false;
    }

    // Update the blocks if the game is over
    if ((isGameOver) && (!isGameWon))
    {
        // If this is the first time the game is over,
        // randomly assign a velocity to each block
        if (isFirstGameOver)
        {
            isFirstGameOver = false;
            Random r = new Random();
            int index = 0;
            foreach(Block b in blocks)
            {
                if ((b != null) && (playerIndex != index))
                {
                    // Assign a random velocity
                    float x, y, z = 0.0f;
                    x = (float)r.NextDouble() * Block.MaximumVelocity;
                    y = (float)r.NextDouble() * (Block.MaximumVelocity * 2);
                    z = (float)r.NextDouble() * Block.MaximumVelocity;
                    if (r.Next(50) > 25) { x *= -1; }
                    if (r.Next(50) > 25) { y *= -1; }
                    if (r.Next(50) > 25) { z *= -1; }
                    b.SetBlockVelocity(new Vector3(x, y, z));
                }
                index++;
            }
        }
        foreach(Block b in blocks)
        {
            if (b != null)
            {
                b.UpdateBlock(time);
            }
        }
    }

}

The time parameter passed into this method is the time that has passed since the last frame was rendered or, more specifically, the time that has elapsed since the last call to this method. The current total elapsed time is incremented by this time, and the time remaining to solve the level is calculated (assuming you haven't already run out of time). Obviously, you should check whether the game is won before you check whether the player has lost. (You always want to give the player the benefit of the doubt.) The implementation of this method will be coming in just a few moments.
If the player hasn't won the game yet, you should check whether she's lost the game. The game is lost if the elapsed time exceeds the maximum time for the level or if the total number of moves exceeds the maximum number for the level. The next portion of code causes the blocks to explode off the screen in a nifty effect if the level is lost. First, you need to see whether this is the first time you've made it into this loop, and if it is, you randomly assign a velocity to each block with the exception of the one the player is currently on. After you set the velocity on the blocks, each subsequent call simply calls the UpdateBlock method to move the blocks based on the current velocity.
You still need to implement the GameWon method, so use the code in Listing 8.8 for this method.
Listing 8.8. Checking Whether the Game Is Won
private bool GameWon(float totalTime)
{
    bool won = true;
    numberBlocks = 0;
    numberCorrectBlocks = 0;
    // Just scroll through each block and see if it is the correct color
    for each(Block b in blocks)
    {
        // Is there a block here?
        if (b != null)
        {
            numberBlocks++;
            b.SetBlockTime(totalTime);
            // Is it the right color?
            if (b.BlockColor != finalColor)
            {
                // Nope, you haven't won yet.
                b.SetBlockPulse(true);
                won = false;
            }
            else
            {
                b.SetBlockPulse(false);
                numberCorrectBlocks++;
            }
        }
    }

    return won;
}

The time is passed into this method because while this code scans through the blocks, the time is updated to allow the pulsating code to work correctly. Each time this method is called (which is every frame), the number of blocks and the number of correct blocks are both reset to zero, the method assumes the game is won, and then each block in the level is examined. If there is a block in any given position, the number of blocks is incremented and the time set. If the color of the block is correct (meaning it is equal to the final color), the pulse of the block is turned off, and the number of correct blocks is incremented. If the block isn't the correct color, the pulse is turned on, and the won variable is set to false to signify that the level hasn't been won yet. After each block is examined, the method returns either true if the level has been won or false otherwise and leaves the number of blocks and the number of correct blocks' variables set correctly.
The last thing that you need to add to your Level class is public accessibility for the members which the game engine will need to use. Add the properties in Listing 8.9 to the class to facilitate this.
Listing 8.9. Public Properties
/// 
/// Total number of blocks
/// 
public int NumberBlocks
{
    get { return numberBlocks; }
}
/// 
/// Total number of correct blocks
/// 
public int NumberCorrectBlocks
{
    get { return numberCorrectBlocks; }
}
/// 
/// Get the time remaining in the level
/// 
public string TimeRemaining
{
    get { return timeLeft.ToString(); }
}
/// 
/// The total number of moves taken
/// 
public int TotalMoves
{
    get { return totalMoves; }
}
/// 
/// The total number of moves allowed
/// 
public int MaximumMoves
{
    get { return maxMoves; }
}
/// 
/// Determines if the game is over for any reason
/// 
public bool IsGameOver
{
    get { return isGameOver; }
}
/// 
/// Determines if the game has been won
/// 
public bool IsGameWon
{
    get { return isGameWon; }
}

These standard properties really need no further explanation.

Comments

Popular Posts