Adding collisions to my little engine

Wow, it’s been a whole month since I last posted. Somehow it feels like so much longer. I’ve been so busy because my game was released on PS4 and Steam, and we’ve been so busy promoting it (which included a trip to Germany).

But, finally I’ve been able to do a bit of work for myself again. I’ve decided to add collisions to my project. They were already there in my previous iteration, but the existing implementation does not work at all with the component based system I’ve been working on. So, time for a refactor.

Before I started I moved the position, rotation and scale variables into the base GameObject class. There were way too many components that needed this information, and having to retrieve this from one component and pass it to the others was just getting ridiculous. The Unity game engine actually has a Transform component that stores this information and every GameObject includes a Transform component by default. I thought that was overkill for my little project, but I might change that again later on because it might be good to keep all the transform information together in memory.

Continuing with the theme of Unity being my main inspiration, I decided to create a ColliderComponent. Right now this is really an AABB collider, but eventually I think this will just be the base class for a series of colliders. I incorporated my existing AABB implementation into this component.

bool ColliderComponent::IsCollidingWith(ColliderComponent *a_pOtherCollider)
{
    m_pOtherCollider = nullptr;
    if (!m_pGameObject->IsActive()) {
        return false;
    }
    if (m_collisionTag == a_pOtherCollider->m_collisionTag) {
        return false;
    }
    if (max.x < a_pOtherCollider->min.x || min.x > a_pOtherCollider->max.x)
        return false;
    if (max.y < a_pOtherCollider->min.y || min.y > a_pOtherCollider->max.y)
        return false;
    //store the other collider
    m_pOtherCollider = a_pOtherCollider;
    return true;
}

There are a few things going on in this function. The lower half should look familiar – it’s basically just the existing AABBvAABB code from my previous implementation, just checking that the corners of the bounding box intersect.
Above that I’m checking to see if m_pGameObject is active. This is a variable I added to the base Component class – it’s a reference to the GameObject that the component is attached to. This made it easier to share information between Components (especially the transform information I mentioned earlier).

The CollisionComponent also has a collisionTag. I only have 3 tags right now: BULLET, PLAYER and ENEMY. No Bullets should collide with one another, and enemies should also pass through one another (for now), so I’m simply checking that nothing with the same tag should collide. Ideally there would be some sort of lookup table here to see what is allowed to collide with what, but that’s definitely a task for another time.

If there has been a collision I store the other collider, currently as a public variable so that I can check it in the gameplay scripts.

To call this function I created a PhysicsManager. The PhysicsManager stores references to all the CollisionComponents and has the following Update function:

void PhysicsManager::Update()
{
    //check all colliders against all other colliders. 
    //Is there a faster way to do this?? 
    //Obviously methods exist, should do some research
    for (unsigned int i = 0; i < m_colliders.size(); i++) {
        for (unsigned int j = 0; j < m_colliders.size(); j++) {
            //process collisions
            m_colliders[i]->IsCollidingWith(m_colliders[j]);
        }
    }
}

So basically it just runs through all the colliders and sets the “other” collider if there has been a collision. This collider is then checked in the GameObject gameplay code like so:

//check collisions here?
ColliderComponent* colliderComponent = 
    dynamic_cast<ColliderComponent*>(GameObject::GetComponent(COLLIDER));
if (colliderComponent->m_pOtherCollider != nullptr) {
    //ok, this means there has been a collision
    //check if collision is ship object or bullet
    if (dynamic_cast<ShipObject*>(
        colliderComponent->m_pOtherCollider->m_pGameObject)) {
        //inflict damage
        healthComponent->TakeDamage(10);
    }
    else if (dynamic_cast<BulletObject*>(
        colliderComponent->m_pOtherCollider->m_pGameObject)) {

        //inflict damage
        healthComponent->TakeDamage(10); 
    }
}

So here you can see I’m checking if the other collider is set and then checking to see what the game object is that we’ve collided with. In hindsight I should probably be checking the physics tag at this point, rather than doing a bit of a hacky dynamic cast. Also the damage is hardcoded in right now, but really I should be retrieving the weapon power of the bullet for the last line and applying that in place of “10”.

The last thing to note is that I am now calculating the bounding boxes of the objects every frame in the update function of the ColliderComponent instead of calculating this twice a frame and having to process the result in the main Update loop in the GameScreen.

Here is the ColliderComponent Update function:

void ColliderComponent::Update(const double a_dDeltaTime)
{
    min.x = max.x = m_pGameObject->m_position.x;
    min.y = max.y = m_pGameObject->m_position.y;

    glm::mat4 globalTransform = 
        glm::translate(glm::mat4(1), glm::vec3(m_pGameObject->m_position.x, 
                       m_pGameObject->m_position.y, 
                       m_pGameObject->m_position.z)) *
                       glm::rotate(glm::mat4(1), 
                       m_pGameObject->m_fRotationAngle, 
                       glm::vec3(0, 0, 1)) * 
                       glm::scale(glm::mat4(1), 
                       glm::vec3(m_pGameObject->m_fScale, 
                       m_pGameObject->m_fScale, 1));

    for (unsigned int i = 0; i < 4; ++i)
    {
        glm::vec4 temp = globalTransform * m_corners[i];
        if (temp.x < min.x)
            min.x = temp.x;
        if (temp.y < min.y)
            min.y = temp.y;
        if (temp.x > max.x)
            max.x = temp.x;
        if (temp.y > max.y)
            max.y = temp.y;
    }
}

So, here’s the recalculation of the bounding box each frame. Having to recalculate the globalTransform once per object per frame seems like a silly idea. Especially since the SpriteComponent is also making this calculation. It seems like the globalTransform should also be a part of the GameObject (or Transform) so it’s only calculated once per frame. And possibly it might only get recalculated if the position, rotation or scale have changed.

So that brings me to the end of my Collision refactor. I was happy to get this all up and running in only a few hours, but I definitely feel that I made some poor decisions, especially in terms of performance. I think my next job will be to fix up some of the issues I’ve already mentioned, as well as do a little research into a better way to process the collisions.

If you have any suggestions then please let me know in the comments, and as always, you can find my code on github.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s