In the previous part of this chapter, you prepared the ReachPlanet
and CrashWithAsteroid
methods. However, you cannot detect situations when the rocket reaches the planet or is crashed. In this part, you will implement a mechanism of detecting collisions between the rocket and other objects in the game world.
There are many approaches for solving the problem of detecting collisions between objects. One of the common approach is based on an idea of bounding sphere (see the following figure). This approach simplifies the process of finding collisions between elements, because each of them is represented as a sphere. Therefore, you can simply compare distance between centers of two spheres and their radiuses. This approach can be very beneficial for detecting collisions between objects similar to spheres. Such a situation occurs in your game, because the planet is simply a sphere and the asteroid is very similar to it.
The bounding sphere solution can be even further simplified by detecting collisions between a point and a sphere. Such an approach is used in your game to check whether the rocket reaches the planet or hits any asteroid. The sphere is represented by the planet or asteroid and the point is represented by the location of the rocket.
A distance between any two points in three-dimensional space can be calculated with the following formula:
where A
x
, A
y
, A
z
denote coordinates of the first point (A), and B
x
, B
y
, B
z
are coordinates of the other point (B). The calculated distance is then compared with a radius of the sphere. If it is equal or less, the collision occurs. Otherwise, the point is located outside the sphere.
The implementation consists of creating a new class (named CollisionDetector
) and making some minor modifications in the GameRenderer
class.
The mechanism of collision detection is implemented in the CollisionDetector
class. You should not forget to add using
directive for DirectX
in the header file.
The code of the CollisionDetector
class is as follows:
class CollisionDetector { public: bool static IsHit(XMFLOAT3 point, XMFLOAT3 sphere, float radius); };
The class contains only one static method (IsHit
), which takes three parameters, coordinates of the point, coordinates of a center of the sphere, and the radius of the sphere. The method returns a value indicating whether a collision is detected.
An implementation of the IsHit
method is related to the formula presented earlier. Its value is calculated and then compared with the radius. At the end, the result is returned, as shown in the following code:
bool CollisionDetector::IsHit(XMFLOAT3 point, XMFLOAT3 sphere,
float radius)
{
float distance = sqrt(
(point.x - sphere.x) * (point.x - sphere.x)
+ (point.y - sphere.y) * (point.y - sphere.y)
+ (point.z - sphere.z) * (point.z - sphere.z));
return distance <= radius;
}
Some changes in the GameRenderer
class are also required to detect collisions. At the beginning, you include the CollisionDetector.h
file, and then declare three private methods called ReachPlanet
, Crash
, and DetectCollisions
:
void ReachPlanet(); void Crash(); void DetectCollisions();
The first method is called when the rocket reaches the planet. The second is used when the rocket misses the planet or hits any asteroid. The DetectCollisions
method is called to check whether any collision between the rocket and either the target planet or asteroids occurs.
The main code related to detecting collisions is placed in the DetectCollisions
method, as shown in the following block:
void GameRenderer::DetectCollisions()
{
bool isPlanetHit = CollisionDetector::IsHit(
m_rocket.GetLocationSimple(), m_planet.GetLocation(),
m_planet.GetScale());
if (isPlanetHit) { ReachPlanet(); }
bool isAnyAsteroidHit = false;
for (auto a = m_asteroids.begin(); a != m_asteroids.end(); ++a)
{
bool isAsteroidHit = CollisionDetector::IsHit(
m_rocket.GetLocationSimple(), a->get()->GetLocation(),
a->get()->GetScale());
if (isAsteroidHit)
{
isAnyAsteroidHit = true;
break;
}
}
bool missPlanet =
m_rocket.GetZ() < -m_game.GetDistanceToPlanet() - 5;
if (isAnyAsteroidHit || missPlanet) { Crash(); }
}
Here, you check whether the rocket reaches the planet. To do it, you can use the CollisionDetector
class and pass the location of the rocket, as well as the location and the radius of the planet as parameters of the IsHit
method. It is worth mentioning that radius of the planet is equal to its scale, because in your model each vertex coordinate in the planet model takes values in range <-1;1>
, thus the radius is equal to 1. If the method returns TRUE
, it means that you have reached the planet. Then, you should call the ReachPlanet
method.
You also need to ensure that the rocket does not hit any asteroid. It can be achieved by iterating through a collection of asteroid data and calling the IsHit
method from the CollisionDetector
class. If any asteroid is hit, the value of the isAnyAsteroidHit
variable is set to TRUE
and you exit the loop. Then, you verify whether the rocket misses the planet. At the end, you check whether the value of the isAnyAsteroidHit
or missPlanet
variable is equal to TRUE
. In such a case, you call the Crash
method.
You also need to call the DetectCollisions
method inside the Update
method, as shown in the following code:
void GameRenderer::Update(float timeTotal, float timeDelta) { (...) DetectCollisions(); float distance = m_rocket.Fly( timeDelta * 1.5f * m_game.GetSpeedFactor()); (...) }
The ReachPlanet
method is executed when the rocket reaches the planet. It calls the ReachPlanet
method (on the Game
instance), which increases the number of the current level. Then, it loads the next stage, by calling the LoadLevel
method, as shown in the following code:
void GameRenderer::ReachPlanet()
{
m_game.ReachPlanet();
LoadLevel();
}
Whenever the user crashes the rocket, the Crash
method is executed. Here, the CrashWithAsteroid
method (on the Game
instance) is called and the same level is restarted, unless the player runs out of rockets, as presented as follows:
void GameRenderer::Crash()
{
m_game.CrashWithAsteroid();
if (m_game.GetRocketsNumber() > 0) { LoadLevel(); }
}
18.221.136.142