Skip to main content Link Search Menu Expand Document (external link)

Theme: auto

Collisions and Frog Death

We’re going to use AABB collision as discussed in lecture to detect whether the Frog intersects with various objects. In your Lab03 folder, you will find CollisionComponent.h and CollisionComponent.cpp. This has a declaration of a new Component subclass called CollisionComponent, but you’ll need to implement the functions yourself.

First, implement GetMin() and GetMax(), which return Vector2s for the min and max points of the box, respectively. You compute each of these points through a combination of the owner’s position, owner’s scale, and the mSize member that has the width/height of the box NOT taking into account the owner scale. For example, the min.x position is:

min.x = ownerPosition.x  (mSize.x * ownerScale) / 2.0f

Remember that the min.y is the smallest numerical y-value no matter which way the y-axis goes. With SDL coordinates, the minimum y-value is towards the top of the screen, but the calculation for min.y is not affected by that.

Now implement the Intersect function, which returns true on an intersection. Use the basic AABB collision approach discussed in class where you test for the four cases the boxes cannot intersect. If none of these cases are true, then the boxes must intersect.

CollisionComponent Unit Tests

Your “add to pending destroy” function in Game must be named AddPendingDestroy or the unit tests will not compile. This is because the unit tests do not use your Game.h/cpp files. So, if you used a different name, you will need to rename it.

To validate that your CollisionComponent functions are implemented correctly, any time you push a change to Lab03/CollisionComponent.cpp, we run unit tests on GitHub Actions. After implementing correct GetMin, GetMax, and Intersect functions, you should pass 4 of the test cases, but you will still fail the last two 3 because they test the GetMinOverlap function that you’ll implement in the next part.

To view the test results on GitHub, go to your repo page and under the “Actions” tab you should be able to see the recent runs of the “Unit Tests (Lab03)” action:

Unit Tests (Lab03)

If you click on the workflow run (in the above case it’s “Lab 3 refactor”), it will show you which tests passed or failed:

Test report

At this point, you will expect to pass the tests other than the GetMinOverlap ones.

The spec for this part requires that you pass the unit tests for GetMin, GetMax, and Intersect on GitHub Actions.

If you’re not passing all the GetMin, GetMax, and Intersect test cases, that means there is something wrong which you need to fix. Rather than trying to debug by constantly pushing your files, we suggest you debug the unit tests locally to figure out what’s wrong, following the further information below.

Adding Collision Component to Actors

Now add a CollisionComponent to Frog and Vehicle. The Frog collision should have a size of {25, 25}. For the Vehicle, the dimensions of the collision component depend on if it’s a car or a truck. Use the SetSize function to set the dimensions of the collision component. A car should be {32, 32} while a truck should be {64, 24} (you can do this in the Vehicle::SetType function you hopefully made).

Adding a GetComponent Function

Sometimes, we want to be able to get a pointer to a specific type of component from an Actor. For example, in a moment the Frog will need to get a pointer to the Vehicle’s CollisionComponent. To make this easier to do, we can add the following templated GetComponent function in the public section of Actor:

// Returns component of type T, or nullptr if it doesn't exist
template <typename T>
T* GetComponent() const
{
    // Loop over all components
    for (auto c : mComponents)
    {
        // dynamic_cast will return nullptr if c is not type T*
        T* t = dynamic_cast<T*>(c);
        if (t)
        {
            return t;
        }
    }

    return nullptr;
}

Then, let’s suppose you have a Vehicle* called v. You can use GetComponent to get the CollisionComponent with the following:

CollisionComponent* vehicleCollision = v->GetComponent<CollisionComponent>();

While GetComponent is convenient to use, because of the loop and dynamic_cast, it does have some overhead cost. That’s why the style guide says you should use get component sparingly.

Testing Frog vs. Vehicle Collision

Add an override of HandleUpdate to Frog. You need to check whether the frog collides with any vehicles, and if so, kill the poor frog. This means you’ll need to track all the vehicles in Game, much like you did for the asteroids in Lab 2.

Then, your Frog::HandleUpdate can test if the frog intersects with any vehicles. If it does, kill the frog. For now, killing the frog should just move the frog back to the original start position at the bottom of the screen. (You probably want to save this “original start position” in Frog when you create the frog when loading the level).

You should still be able to move the frog as before, but now if you intersect with a vehicle, the frog will move back to the start position, like this:

Adding a DeadFrog

To make it easier to visualize where you died, make a new Actor subclass called DeadFrog. It’ll need a SpriteComponent using "Assets/Dead.png". You also need to make it so that the DeadFrog destroys itself after 0.5 seconds. You can implement this much like the lifetime for lasers in Lab 2.

Now change it so when the frog dies, before moving back to the start position, it creates a DeadFrog at the spot it died.

Now when the frog dies you should see a yellow skull and crossbones at the point of death (for 0.5 seconds), like this:

Responsible Drivers

It turns out the vehicle drivers feel bad about running over countless frogs. Add an override of Vehicle::HandleUpdate. In here, figure out if the frog is within an angle in front of the vehicle. If it is, the vehicle should move at half of its original speed, otherwise the vehicle should move at normal speed.

We’re going to consider the frog to be “in front of” the vehicle if the frog is within Pi/6 radians of the forward vector of the vehicle. That means the vehicle driver can see the frog and so they’ll be responsible and slow down!

To implement this, we want you to use Vector2::Dot as well as Math::Acos. If you’re not sure how to use this to calculate whether the frog is “in front,” you should review the slides. Also, as with many math problems, it might help to draw a diagram on paper if you get stuck.

You should verify that if the frog is “in front” of a vehicle that vehicle moves slower, and otherwise they move at normal speed. It should look like this:

Once you’e pushed this code, you’re ready to move on to part 3.

Further Info: CollisionComponent Unit Tests

The tests for CollisionComponent will automatically run on any push where you modify the Lab03/CollisionComponent.cpp file.

Debugging the Tests Locally

If you are unexpectedly failing unit tests and would like to be able to debug locally, you can do the following:

  1. Clone the unit tests repo: https://github.com/tac380-20253/tests-Lab03 into a separate folder (NOT into your regular labs repo)
  2. Copy the following files from your Lab03 folder to your tests-Lab03 folder:
    • Actor.h/cpp
    • CollisionComponent.h/cpp
    • Component.h/cpp
    • Transform.h/cpp
  3. Open the tests-Lab03 repo folder in CLion
  4. You can now run in the debugger like you normally do