Using Managed DirectX to Write a Game

Adding Obstacles (contd.)

Three things left before the obstacles are in the scene: You need to add a call into our obstacle addition method somewhere, you need to make sure you call the update function on each obstacle in the scene, and you need to render the obstacles. Since you will need a method to reset all the member variables back to defaults in order to start a new game, you should just create that function now, and use this method to be our initial call to AddObstacles. Add the method found in Listing 6.13.

Listing 6.13 Loading Default Game Options

/// <summary>
/// Here we will load all the default game options
/// </summary>
private void LoadDefaultGameOptions()
{
  // Road information
  RoadDepth0 = 0.0f;
  RoadDepth1 = -100.0f;
  RoadSpeed = 30.0f;
  // Car data information
  car.Location = RoadLocationLeft;
  car.Speed = 10.0f;
  car.IsMovingLeft = false;
  car.IsMovingRight = false;
  // Remove any obstacles currently in the game
  foreach(Obstacle o in obstacles)
  {
    // Dispose it first
    o.Dispose();
  }
  obstacles.Clear();
  // Add some obstacles
  AddObstacles(RoadDepth1);
  // Start our timer
  Utility.Timer(DirectXTimer.Start);
}

This method takes the various member variables of the classes you might care about and resets them to the defaults. It also takes any existing obstacles in the list, disposes them, and clears the list before refilling the list with new obstacles. It finally starts the timer. You should add the call to this function after you've created the device in the InitializeGraphics method. Do not add this function into the OnDeviceReset method; you only want to call this function when a new game is being started.

// Load the default game options
LoadDefaultGameOptions();

Now you need to add the call to update the obstacles into the OnFrameUpdate method. You want to update every obstacle every frame, so you will need to enumerate them. Add the following into the OnFrameUpdate method before the car's update method:

// Move our obstacles
foreach(Obstacle o in obstacles)
{
  // Update the obstacle, check to see if it hits the car
  o.Update(elapsedTime, RoadSpeed);
}

The last step before having the obstacles in the game engine would be to actually render them. In your OnPaint method, immediately after the car's draw method, you should add similar code for rendering your obstacles:

// Draw any obstacles currently visible
foreach(Obstacle o in obstacles)
{
  o.Draw(device);
}

Try running the game now! As you speed down the road, you pass a few obstacles, but then what's this? It seems that after you pass the first few obstacles, no more ever appear. If you remember correctly, the AddObstacles method only adds the obstacles to one section of road, and you are constantly moving the road sections, yet you never call the method again for these "new" road sections. Update the section of code you use to add new road sections as follows:

// Check to see if we need to cycle the road
if (RoadDepth0 > 75.0f)
{
  RoadDepth0 = RoadDepth1 - 100.0f;
  AddObstacles(RoadDepth0);
}
if (RoadDepth1 > 75.0f)
{
  RoadDepth1 = RoadDepth0 - 100.0f;
  AddObstacles(RoadDepth1);
}

Now things are looking more promising. You have the car speeding down the road, flying by obstacles (and at least currently, flying through obstacles). The obstacles themselves seem a little boring though. You should add a little movement to them. You can make them rotate around as you speed toward them! First, you'll need to add a few new member variables to the obstacle class to control the rotation:

// Rotation information
private float rotation = 0;
private float rotationspeed = 0.0f;
private Vector3 rotationVector;

The speed they rotate and the axis they rotate on should be random so that they appear to all be moving differently. This can be easily accomplished by adding the following two lines at the end of the obstacle class constructor:

rotationspeed = (float)Utility.Rnd.NextDouble() * (float)Math.PI;
rotationVector = new Vector3((float)Utility.Rnd.NextDouble(),
  (float)Utility.Rnd.NextDouble(),(float)Utility.Rnd.NextDouble());

Two things left to do before the obstacles will rotate correctly. First you need to include the rotation into the update function as follows:

rotation += (rotationspeed * elapsedTime);

Nothing unusual here; simply increase the rotation based on the elapsed time and the current (randomly selected) rotation speed. Finally, you will need to actually add a rotation into your world transform so that the obstacles are rendered rotating. Update the two world transform calls as follows:

if (isTeapot)
{
  device.Transform.World = Matrix.RotationAxis(rotationVector, rotation)
    * Matrix.Scaling(ObjectRadius, ObjectRadius, ObjectRadius)
    * Matrix.Translation(position);
}
else
{
  device.Transform.World = Matrix.RotationAxis(rotationVector, rotation)
    * Matrix.Translation(position);
}

Running the game now, you can see the obstacles spinning around randomly as you speed toward them. What's the next step? Well, it would be great if the car would actually collide with the obstacles, and you could keep a score to see how well you've done. Once you start adding the score, you'll also need to maintain game state. Add the following member variables to your main engine in the DodgerGame class:

// Game information
private bool isGameOver = true;
private int gameOverTick = 0;
private bool hasGameStarted = false;
private int score = 0;

All of the game's information is stored here. You know whether or not the game is over, whether the game has started for the first time, the last time the game was over, and the current score of the user playing the game. That's all well and good, but what do you need to actually do with all of this? You should start with the score, since that's what the player is going to care most about anyway. Every time the player passes an obstacle, you will want to increase the score. However, you also want to make the game harder as it progresses, so you should also increase the road's speed so that the obstacles come at the player even faster. First things first: Add a line to reset the score in the LoadDefaultGameOptions so that when a new game is started, the player won't start with points:

car.IsMovingRight = false;
score = 0;

Now, in the OnFrameUpdate method, before you actually move the obstacles, add the following code:

// Remove any obstacles that are past the car
// Increase the score for each one, and also increase
// the road speed to make the game harder.
Obstacles removeObstacles = new Obstacles();
foreach(Obstacle o in obstacles)
{
  if (o.Depth > car.Diameter - (Car.Depth * 2))
  {
    // Add this obstacle to our list to remove
    removeObstacles.Add(o);
    // Increase roadspeed
    RoadSpeed += RoadSpeedIncrement;
    // Make sure the road speed stays below max
    if (RoadSpeed >= MaximumRoadSpeed)
    {
      RoadSpeed = MaximumRoadSpeed;
    }
    // Increase the car speed as well
    car.IncrementSpeed();
    // Add the new score
    score += (int)(RoadSpeed * (RoadSpeed / car.Speed));
  }
}
// Remove the obstacles in the list
foreach(Obstacle o in removeObstacles)
{
  obstacles.Remove(o);
  // May as well dispose it as well
  o.Dispose();
}
removeObstacles.Clear();

What you want to do here is get a list of obstacles that you've already passed (this "list" should only contain one obstacle at a time). For each obstacle in this list, you will want to increase the score, as well as increase the road speed to raise difficulty, and increase the car's movement speed (although not as much as you increase the road's speed). After all that has been completed, you remove these obstacles from the real obstacle list. As you can see, a formula using the current road speed is used when calculating the score, so that as you get farther along, you will get more points. You'll also notice a method we use for the car that we haven't implemented yet. Nothing fancy here:

/// <summary>
/// Increment the movement speed of the car
/// </summary>
public void IncrementSpeed()
{
  carSpeed += SpeedIncrement;
}

Now you will need to add a new method to determine when the car has actually hit one of the obstacles. You should add this check into the obstacle class. Add the following method there:

public bool IsHittingCar(float carLocation, float carDiameter)
{
  // In order for the obstacle to be hitting the car,
  // it must be on the same side of the road and
  // hitting the car
  if (position.Z > (Car.Depth - (carDiameter / 2.0f)))
  {
    // are we on the right side of the car
    if ((carLocation < 0) && (position.X < 0))
      return true;
    if ((carLocation > 0) && (position.X > 0))
      return true;
  }
  return false;
}

Pretty simple stuff here; you check to see if the car is at the same depth as the obstacle and can hit it, and if it is, and it's on the same side of the road, return true. Otherwise, the car and obstacle haven't hit, so you can return false. With this code in now, you need to implement this into the game engine. Replace the obstacle update code with this new and improved code:

// Move our obstacles
foreach(Obstacle o in obstacles)
{
  // Update the obstacle, check to see if it hits the car
  o.Update(elapsedTime, RoadSpeed);
  if (o.IsHittingCar(car.Location, car.Diameter))
  {
    // If it does hit the car, the game is over.
    isGameOver = true;
    gameOverTick = System.Environment.TickCount;
    // Stop our timer
    Utility.Timer(DirectXTimer.Stop);
  }
}

Now, after you update each obstacle, you check to see if it is hitting the car. If it is, the game is over. You set the game settings, and stop the timer.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“To iterate is human, to recurse divine” - L. Peter Deutsch