Part Ten - Game Screen and Interface

Now that we have a fairly playable game, lets look at the game's interface screen and see what we can to to neaten things up a bit.

Lets start out by designing our game screen overlay. Basically this is a large image that is mostly occupied by a transparent area in the center.

This image will be drawn over our screen after everything else has been draw (except for some text which we will also be adding in this installment). Here is the game screen I put together (I know it looks like two little images, but it is actually one with a big transparent space in the middle):

Save this to your Content\Textures folder and add it to your project.

You can see on the image above we had positions for the number of ships the player has left, the number of superbombs (which we haven't implemented yet, but will do in the PowerUps segment) and locations for the current level and the player's score.

We'll add a few things to our declarations to support the code we will be adding:

        Texture2D t2dGameScreen;
        SpriteFont spriteFont;
        Vector2 vLivesTextLoc = new Vector2(100, 677);
        Vector2 vWaveTextLoc = new Vector2(1065, 663);
        Vector2 vScoreTextLoc = new Vector2(1065, 695);
        Vector2 vStartTextLoc = new Vector2(30, 350);
        Vector2 vGameOverTextLoc = new Vector2(570, 330);

t2dGameScreen will, of course, hold the image above.

spriteFont will contain the Pericles.SpriteFont file we created way, way back in part 2 but haven't yet used.

The Vector2 objects will all hold locations of the text we will be drawing to the screen. The SpriteBatch.DrawString() method takes a vector for the location of the text to be draw, so instead of creating a new Vector2 object every time we want to draw text we will maintain them throughout the game. (Otherwise we would be needlessly creating 5 Vector2 object during every draw loop. It could be argued that many of our variables (like loop control integers, etc) would be more efficient if stored constantly instead of instanciated during the loops, but that will get messy if we do it for too many things).

The other nice thing about having them all in our declarations area is that we can change them easily to reposition our text. I've figured out where they look good on the game screen above (and the title screen in the case of vStartTextLoc) but if you have created your own screens you will want to play with the values to get them in the right spots.

Next we need to update a few of our routines to actually keep score and track the wave we are on, etc. Lets start off with our DestroyEnemy method. Add the following line to the bottom of the method:

            iPlayerScore += 10;

So each enemy the player kills will be worth 10 points.

Next, we need to update our StartNewGame() method to look like this:

        protected void StartNewGame()
            iLivesLeft = 3;
            player.ScrollRate = 0;
            iGameStarted = 1;
            iGameWave = 0;
            iPlayerScore = 0;

We've added a reset for the iGameWave and iPlayerScore variables as well as iLivesLeft and player.ScrollRate.

We have two new pieces of content we need to add in LoadContent():

            t2dGameScreen = Content.Load<Texture2D>(@"Textures\GameScreen");
            spriteFont = Content.Load<SpriteFont>(@"Fonts\Pericles");

Finally, it is time to work on our Draw() method. Fortunately we don't need anything too extensive here, so I won't be pasting the entire method into the web page again. Lets handle drawing on the Title Screen first, where we are going to flash the text "Press START or SPACE to Begin". In your Draw() code, scroll all the way to the bottom and add these lines in the Title Screen Mode region, right after the spriteBatch.Draw(t2dTitleScreen...) call:

                if (gameTime.TotalGameTime.Milliseconds % 1000 < 500)
                    spriteBatch.DrawString(spriteFont, "Press START or SPACE to Begin", 
                        vStartTextLoc, Color.Gold);

Here we are checking the absolute game time, or how many milliseconds have passed since we started the program. We don't care about elapsed time here because then we would have to set up a timer and a comparison variable like we do with our timing code, and we don't really need that complexity here. We use the Modulo operator again to get the remainder of dividing the milliseconds value by 1000. If the remainder is less than 500, we draw the text. Otherwise we don't. Since there are 1000 milliseconds in 1 second, what we are really doing is switching the drawing code on and off every half second.

You can see here how easy drawing text is with XNA 2.0 and beyond. In the past, we had to write a function draw out each character with SpriteBatch.Draw() calls one at a time, stepping through the string until everything had been drawn. All that is gone now thanks to the DrawString() method.

Just above this section of code is the end of the Game Mode draw region. The last thing we do in that code is draw the player explosion. Right after that, just before the end of the region, lets add the following:

                // Draw the Game Screen overlay
                spriteBatch.Draw(t2dGameScreen, new Rectangle(0, 0, 1280, 720), Color.White);
                spriteBatch.DrawString(spriteFont, iLivesLeft.ToString(), 
                    vLivesTextLoc, Color.White);
                spriteBatch.DrawString(spriteFont, iGameWave.ToString(), 
                    vWaveTextLoc, Color.White);
                spriteBatch.DrawString(spriteFont, iPlayerScore.ToString(), 
                    vScoreTextLoc, Color.White);
                // If the player is dead and this is their last life, display
                // "GAME OVER" while waiting for the fPlayerRespawnCount to end.
                if (iProcessEvents == 0 && iLivesLeft == 1)
                    spriteBatch.DrawString(spriteFont, "G A M E   O V E R", 
                        vGameOverTextLoc, Color.Gold);

Here, as you can see, we draw the game screen after all of our actual game drawing is done. This makes sure our game screen "covers up" everything it is supposed to. That is also why we do all of our text drawing after that, so it appears on top of the game screen.

The rest of the code is just a few calls to spriteBatch.DrawString(), using our sprite font and the various variables we want to output, and the vectors we established earlier for the position of the text.

Finally, you can see that we check the value of iProcessEvents and iLivesLeft to see if we should display "GAME OVER" to the screen. The way our control variables work, iProcessEvents is set to false whenever the player runs into an enemy ship. It stays that way for 4 seconds while the player explodes. After that 4 seconds is over, iLivesLeft is decremented and, if it reaches zero, the game returns to Title Screen mode.

So, if you have just died (iProcessEvents==0) and you are on your last life (iLivesLeft==1) then we know that the game is going to exit to the Title Screen in 4 seconds, so draw "GAME OVER".

Fire up your game, and you should have an interface with a score, level number, and lives left!

In the next installment we will add Power Ups to our game. These are things that will spawn occasionally that will make improvements to the player's star fighter.

(Continued in Part 11)


Site Contents Copyright 2006 Full Revolution, Inc. All rights reserved.
This site is in no way affiliated with Microsoft or any other company.
All logos and trademarks are copyright their respective companies.