Circular collision detection

One way to detect collision is to see how far each of the objects are from each other's center. This is known as circular collision detection because it treats each object as if it is bound by a circle, and uses the radius of that circle to determine whether the objects are close enough to collide.

Take a look at the following diagram:

Circular collision detection

The circles on the left are not colliding, while the circles on the right are colliding. For the non-colliding circles, the distance (d) between the center points of the two circles is greater than the sum of the two radii (r1 + r2). For the colliding circles, the distance (d) between the two centers is less than the sum of the two radii (r1 + r2). We can use this knowledge to test any two objects for collision based on the radii of the circles and the distance between the objects center point.

So, how do we use this information?

  1. We will know r1 and r2 because we set them when we create the sprite.
  2. We will calculate two legs of a right-triangle using the x and y coordinates for the center of each circle.
  3. We will calculate d, the distance between two center points, using a variant of the Pythagorean Theorem.

It will probably hurt your brain a little, but I'd like to refresh your memory one tenet of basic geometry.

The Pythagorean Theorem

The Pythagorean Theorem allows us to find the distance between any two points in a two-dimensional space if we know the lengths of the line segments that form a right-angle between the points.

The Pythagorean Theorem

a2 + b2 = c2

In our case, we are trying to calculate the distance (c) between the two points.

A little algebra will transform this equation to:

The Pythagorean Theorem

It is computationally expensive to perform the square root. A nice mathematical trick will actually allow us to perform our collision detection without calculating the square root.

If we were to use square roots to do this calculation, here is what that might look like:

c = sqrt(a * a + b * b);
if (c <= r1 + r2) return true;

Although this would work, there is a nice little mathematical trick that allows us to accomplish this test without taking the square root. Take a look at this:

c = a * a + b * b;
if (c<= r1 * r1 + r2 * r2) return true;

It turns out that we can just keep everything in the equation at the power of 2 and the comparison still works. This is because we are only interested in the relative comparison between the distance and the sum of the radii, not the absolute mathematical values.

Tip

If the math we presented here boggles your brain, then don't worry too much. Circular collision detection is so common that the math to detect it is generally already built into the game engine that you will use. However, I wanted you to take a little look under the hood. After all, game programming is inherently mathematical, and the more you understand the math, the better you will be at coding.

Adding the circular collision code

Now, it's time to modify Sprite.h to add support for the circular collision detection. First, we need to add some member variables to hold the center point and radius. Add these two properties to Sprite.h:

float m_radius;
Point m_center;

Then add the following methods declarations:

void SetRadius(const GLfloat p_radius) { m_radius = p_radius; }
const float GetRadius() const { return m_radius; }
void SetCenter(const Point p_center) { m_center = p_center; }
const Point GetCenter() const;
const bool IntersectsCircle(const Sprite* p_sprite) const;

These methods allow us to set and retrieve the center point and radius of the sprite. The GetCenter method is more than one line, so we will implement it in Sprite.cpp:

const Sprite::Point Sprite::GetCenter() const
{
  Point center;
  center.x = this->GetPosition().x + m_center.x;
  center.y = this->GetPosition().y + m_center.y;

  return center;
}

An important point to note here is that m_center represents an x and y offset from the sprite's anchor point. So, to return the center point we will add m_center to the current position of the sprite and this will give us the current center point of the sprite exactly where it is in the game.

We now need to add the code to perform the collision detection. Add the following code to Sprite.cpp:

const bool Sprite::IntersectsCircle(const Sprite* p_sprite) const
{
 if (this->IsCollideable() && p_sprite->IsCollideable() && this->IsActive() && p_sprite->IsActive())
 {
  const Point p1 = this->GetCenter();
  const Point p2 = p_sprite->GetCenter();
  float y = p2.y - p1.y;
  float x = p2.x - p1.x;
  float d = x*x + y*y;
  float r1 = this->GetRadius() * this->GetRadius();
  float r2 = p_sprite->GetRadius() * p_sprite->GetRadius();
  if (d <= r1 + r2)
  {
   return true;
  }
 }
 return false;
}

As we have already explained the use of the Pythagorean Theorem, this code should actually seem a little familiar to you. Here is what we are doing:

The function accepts one sprite to compare with itself.

  • First, we check to make sure both sprites are collideable.
  • p1 and p2 represent the two centers.
  • x and y represent the lengths of the a and b sides of a right-angled triangle. Notice that the calculation is simply the difference between the x and y position of each sprite, respectively.
  • r1 and r2 are the radii of the two circles (left as a power of 2).
  • d is the distance between the two centers (left as a power of 2).
  • If d is less than or equal to the sum of the two radii, then the circles are intersecting.

Why use circular collision detection?

As we discussed many times, textures are represented as rectangles. In fact, we will take advantage of this when we cover rectangular collision detection later in the chapter. The following figure illustrates how rectangular and circular collision detection differ (the relative sizes are exaggerated to make a point):

Why use circular collision detection?

The sprites on the left are colliding using a rectangular bounding box. The sprites on the right are colliding using a bounding circle. In general, using a bounding circle is visually more convincing when we are dealing with rounder shapes.

Tip

I'll admit the difference in this example is not that big. You could get away with rectangular or circular collision detection in this example. The round nature of the oil can made it a good candidate for circular collision detection. Circular collision detection is really essential if the two objects that are colliding are actually circles (that is, two balls colliding).

With the code that we developed, we need to define the center and radius for any sprites that will use circular collision detection. Add the following code to the LoadTextures function in RoboRacer.cpp:

Sprite::Point center;
float radius;

center.x = robot_right->GetSize().width / 2.0f;
center.y = robot_right->GetSize().height / 2.0f;
radius = (center.x + center.y) / 2.0f;

robot_right->SetCenter(center);
robot_right->SetRadius(radius);
robot_left->SetCenter(center);
robot_left->SetRadius(radius);

center.x = pickup->GetSize().width / 2.0f;
float yOffset = (pickup->GetSize().height / 4.0f) * 3.0f;
center.y = yOffset;
pickup->SetCenter(center);
radius = pickup->GetSize().width / 2.0f;
pickup->SetRadius(radius);

Don't get too worried about the exact values that we are using here. We are basically setting up a bounding circle for Robo and the oil can that match the preceding figure. Robo's bounding circle is set to the middle of the robot, while the oil can's circle is set to the bottom half of the texture.

Wiring in the collision detection

We are now going to add a new function that will perform all of our collision detection. Add the following function to RoboRacer2D.cpp:

void CheckCollisions()
{
 if (player->IntersectsCircle(pickup))
 {
  pickup->IsVisible(false);
  pickup->IsActive(false);
  player->SetValue(player->GetValue() + pickup->GetValue());
  pickupSpawnTimer = 0.0f;
 }
}

The purpose of this code is to check to see whether the player has collided with the pickup:

  • If the call to player->IntersectsCircle(pickup) returns true, then the player has collided with the pickup
  • The pickup is deactivated and made invisible
  • The pickup's value is added to the player's value (this will be the base for scoring in a future chapter)
  • The spawn timer is reset

We have two small details left. First, you must add a call to CheckCollisions to the Update function:

if (m_gameState == GS_Running)
{
  background->Update(p_deltaTime);
  robot_left->Update(p_deltaTime);
  robot_right->Update(p_deltaTime);
  robot_left_strip->Update(p_deltaTime);
  robot_right_strip->Update(p_deltaTime);
  
  pause->Update(p_deltaTime);
  resume->Update(p_deltaTime);
  
  pickup->Update(p_deltaTime);
  SpawnPickup(p_deltaTime);
  
  CheckCollisions();
}

Secondly, you need to make the player and pickup collideable. Add these three lines to the bottom of LoadTextures just before the return statement:

robot_left->IsCollideable(true);
robot_right->IsCollideable(true);
pickup->IsCollideable(true);

Now, the real fun starts! Play the game and when the oil can spawns, and use Robo to pick it up. Five seconds later another oil can spawns. The fun never ends!

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

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