c++dynamic-castmultiple-dispatch

How to use dynamic_cast efficiently?


There is an abstract class Entity, and other classes like Player and Enemy are inherit from it. When game detects a collision between the entities, the following method is called:

    void handleCollision(Entity* ent1, Entity* ent2) {
        if (dynamic_cast<Player*>(ent1) || dynamic_cast<Player*>(ent2) &&
            dynamic_cast<Enemy*>(ent1) || dynamic_cast<Enemy*>(ent2)) {
            //player <-> enemy collision
        }
        else if (dynamic_cast<Player*>(ent1) || dynamic_cast<Player*>(ent2) &&
                 dynamic_cast<Projectile*>(ent1) || dynamic_cast<Projectile*>(ent2)) {
            //player <-> projectile collision
        }
        else if () {
            //...
        }
        else if() {
            //...
        }
    }

Each entity has unique behavior when colliding with another, which depends on the type of entity (Player, Enemy, etc), that's why I need to check every possible combination between entities as shown above. But I don't like the fact it creates a huge else if chain, where each entity is checked multiple times. Is there another way of doing it?


Solution

  • Trying to expand Ben Voigt's comment about multiple virtual dispatch, something along the lines of:

    void handleCollision(Entity* ent1, Entity* ent2)
    {
      ent1->collide_with(ent2);
    }
    

    Where:

    class Entity
    {
     public:
      virtual void collide_with(Entity*) = 0; // Dispatcher
    
      virtual void handle_collision_with(Entity*) {}
      virtual void handle_collision_with(class Player*) {}
      virtual void handle_collision_with(class Enemy*) {}
      virtual void handle_collision_with(class Projectile*) {}
    };
    
    
    class Player : public Entity
    {
    public:
      virtual void collide_with(Entity* other) override
       {
        other->handle_collision_with(this);
       }
    
      virtual void handle_collision_with(Entity* other) override
       {
        // Unhandled entity
       }
    
      virtual void handle_collision_with(Player* other) override
       {
        // Handle collision player-player
       }
    
      virtual void handle_collision_with(Projectile* projectile) override
       {
        // Handle collision player-projectile
       }
    };
    
    class Enemy : public Entity
    {
    public:
      virtual void collide_with(Entity* other) override
       {
        other->handle_collision_with(this);
       }
    
      virtual void handle_collision_with(Enemy* other) override
       {
        // Handle collision enemy-enemy
       }
    
      virtual void handle_collision_with(Player* player) override
       {
        // Handle collision enemy-player
       }
    
      virtual void handle_collision_with(Projectile* projectile) override
       {
        // Handle collision enemy-projectile
       }
    };
    
    class Projectile : public Entity
    {...}
    

    source: a-polyglots-guide-to-multiple-dispatch