Creating the Tankers Project


Load the Visual Studio .NET 2003 environment and create a new C# Windows application project. Call this Tankers if you are planning on matching it with the code included on the CD. The first thing you want to do is use the sample framework as you did in Blockers because you do not need to modify this code at all to work with your new Tankers game. You can either copy the code into your new project or "link" the files from your old project. Personally, I recommend linking because then you have only the one set of source files that are "shared" between multiple projects. The benefit is that any fixes made to one are automatically picked up by all others. The Visual Studio .NET integrated development environment (IDE) way of linking a new source file into your project isn't intuitive, in my opinion. If you didn't realize it was there, you might not even see it. See Figure 11.2 for an example of linking a file to your project.

Figure 11.2. Linking a file.


You could also try copying the gameengine.cs file from Blockers (not linking) because some of the code will be similar, but it's probably easier to start from scratch rather than get yourself in a bind by forgetting to erase the right code or deleting too much. Go ahead and rename the form1.cs file that was automatically generated to gameengine.cs. You want to ensure that you have the references you need, namely Microsoft.DirectX.dll, Microsoft.DirectX.Direct3d.dll, and Microsoft.directx.direct3dx.dll (at least for now). Then, replace the automatically generated code in your game engine with the code in Listing 11.1 (which should look somewhat familiar).

Listing 11.1. The Game Engine Framework
 using System; using System.Configuration; using System.Drawing; using System.Windows.Forms; using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; using Direct3D = Microsoft.DirectX.Direct3D; namespace Tankers {     /// <summary>     /// Main Game Engine     /// </summary>     public class GameEngine : IFrameworkCallback, IDeviceCreation     {         public const string GameName = "Tankers";         public static readonly string MediaPath =               ConfigurationSettings.AppSettings.Get("MediaPath");         public static Material WhiteMaterial;         public static readonly Vector3 UpDirection = new Vector3(                                                      0.0f, 1.0f, 0.0f);         private Framework sampleFramework = null; // Framework for samples         #if (DEBUG)         private Font debugFont = null;         public int numberVerts = 0;         public int numberFaces = 0;         #endif         public bool IsDeviceAcceptable(Caps caps, Format adapterFormat,             Format backBufferFormat, bool windowed)         {             // Skip back buffer formats that don't support alpha blending             if (!Manager.CheckDeviceFormat(caps.AdapterOrdinal, caps.DeviceType,                 adapterFormat, Usage.QueryPostPixelShaderBlending,                 ResourceType.Textures, backBufferFormat))                 return false;             // Skip any device that doesn't support at least a single light             if (caps.MaxActiveLights == 0)                 return false;             return true;         }         public void ModifyDeviceSettings(DeviceSettings settings, Caps caps)         {             // This application is designed to work on a pure device by not             // using any get methods, so create a pure device if supported             // and using HWVP.             if ( (caps.DeviceCaps.SupportsPureDevice) &&                 ((settings.BehaviorFlags & CreateFlags.HardwareVertexProcessing)                   != 0 ) )                 settings.BehaviorFlags |= CreateFlags.PureDevice;         }         private void OnCreateDevice(object sender, DeviceEventArgs e)         {             SurfaceDescription desc = e.BackBufferDescription;             #if (DEBUG)             // In debug mode, use this font to render stats             debugFont = new Font(e.Device, new System.Drawing.Font("Arial", 12));             #endif         }         private void OnResetDevice(object sender, DeviceEventArgs e)         {             SurfaceDescription desc = e.BackBufferDescription; #if (DEBUG)             // Make sure the debug font resets correctly             debugFont.OnResetDevice(); #endif         }         private void OnLostDevice(object sender, EventArgs e)         { #if (DEBUG)             if ( (debugFont != null) && (!debugFont.Disposed) )             {                 // Make sure the debug font gets rid of any resources                 debugFont.OnLostDevice();             } #endif         }         private void OnDestroyDevice(object sender, EventArgs e)         {             // Clean up our objects             try             {     #if (DEBUG)                 // Cleanup the font                 debugFont.Dispose();     #endif             }             catch             {                 System.Diagnostics.Debugger.Log(0, GameEngine.GameName,                                                 "Error during cleanup.");             }         }     } } 

Obviously, this code isn't ready to be compiled, but here is the basic stuff you need to get started, most of which you've seen before from Blockers. The big difference is the material that is marked static, and the debug information includes counts for the number of vertices and faces. The new material is used for rendering; because most everything in the game requires a white material, it makes sense to have one that is shared everywhere. Also, this time, you actually implement a camera class that will perform basic culling (which means it does not render objects it cannot see). To verify it is working, you need to know how many vertices and faces you are drawing.

You'll notice that the white material object hasn't actually been created yet. Because it is static, you only want it to be created once, and there is a mechanism in .NET to handle this for you. Add the creation code you find in Listing 11.2 to your game engine class now.

Listing 11.2. The Game Engine Creation Code
 /// <summary>Create a new instance of the class</summary> public GameEngine(Framework f) {     // Store framework     sampleFramework = f; } /// <summary>Static constructor used to initialize materials</summary> static GameEngine() {     WhiteMaterial = new Material();     WhiteMaterial.DiffuseColor = new ColorValue(1.0f, 1.0f, 1.0f, 1.0f);     WhiteMaterial.AmbientColor = new ColorValue(1.0f, 1.0f, 1.0f, 1.0f); } 

The "normal" constructor here is basically the same as the one from Blockers. When the object is created, the sample framework is simply stored. The next constructor is new, however, and it is called a static constructor. This constructor is called before any other piece of code for this class (whether it is an instance method or static data). Because the white material object still needs creation, this is the perfect place to add the creation code for that object.

The rest of the initialization for the graphics device is similar to the code you wrote for Blockers, and going through it here isn't necessary. Now, you need to add the main method to your application, which appears in Listing 11.3.

Listing 11.3. The Executable Entry Point
 /// <summary> /// Entry point to the program. Initializes everything and goes into /// a message processing loop. Idle time is used to render the scene. /// </summary> static int Main() {     using(Framework sampleFramework = new Framework())     {         GameEngine tankersEngine = new GameEngine(sampleFramework);         // Set the callback functions. These functions allow the sample         // framework to notify the application about device changes, user         // input, and windows messages. The callbacks are optional so you         // need only set callbacks for events you're interested in. However,         // if you don't handle the device reset/lost callbacks, then the sample         // framework won't be able to reset your device since the application         // must first release all device resources before resetting. Likewise,         // if you don't handle the device created/destroyed callbacks then         // the sample framework won't be able to re-create your device resources.         sampleFramework.Disposing += new EventHandler(                                           tankersEngine.OnDestroyDevice);         sampleFramework.DeviceLost += new EventHandler(                                           tankersEngine.OnLostDevice);         sampleFramework.DeviceCreated += new DeviceEventHandler(                                           tankersEngine.OnCreateDevice);         sampleFramework.DeviceReset += new DeviceEventHandler(                                           tankersEngine.OnResetDevice);         sampleFramework.SetKeyboardCallback(new KeyboardCallback(                                           tankersEngine.OnKeyEvent));         // Catch mouse move events         sampleFramework.IsNotifiedOnMouseMove = true;         sampleFramework.SetMouseCallback(new MouseCallback(                                           tankersEngine.OnMouseEvent));         sampleFramework.SetCallbackInterface(tankersEngine);         try         { #if (!DEBUG)             // In retail mode, force the app to fullscreen mode             sampleFramework.IsOverridingFullScreen = true; #endif             // Show the cursor and clip it when in full screen             sampleFramework.SetCursorSettings(true, true);             // Initialize the sample framework and create the desired window             // and Direct3D device for the application. Calling each of these             // functions is optional, but they allow you to set several options             // which control the behavior of the sampleFramework.             sampleFramework.Initialize( false, false, true );             // Parse the command line, handle the default hotkeys,             // and show msgboxes             sampleFramework.CreateWindow("Tankers - The Game");             sampleFramework.CreateDevice( 0, true, Framework.DefaultSizeWidth,                 Framework.DefaultSizeHeight, tankersEngine);             // Pass control to the sample framework for handling the message             // pump and dispatching render calls. The sample framework will             // call your FrameMove and FrameRender callback when there is idle             // time between handling window messages.             sampleFramework.MainLoop();         } #if(DEBUG)         catch (Exception e)         {             // In debug mode show this error (maybe - depending on settings)             sampleFramework.DisplayErrorMessage(e); #else     catch     {         // In release mode fail silently #endif             // Ignore any exceptions here, they would have been handled             // by other areas             return (sampleFramework.ExitCode == 0) ? 1 :                     sampleFramework.ExitCode; // Return an error code here         }         // Perform any application-level cleanup here. Direct3D device         // resources are released within the appropriate callback functions         // and therefore don't require any cleanup code here.         return sampleFramework.ExitCode;     } } 

The idea here is pretty basic: create the game engine, initialize the graphics, and run. It's just what the Blockers game did, actually. If you see any errors, notify the user and bail out of the game. If you planned to release this game on the retail shelves, you would want much more verbose and deductive error handling and probably logging. That would just add clutter to the code here, though, and would do more harm than good for this scenario because your goal is to teach yourself game programming, not error handling. You need to add the OnKeyEvent and OnMouseEvent methods to your class because you can't even compile without them there. (See Listing 11.4.)

Listing 11.4. Handling User Input
 /// <summary> /// Handle keyboard strokes /// </summary> private void OnKeyEvent(System.Windows.Forms.Keys key, bool keyDown,                         bool altDown) { } /// <summary> /// Hook the mouse events /// </summary> private void OnMouseEvent(bool leftDown, bool rightDown, bool middleDown,     bool side1Down, bool side2Down, int wheel, int x, int y) { } 

This code should appear familiar because you did essentially the same thing in Blockers. Now with the boilerplate code in place, you can get started.



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