Adding a Movable Car into Your Scene

Now that you have the road rendering, as well as moving through the scene, you should add the object the player will interact with: the car. You could simply put the car variables and constants and code into the main class where we have the road sections, but that just wouldn't be very modular. You will want to maintain the car code separately and in its own class. Create the car class now. From the project menu, select the Add New Item option, and choose to add a new class, named "Car". Make sure you include the using clauses for the Managed DirectX references in this new code file that has been created for you.

What will the car class need to do? Since the car will remain stationary while everything moves below it, the car will not need to move backward or forward. However, given that the car will need to dodge any obstacles in the road, it will need to be able to move left or right. It will also need to be able to render itself. Given this information, you should add the following set of variables in your class:

 // Car constants public const float Height = 2.5f; public const float Depth = 3.0f; public const float SpeedIncrement = 0.1f; private const float Scale = 0.85f; // Car data information private float carLocation = DodgerGame.RoadLocationLeft; private float carDiameter; private float carSpeed = 10.0f; private bool movingLeft = false; private bool movingRight = false; // Our car mesh information private Mesh carMesh = null; private Material[] carMaterials = null; private Texture[] carTextures = null; 

These will control everything you need to control for your car. You have constants for the height and depth of the car, which will remain static (since you are only moving left or right). The speed at which the car's lateral movement can change is also constant. The final constant is because the car's model is slightly larger than the side of the road, so you will need to scale the model down slightly.

The non-constant data in the car class is relatively self-explanatory. You have the current location of the car on the road, which we will default to the left side. You have the diameter of the car, which will be used when it is time to determine when it has collided with an obstacle. You have the current lateral speed of the car (since the road can increase in speed, the car movement speed should increase as well). Of course, you also have our two Boolean variables that determine which direction you are moving, if at all. Lastly, the mesh data is stored.

The constructor for the car class will need to create the mesh object (and its associated structures) and calculate the diameter of the car. Replace the default constructor created for you with the code found in Listing 6.7.

Listing 6.7 Creating a Car Class
 /// <summary> /// Create a new car device, and load the mesh data for it /// </summary> /// <param name="device">D3D device to use</param> public Car(Device device) {     // Create our car mesh     carMesh = DodgerGame.LoadMesh(device, @"..\..\car.x", ref carMaterials, ref carTextures);     // We need to calculate a bounding sphere for our car     VertexBuffer vb = carMesh.VertexBuffer;     try     {         // We need to lock the entire buffer to calculate this         GraphicsStream stm = vb.Lock(0, 0, LockFlags.None);         Vector3 center; // We won't use the center, but it's required         float radius = Geometry.ComputeBoundingSphere(stm,             carMesh.NumberVertices, carMesh.VertexFormat, out center);         // All we care about is the diameter.  Store that         carDiameter = (radius * 2) * Scale;     }     finally     {         // No matter what, make sure we unlock and dispose this vertex         // buffer.         vb.Unlock();         vb.Dispose();     } } 

Creating the mesh is simple, since it's the exact same method you used when creating the road's mesh, just with different variable names. Calculating the diameter of the car (which is next), though, is new and exciting code. What you are really doing is calculating the bounding sphere (the sphere that completely contains all of the points in your mesh) for the car. The Geometry class contains this function, but it requires that you pass in the vertex data you wish to calculate the bounding sphere for.

What you need is to get the vertex data from the mesh. You already know that the vertex data is stored in vertex buffers, so you can use the vertex buffer that's stored in the mesh. In order to read the data from the vertex buffer, you will need to call the lock method. You will learn about the various methods from the vertex buffer in the next chapter; for now, this method will just return all of the vertex data to you in a stream. You can then use the ComputeBoundingSphere method to get the "center" of this mesh and the radius of the sphere. Since you don't care about the center of the mesh, and want to store the diameter, you can just double the radius and store that. However, since you are scaling down the mesh, you should also scale down the diameter. Finally (in your finally block no less), you make sure the vertex buffer we were using has been unlocked and disposed.

Now you should add a method to do the drawing of your car. Since you are storing the location of the car in the class, the only thing you will need for this method is the device to use for the drawing. This method will be strikingly similar to the DrawRoad call, with the only differences being the variables used and the fact that we scale the mesh before translating it. Add the following code:

 /// <summary> /// Render the car given the current properties /// </summary> /// <param name="device">The device used to render the car</param> public void Draw(Device device) {     // The car is a little bit too big, scale it down     device.Transform.World = Matrix.Scaling(Scale,         Scale, Scale) *  Matrix.Translation(carLocation, Height, Depth);     for (int i = 0; i < carMaterials.Length; i++)     {         device.Material = carMaterials[i];         device.SetTexture(0, carTextures[i]);         carMesh.DrawSubset(i);     } } 

Before you actually use the car class, though, you will need to make the private variables you may need access to publicly available. Add this public property list to your car class:

 // Public properties for car data public float Location {     get { return carLocation; }     set { carLocation = value; } } public float Diameter {     get { return carDiameter; } } public float Speed {     get { return carSpeed; }     set { carSpeed = value; } } public bool IsMovingLeft {     get { return movingLeft; }     set { movingLeft = value; } } public bool IsMovingRight {     get { return movingRight; }     set { movingRight = value; } } 

Now, you will need to add a variable to maintain the car class in the main game engine. Add the following somewhere in your variable declaration section of DodgerGame:

 // Car private Car car = null; 

Since you use the device as a parameter to the constructor of the car, you can't actually create the car class until the device has been created. A good place to put the car's creation would be in the OnDeviceReset method. Immediately after creating the road mesh add the following to create your car object:

 // Create our car car = new Car(device); 

Now that you have the car object created, you can update the rendering method to include the car drawing method. After the two DrawRoad methods being called in the OnPaint method, add a call to your draw method from the car object:

 // Draw the current location of the car car.Draw(device); 

Now you have the car rendered on your moving road. How can you control the movement of the car from one side of the road to the other, though? Ignoring the mouse for now, you can assume that the user has a keyboard, and they will use the keyboard to control the car. The arrow keys make perfect sense for this control, so you should use them. Override the OnKeyDown method in the DodgerGame class as seen in Listing 6.8.

Listing 6.8 Handling Keystrokes
 /// <summary> /// Handle key strokes /// </summary> protected override void OnKeyDown(System.Windows.Forms.KeyEventArgs e) {     // Handle the escape key for quiting     if (e.KeyCode == Keys.Escape)     {         // Close the form and return         this.Close();         return;     }     // Handle left and right keys     if ((e.KeyCode == Keys.Left) || (e.KeyCode == Keys.NumPad4))     {         car.IsMovingLeft = true;         car.IsMovingRight = false;     }     if ((e.KeyCode == Keys.Right) || (e.KeyCode == Keys.NumPad6))     {         car.IsMovingLeft = false;         car.IsMovingRight = true;     }     // Stop moving     if (e.KeyCode == Keys.NumPad5)     {         car.IsMovingLeft = false;         car.IsMovingRight = false;     } } 

Nothing strange is going on here. If the Escape key is pressed, the game will be ended by closing the form. If the left or right key is pressed, you set the appropriate "moving" variable true, while setting the opposite moving variable false. If the 5 key on the number pad is pressed, you stop movement of the car completely. So now, running the application and pressing these keys will cause the variables controlling the car to update, but the car itself will not move. You need to add an update function to the car as well. Add the method found in Listing 6.9 into your car class.

Listing 6.9 Updating Car Movements
 /// <summary> /// Update the cars state based on the elapsed time /// </summary> /// <param name="elapsedTime">Amount of time that has elapsed</param> public void Update(float elapsedTime) {     if (movingLeft)     {         // Move the car         carLocation += (carSpeed * elapsedTime);         // Is the car all the way to the left?         if (carLocation >= DodgerGame.RoadLocationLeft)         {             movingLeft = false;             carLocation = DodgerGame.RoadLocationLeft;         }     }     if (movingRight)     {         // Move the car         carLocation -= (carSpeed * elapsedTime);         // Is the car all the way to the right?         if (carLocation <= DodgerGame.RoadLocationRight)         {             movingRight = false;             carLocation = DodgerGame.RoadLocationRight;         }     } } 

This method takes in the elapsed time so that you can maintain the same movement on all computers much like you do with the road. The function itself is quite simple. If one of the movement variables is true, you will move the appropriate direction (based on the elapsed time). You then detect whether you've moved all the way to the correct location, and if you have, you will stop the movement completely. However, this method isn't being called currently, so you will need to update the OnFrameUpdate method in the DodgerGame class to include calling this method. Add the following line to the end of that method:

 // Now that the road has been 'moved', update our car if it's moving car.Update(elapsedTime); 


Managed DirectX 9 Graphics and Game Programming, Kick Start
Managed DirectX 9 Kick Start: Graphics and Game Programming
ISBN: B003D7JUW6
EAN: N/A
Year: 2002
Pages: 180
Authors: Tom Miller

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