Handling Multiple Event Types

Can I have an observer observe multiple different types of events from the same subject?

Yes, a single observer can handle multiple event types from the same subject. There are several ways to implement this, each with its own advantages. Let's explore the main approaches.

Multiple Delegate Properties

The simplest approach is to have separate delegates for each event type:

class Player {
 public:
  using HealthDelegate = std::function<
    void(int NewHealth)>;
  using LevelDelegate = std::function<
    void(int NewLevel)>;

  void SetOnDamageDelegate(HealthDelegate D) {
    OnDamage = D;
  }
  void SetOnLevelUpDelegate(LevelDelegate D) {
    OnLevelUp = D;
  }

  void TakeDamage(int Damage) {
    Health -= Damage;
    if (OnDamage) OnDamage(Health);
  }

  void GainExperience(int Exp) {
    Experience += Exp;
    if (Experience >= 100) {
      Level++;
      if (OnLevelUp) OnLevelUp(Level);
      Experience -= 100;
    }
  }

 private:
  HealthDelegate OnDamage;
  LevelDelegate OnLevelUp;
  int Health{100};
  int Level{1};
  int Experience{0};
};

Event Objects

Another approach is to use a single delegate type but pass event objects that contain information about the event type:

enum class PlayerEventType { Damage, LevelUp };

struct PlayerEvent {
  PlayerEventType Type;
  int Value;
};

class Player {
public:
  using EventDelegate = std::function<
    void(const PlayerEvent&)>;

  void SetEventDelegate(EventDelegate D) {
    OnEvent = D;
  }

  void TakeDamage(int Damage) {
    Health -= Damage;
    if (OnEvent) {
      OnEvent({PlayerEventType::Damage, Health});
    }
  }

  void GainExperience(int Exp) {
    Experience += Exp;
    if (Experience >= 100) {
      Level++;
      if (OnEvent) {
        OnEvent({
          PlayerEventType::LevelUp, Level
        });
      }
      Experience -= 100;
    }
  }

private:
  EventDelegate OnEvent;
  int Health{100};
  int Level{1};
  int Experience{0};
};

The observer can then handle different event types:

void HandlePlayerEvent(const PlayerEvent& E) {
  switch (E.Type) {
    case PlayerEventType::Damage:
      std::cout << "Health changed to: "
        << E.Value << '\n';
      break;
    case PlayerEventType::LevelUp:
      std::cout << "Level up to: "
        << E.Value << '\n';
      break;
  }
}

Choose the approach that best fits your needs:

  • Multiple delegates are simpler but less flexible
  • Event objects are more complex but provide a unified interface
  • Event objects make it easier to add new event types later

Delegates and the Observer Pattern

An overview of the options we have for building flexible notification systems between game components

Questions & Answers

Answers are generated by AI models and may not have been reviewed. Be mindful when running any code on your device.

Why Use Delegates?
Why do we need to use delegates instead of just calling functions directly?
Delegate Performance Impact
How does the performance of delegates compare to direct function calls?
Pausing Observer Notifications
Is it possible to temporarily pause notifications to specific observers?
std::function vs Function Pointer Performance
What's the overhead of using std::function compared to raw function pointers?
Smart Pointers with Observers
Can I use smart pointers to automatically manage observer registration/unregistration?
Observer Pattern Without Dynamic Allocation
Is it possible to implement the observer pattern without dynamic memory allocation?
Or Ask your Own Question
Get an immediate answer to your specific question using our AI assistant