0

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?

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • 1
    You don't want [tag:upcasting] here, those are downcasts... – Ben Voigt Apr 23 '21 at 16:53
  • 3
    Read about "multiple virtual dispatch" – Ben Voigt Apr 23 '21 at 16:53
  • 3
    Don't use dynamic cast. Use virtual functions. – eerorika Apr 23 '21 at 16:58
  • 1
    Alternatively `std::unordered_map, std::function>` – Mooing Duck Apr 23 '21 at 16:59
  • @eerorika can you give an example? I understand what virtual functions are, but I don't know how to apply them in this situation. – kastrbl4nik Apr 23 '21 at 16:59
  • Not what you're asking about, butyour `if` conditions are not right, because `&&` has a higher precedence than `||`. – 1201ProgramAlarm Apr 23 '21 at 17:03
  • You can identify each class with an enum and write a functon that returns you the type and then decide. But there might be even more simple solutions. Which information has to be passed between the two entities taking part in a collision or is the type sufficient to decide on the collision behaviour? – 2b-t Apr 23 '21 at 18:46
  • 1
    I suspect that those `if’` statements don’t do what they’re intended to do. `a || b && c || d` is compiled as if it were written `a || (b &&c) || d`. I’m guessing it was intended to be `(a || b) && (c || d)`. – Pete Becker Apr 23 '21 at 18:56

2 Answers2

1

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

MatG
  • 574
  • 2
  • 7
  • 19
-1

Use a virtual function defined in Entity class to uniquely identify the derived class whether Player or Enemy. This will be a good practice to avoid any runtime errors as well.

 enum EntityType { Entity, Player, Enemy}

In the Entity class define a virtual function like this,

virtual EntityType getType (return Entity;)

and override the function in two classes accordingly.