Asteroid slalom

It's now time to implement the final feature of this chapter. We are going to implement a slalom race with a twist. In a typical slalom, the point is to race around each obstacle without touching it. To keep things simple, we are going to race through each asteroid. If you successfully pass through each asteroid, you win the race.

Setting up collision detection

In order to determine whether you have passed through an asteroid, we have to implement some 3D collision detection. There are many types of collision detection, but we are going to keep it simple and implement spherical collision detection.

Spherical collision detection is a simple check to see whether the center of two 3D objects are within a certain distance of each other. As our asteroids are spheres, this will be a pretty accurate indication as to whether we have collided with one. The ship, however, is not a sphere, so this technique isn't perfect.

Let's start our collision detection coding by adding the appropriate methods to the Model class. Open Model.h and add the following methods:

const bool IsCollideable();
void IsCollideable(const bool collideable);
const bool CollidedWith(Model* target);
const Vec3 GetCenter() const;
void SetRadius(const float p_radius);
const float GetRadius() const;

Here is how we will use each method:

  • IsCollideable is used to either get or set the m_collideable flag. Objects are set to collide by default. All of the objects in our game are set to collide so that we can detect if the ship has hit an asteroid. However, it is very common to have some objects in a game that you don't collide with. If you set IsCollideable(false), then collision detection will be ignored.
  • CollidedWith is the method that performs the actual collision detection.
  • GetCenter is a helper function that calculates the center point of the object in world space.
  • SetRadius and GetRadius are help functions to manage the collision radius for the object.

We also need to add two variables to track the radius and collision:

 float m_radius;
 bool m_collideable;

Now, open Model.cpp and add the following code to implement the collision methods.

First, we need to define the radius in the constructor. Add the following line of code to the constructor:

 SetRadius(1.0f);
 IsCollideable(true);

Now add the following methods:

const bool Model::IsCollideable()
{
  return m_collideable;
}

void Model::IsCollideable(const bool p_collideable)
{
  m_collideable = p_collideable;
}

const bool Model::CollidedWith(Model* p_target)
{
  if (p_target->IsCollideable() && this->IsCollideable())
  {
    const Vec3 p1 = this->GetCenter();
    const Vec3 p2 = p_sprite->GetCenter();
    
    float y = p2.y - p1.y;
    float x = p2.x - p1.x;
    float z = p2.z - p1.z;
    float d = x*x + y*y + z*z;
    
    float r1 = this->GetRadius() * this->GetRadius();
    float r2 = p_sprite->GetRadius() * p_sprite->GetRadius();
    
    if (d <= r1 + r2)
    {
      return true;
    }
  }
  return false;
}

const Vec3 Model::GetCenter() const
{
 Vec3 center;
 center = GetPosition();
 if (m_IsShip)
 {
  center.z = -m_position.y;
  center.x = m_position.x;
  center.y = m_position.z;
 }
 return center;
}

void Model::SetRadius(const float p_radius)
{
  m_radius = p_radius;
}

const float Model::GetRadius() const
{
  return m_radius;
}
  • IsCollideable and the override are used to either get whether the object can be collided with or get the state of the collision flag.
  • GetCenter returns the current position of the object. As we modeled all of our objects with the object origin at the center, returning the position also returns the center of the object. A more sophisticated algorithm would use the bounding size of the object to calculate the center.
  • GetRadius and SetRadius manage the radius, which is required for the collision check code.
  • CollidedWith is the method that performs all the work. After checking that both the current object and the target objects can collide, then the method performs the following actions:
    • Gets the center point of the two objects
    • Calculates the distance in 3D between the two centers
    • Checks to see whether the distance is less than the sum of the two radii. If so, the objects have collided:
    Setting up collision detection

If you are astute, you will notice that this collision detection is very similar to the collision detection used in RoboRacer2D. We simply added the z dimension to the equations.

Turning on collision

Now, we will implement the collision code in our game. Open SpaceRacer3D.cpp and add the following function just before the Update function:

void CheckCollisions()
{
 bool collision = false;
 for (int i = 0; i < asteroids.size(); i++)
 {
  Model* item = asteroids[i];
  collision = ship->CollidedWith(item);
  if (collision)
  {
   item->IsCollideable(false);
   item->IsVisible(false);
   score++;
   asteroidsHit++;
  }
 }
}

This method performs the following actions:

  • It defines a collision flag.
  • It iterates through all of the asteroids.
  • It checks to see whether the asteroid has collided with the ship.
  • If the asteroid has collided with the ship, we set IsCollideable for the asteroid to false. This stops the collision from occurring multiple times as the ship passes through the asteroid. For our game, we only need to collide with each asteroid once, so this is sufficient.

We need to wire the collision into the Update function. Add the following line to the Update method just after the HandleInput call:

HandleCollisions();

That's it. We have now implemented basic collision detection!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.137.10