Implementing the Main Screen


What you want now is the ability to start your game via the user interface you're about to create. See Figure 12.1 for an example of what the main screen will look like.

Figure 12.1. The main starting screen for Tankers.


How can you use the classes you've already created to make this screen a reality? For this case, it's pretty simple. The majority of the screen consists of a single background (which is the user screen) with two simple buttons on it. If you look at the texture for the buttons, you see that it has quite a few buttons on it, as shown in Figure 12.2.

Figure 12.2. The button texture.


Each button has an off state (the grayish color) and an on state. Notice the extra use of the bottom spot to show a loading image. You'll see this image in use later in the game, but for now, just consider the texture as a good use of texture space. Rather than have all this data stored in multiple small texture files (which is inefficient), you store one larger texture file with all the information instead. How do you actually implement this, though?

First, add a new code file called UiScreens.cs to your project, much as you did for Blockers. This code encompasses the user screens you'll be using (and uses the base classes you already defined in the project). Use the code in Listing 12.8 as your starting point.

Listing 12.8. The Main Screen Class
 using System; using System.Drawing; using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; using Microsoft.Samples.DirectX.UtilityToolkit; namespace Tankers {     /// <summary>     /// The main selection screen for the game     /// </summary>     public class MainUiScreen : UiScreen, IDisposable     {         /// <summary>         /// Current state of the user screen         /// </summary>         private enum ScreenState         {             Main,             Connection,             SelectTank,             SelectHost,             Loading,         }         // Button sizes         private const int ButtonWidth = 208;         private const int ButtonHeight = 58;         // Texture names         private const string MainScreenTitleTexture = "title.png";         private const string ButtonTexture = "Buttons.png";         private Texture buttonTextures = null;         private Texture messageTexture = null;         // Buttons         private UiButton newButton = null;         private UiButton exitButton = null;         // Default to the main screen         private ScreenState currentScreen = ScreenState.Main;         /// <summary>         /// Create the main menu screen         /// </summary>         public MainUiScreen(Device device, int width, int height, Control parent)             : base(device, width, height)         {             // Create the texture for the background             messageTexture = TexturePool.CreateTexture(device,                                              MainScreenTitleTexture);             // Mark the background texture as centered             StoreTexture(messageTexture, width, height, true);             // Create the textures for the buttons             buttonTextures = TexturePool.CreateTexture(device, ButtonTexture);         }         #region IDisposable Members         /// <summary>         /// Clean up any resources         /// </summary>         public override void Dispose()         {             base.Dispose();         }         #endregion     } } 

Rather than have a bunch of different user screens as Blockers did, this time you can take a different approach and have a single user screen handle all your user input needs. You can then determine which one meets your needs the best and suits your style most. Notice first that because the screens are all encompassed in the same class, an enumeration is created for you to track what screen you're on. As you can see here, the possible screens are the main screen (Figure 12.1); the connection screen (where you choose whether you want to host a game or join an existing game); the tank selection screen; and if you've chosen to join an existing game, the screen where you enter the server name. There's also a loading screen between the time the user interface ends and the real game begins.

After the constant and variable declarations, the constructor is pretty basic. It uses the texture pool object you created in the last chapter to load the textures needed for the user interface and, in the case of the title texture, to store that in the user screen. Notice, though, that two buttons are in your list of variables, but you don't actually create them yet. I arranged this part intentionally because you eventually have quite a few more buttons in the user interface, and you want a centralized place to create them all. Add the method in Listing 12.9 to your project, and add a call to this method at the end of the constructor.

Listing 12.9. Creating Buttons
 private void CreateDialogs(Device device, int width, int height, Control parent) {     // Create the new game button     newButton = new UiButton(renderSprite, buttonTextures, buttonTextures,         new Rectangle(ButtonWidth,ButtonHeight * 2,         ButtonWidth, ButtonHeight),         new Rectangle(0, ButtonHeight * 2,         ButtonWidth, ButtonHeight),         new Point((width - ButtonWidth) / 2,         ((height - ButtonHeight) / 2) + (ButtonHeight * 2)),         parent);     newButton.Click += new EventHandler(OnNewButton);     newButton.ShortcutKey = System.Windows.Forms.Keys.Enter;     // Create the quit game button     exitButton = new UiButton(renderSprite, buttonTextures, buttonTextures,         new Rectangle(ButtonWidth,ButtonHeight * 3,         ButtonWidth, ButtonHeight),         new Rectangle(0, ButtonHeight * 3,         ButtonWidth, ButtonHeight),         new Point((width - ButtonWidth) / 2,         ((height - ButtonHeight) / 2) + (ButtonHeight * 3)),         parent);     exitButton.Click += new EventHandler(OnExitButton);     exitButton.ShortcutKey = System.Windows.Forms.Keys.Escape;     // Set up first button states     SetButtonStates(true, false, false, false); } 

The button creation code is pretty much the same type of code you used in Blockers. You can see the code on the included CD for a detailed list of how the first user interface screen is implemented, but rather than regurgitate this stuff you already know, you should look at some of the things you haven't covered yet. What you want is to implement the screen in Figure 12.3.

Figure 12.3. Selecting your host.


Now that's something you haven't done before. Sure, you implemented the text box code earlier this chapter, but now you need to make this work. This code actually requires a new base class to be implemented because the screen consists of more than one background screen. See the texture used in Figure 12.4.

Figure 12.4. The texture used for dialogs.


The top half of this texture makes the background of the dialogs, but the next three lines of text in the texture are various messages that can be drawn on top of that background. The bottom of the texture is what forms the text box that you're writing on. So the single screen you see in Figure 12.3 is actually rendered through a combination of three different pieces of data in this texture (the background, the "Enter Host Name" text, and the text box itself). To handle this part, you want to add a new dialog class to your gui.cs code file. This class essentially allows you to render any section of a texture anywhere onscreen that you want. See Listing 12.10.

Listing 12.10. The Dialog Class
 public class DialogElement {     private Texture textureSource = null;     private Rectangle textureRect;     private Point renderLocation;     /// <summary>     /// Create a new dialog element     /// </summary>     public DialogElement(Texture background, Rectangle source, Point loc)     {         // Store the information         textureSource = background;         textureRect = source;         renderLocation = loc;     }     /// <summary>     /// Render the button in the correct state     /// </summary>     public virtual void Draw(Sprite renderSprite)     {         // Render the element if it exists         if (textureSource != null)         {             // Render to the screen centered             renderSprite.Draw(textureSource, textureRect,                 UiScreen.ObjectCenter, new Vector3(renderLocation.X,                 renderLocation.Y, 1.0f), UiScreen.SpriteColor);         }     } } 

It doesn't get much simpler than that right there. You simply store the texture of the sprite you want to render, the source rectangle of the actual data you plan to render, and the location of the upper-left corner of the position you want to render this sprite to. Then, when you are ready to render, you simply render to that location. The only reason this part is even implemented is for more readable code. It doesn't necessarily help (nor hurt) the actual execution of the game. Add the following variable declarations to handle rendering this screen:

 // Yes button (will be used in more than one spot) private UiButton yesButton = null; private DialogElement dialogBackground = null; private DialogElement enterHostName = null; private UiTextBox hostnameBox = null; 

You want to make sure that you actually create these things sometime, so in your CreateDialogs method you implemented earlier, go ahead and use the code from Listing 12.11 and add it to the end to make sure these objects get created for use.

Listing 12.11. Creating the Enter Host Screen Items
 // Create the yes button yesButton = new UiButton(renderSprite, buttonTextures, buttonTextures,     new Rectangle(ButtonWidth,ButtonHeight * 4,     ButtonWidth, ButtonHeight),     new Rectangle(0, ButtonHeight * 4,     ButtonWidth, ButtonHeight),     new Point((width - ButtonWidth) / 2,     (height - ButtonHeight) - (height / 4 - 20)), parent); yesButton.Click += new EventHandler(OnYesButton); yesButton.ShortcutKey = Keys.Enter; dialogBackground = new DialogElement(dialogTexture, new Rectangle(0,0, 512, 256),     new Point((width - 512) / 2, (height - 256) - 50)); enterHostName = new DialogElement(dialogTexture, new Rectangle(0,384, 512, 64),     new Point((width - 512) / 2, (height-64)/2 + 25)); hostnameBox = new UiTextBox(device, dialogTexture, new Rectangle(0,448, 512, 64),     new Point((width - 512) / 2, (height-64)/2 + 75), parent); 

Here you can see that the actual creation code is equally as simple, and rendering is just as simple. (The code in Listing 12.12 is an excerpt from the rendering method you will find on the included CD.)

Listing 12.12. Rendering the Select Host Screen
 else if (currentScreen == ScreenState.SelectHost) {     dialogBackground.Draw(renderSprite);     enterHostName.Draw(renderSprite);     hostnameBox.Draw(renderSprite, unchecked((int)0xff00AB08));     yesButton.Draw(); } 

As I said, it doesn't get much simpler. I know you're excited to get into the game development, so I try not to bore you with this user interface stuff you've already done in Blockers. (Besides, isn't that what the included CD is for?) However, before you're done with this chapter, I want to point out one more thing: how to combine the user interface with a new class that will be used to render the tank you play as. This part is the Select Your Tank screen, naturally, and you can see an example in Figure 12.5.

Figure 12.5. Picking your tank.




Beginning 3D Game Programming
Beginning 3D Game Programming
ISBN: 0672326612
EAN: 2147483647
Year: 2003
Pages: 191
Authors: Tom Miller

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net