Created
July 28, 2022 06:01
-
-
Save asmwarrior/473a4ad40bfba7a1e8eb777ed0bb7846 to your computer and use it in GitHub Desktop.
code I copy and modified from this link: c++ - ECS Event/Messaging implementation - Code Review Stack Exchange — https://codereview.stackexchange.com/questions/79211/ecs-event-messaging-implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <cstdlib> | |
#include <functional> | |
#include <vector> | |
struct BaseEvent | |
{ | |
virtual ~BaseEvent() {} | |
protected: | |
static size_t getNextType(); | |
}; | |
size_t BaseEvent::getNextType() | |
{ | |
static size_t type_count = 0; | |
return type_count++; | |
} | |
template <typename EventType> | |
struct Event : BaseEvent | |
{ | |
static size_t type() | |
{ | |
static size_t t_type = BaseEvent::getNextType(); | |
return t_type; | |
} | |
Event(const EventType& event) : event_(event) {} | |
const EventType& event_; | |
}; | |
struct EventManager | |
{ | |
template <class EventType> | |
using call_type = std::function<void(const EventType&)>; | |
template <typename EventType> | |
void subscribe(call_type<EventType> callable) | |
{ | |
// When events such as COLLISION don't derive | |
// from Event, you have to get the type by | |
// using one more level of indirection. | |
size_t type = Event<EventType>::type(); | |
if (type >= m_subscribers.size()) | |
m_subscribers.resize(type+1); | |
m_subscribers[type].push_back(CallbackWrapper<EventType>(callable)); | |
} | |
template <typename EventType> | |
void emit(const EventType& event) | |
{ | |
// Same change to get the type. | |
size_t type = Event<EventType>::type(); | |
if (type >= m_subscribers.size()) | |
m_subscribers.resize(type+1); | |
// This a crucial change to the code. | |
// You construct a temporary Event object by | |
// using the EventType object and use Event. | |
// This requires a change to Event, which follows below. | |
Event<EventType> eventWrapper(event); | |
for (auto& receiver : m_subscribers[type]) | |
receiver(eventWrapper); | |
} | |
template <typename EventType> | |
struct CallbackWrapper | |
{ | |
CallbackWrapper(call_type<EventType> callable) : m_callable(callable) {} | |
void operator() (const BaseEvent& event) { | |
// The event handling code requires a small change too. | |
// A reference to the EventType object is stored | |
// in Event. You get the EventType reference from the | |
// Event and make the final call. | |
m_callable(static_cast<const Event<EventType>&>(event).event_); } | |
call_type<EventType> m_callable; | |
}; | |
std::vector<std::vector<call_type<BaseEvent>>> m_subscribers; | |
}; | |
struct PLAYER_HIT | |
{ int damage; }; | |
struct PLAYER_LVL_UP | |
{ int new_level; }; | |
struct COLLISION | |
{ int entity1; int entity2; }; | |
struct PLAYER_GUI | |
{ | |
PLAYER_GUI(EventManager& em) | |
{ | |
using namespace std::placeholders; | |
em.subscribe<PLAYER_HIT>( | |
std::bind(&PLAYER_GUI::handle_hit, this, _1)); | |
em.subscribe<PLAYER_LVL_UP>( | |
std::bind(&PLAYER_GUI::handle_lvl_up, this, _1)); | |
em.subscribe<COLLISION>( | |
std::bind(&PLAYER_GUI::handle_collision, this, _1)); | |
} | |
void handle_hit(const PLAYER_HIT& event) | |
{ | |
std::cout << "handle_hit: damage= " << event.damage << std::endl; | |
} | |
void handle_lvl_up(const PLAYER_LVL_UP& event) | |
{ | |
std::cout << "handle_lvl_up: new_level= " << event.new_level << std::endl; | |
} | |
void handle_collision(const COLLISION& event) | |
{ | |
std::cout << "handle_collision: " << event.entity1 << "," << event.entity2 << std::endl; | |
} | |
}; | |
int main() | |
{ | |
EventManager em; | |
PLAYER_GUI gui(em); | |
em.emit(PLAYER_LVL_UP{1}); | |
em.emit(PLAYER_HIT{2}); | |
em.emit(COLLISION{3,4}); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
If we assume the handlers take just the event they are listen to as a parameter we can simplify the necessary glue code for the event subscription by adding:
and extend the handler(s) (here: PLAYERGUI) from BaseHandler, the usage would be:
subscribe(em, &PLAYER_GUI::handle_collision);