Search code examples
c++collision

How to write an elegant collision handling mechanism?


I'm in a bit of a pickle: say I'm making a simple, 2D, Zelda-like game. When two Objects collide, each should have a resulting action. However, when the main character collides with something, his reaction depends solely on the type of the object with which he collided. If it's a monster, he should bounce back, if it's a wall, nothing should happen, if it's a magical blue box with ribbons, he should heal, etc. (these are just examples).

I should also note that BOTH things are part of the collision, that is, collision events should happen for both the character AND the monster, not just one or the other.

How would you write code like this? I can think of a number of incredibly inelegant ways, for instance, having virtual functions in the global WorldObject class, to identify attributes - for instance, a GetObjectType() function (returns ints, char*s, anything that identifies the object as Monster, Box, or Wall), then in classes with more attributes, say Monster, there could be more virtual functions, say GetSpecies().

However, this becomes annoying to maintain, and leads to a large cascading switch (or If) statement in the collision handler

MainCharacter::Handler(Object& obj)
{
   switch(obj.GetType())
   {
      case MONSTER:
         switch((*(Monster*)&obj)->GetSpecies())
         {
            case EVILSCARYDOG:
            ...
            ...
         }
      ...
   }

}

There's also the option of using files, and the files would have things like:

Object=Monster
Species=EvilScaryDog
Subspecies=Boss

And then the code can retrieve the attributes without the need for virtual functions cluttering everything up. This doesn't solve the cascading If problem, however.

And THEN there's the option of having a function for each case, say CollideWall(), CollideMonster(), CollideHealingThingy(). This is personally my least favourite (although they're all far from likeable), because it seems the most cumbersome to maintain.

Could somebody please give some insight into more elegant solutions to this problem? Thanks for any and all help!


Solution

  • I would do it vice versa - because if the character collides with an object, an object collides with the character as well. Thus you can have a base class Object, like this:

    class Object  {
      virtual void collideWithCharacter(MainCharacter&) = 0;
    };
    
    class Monster : public Object  {
      virtual void collideWithCharacter(MainCharacter&) { /* Monster collision handler */ }
    };
    
    // etc. for each object
    

    Generally in OOP design virtual functions are the only "correct" solution for cases like this:

    switch (obj.getType())  {
      case A: /* ... */ break;
      case B: /* ... */ break;
    }
    

    EDIT:
    After your clarification, you will need to adjust the above a bit. The MainCharacter should have overloaded methods for each of the objects it can collide with:

    class MainCharacter  {
      void collideWith(Monster&) { /* ... */ }
      void collideWith(EvilScaryDog&)  { /* ... */ }
      void collideWith(Boss&)  { /* ... */ }
      /* etc. for each object */
    };
    
    class Object  {
      virtual void collideWithCharacter(MainCharacter&) = 0;
    };
    
    class Monster : public Object  {
      virtual void collideWithCharacter(MainCharacter& c)
      {
        c.collideWith(*this);  // Tell the main character it collided with us
        /* ... */
      }
    };
    
    /* So on for each object */
    

    This way you notify the main character about the collision and it can take appropriate actions. Also if you need an object that should not notify the main character about the collision, you can just remove the notification call in that particular class.

    This approach is called a double dispatch.

    I would also consider making the MainCharacter itself an Object, move the overloads to Object and use collideWith instead of collideWithCharacter.