2013-07-30 2 views
2

Я планирую игровой движок, управляемый событиями. Основная идея состоит в том, что вместо того, чтобы все говорить со всем, все будет говорить с системой событий, которая будет передавать сообщения получателям, не связывая получателей с уведомителями или наоборот.C++: реализация системы событий для игрового движка

  • Объекты регистрируются в системе событий. Идентификатор уведомления и указатель на функцию обратного вызова передаются в качестве параметров каждой команды регистрации.
  • Объекты добавляют уведомления в систему событий. Идентификатор уведомления передается как параметр каждого уведомления. Уведомления добавляются в очередь, содержащую все ожидающие уведомления.
  • Кроме того, система событий поддерживает запланированные уведомления. Идентификатор уведомления и будущее время исполнения передаются в качестве параметров каждого уведомления. Затем запланированные уведомления сохраняются в структуре данных («расписание»), содержащей запланированные уведомления в порядке будущего времени исполнения.
  • Объект invoker обязывает систему событий обрабатывать все оповещения в очереди. Система событий извлекает уведомления в порядке и вызывает функцию обратного вызова для каждого объекта, который зарегистрировал себя с тем же идентификатором, что и текущее уведомление.
  • Объект invoker обязывает системе событий обрабатывать одно запланированное уведомление. Самое старое уведомление выдается из расписания, и вызываются обратные вызовы всех объектов, зарегистрированных с тем же идентификатором уведомления.

.

class Registration 
{ 
public: 
    void callback(void){ callback_(); } 
    void setCallback((*callback)(void)); 
    void addToEventSystem(int ID, EventSystem &eventSystem); 
private: 
    void (*callback_)(void); 
}; 

class EventSystem 
{ 
public: 
    void register(int ID, Registration* registration); 
    void unRegister(int ID, Registration* registration); 
    void addNotificationToQueue(int ID); 
    void addNotificationToSchedule(int ID, int notificationTime); 
    void processQueuedNotifications(void); 
    void processNextScheduled(void); 
    int getCurrentTime(void); 
private: 
    //placeholder types 
    <list> notificationQueue; 
    <binaryheap> notificationSchedule; 
}; 

//------------Use:------------------ 
class ReceiverObject 
{ 
public: 
    void doStuff(void); 
    void initialize(void){ 
     keyPressRegistration.setCallback(doStuff); 
     //multiple registrations with different ID:s to same eventsystem possible 
     keyPressRegistration.addToEventSystem(1234,eventSystem); 
     keyPressRegistration.addToEventSystem(42,eventSystem);}; 
private: 
    Registration keyPressRegistration; 
}; 

int main() 
{ 
    ReceiverObject receiverObject; 
    EventSystem eventSystem; 
    receiverObject.initialize(); 
    eventSystem.addNotificationToQueue(1234); 
    eventSystem.processQueuedNotifications(); 
} 

Однако я не совсем удовлетворен этим решением, в основном потому, что система не позволяет легко передачи параметров получателям, и я скептически отношусь к обратным вызовам функциев-членов, это хорошая практика дизайна? Как насчет названий методов/классов/переменных? Мы приветствуем конструктивную критику, руководство и альтернативные подходы к проблеме.

+2

Как насчет передачи 'std :: function' вместо простого указателя функции для ваших обратных вызовов? –

+0

Действительно, эти люди выглядят многообещающими. У меня просто нет опыта или знаний о C++ 11, но я дам новый стандарт взгляду – jms

ответ

4

Это не связано с вашим вопросом, но, говоря о дизайне, я бы избегал использования глобальной системы уведомлений «для всего», поскольку я видел плохие последствия в прошлом. Вы просто склонны использовать тяжелую систему событий в местах, где один объект просто вызовет какой-либо метод на другом объекте.

Специализированные системы с templatatised работают лучше, то есть системы, которые позволяют вам контролировать срок службы объектов и предназначены для обработки событий определенных типов и известных параметров в них.

В любом случае вам будет трудно решить такие проблемы, как ожидающие события, связанные с доставкой уже убитым получателям.

1

У меня лично был бы класс интерфейса EventHandler, который вы можете зарегистрировать, который будет иметь виртуальную функцию action (и, возможно, функцию для системы событий, чтобы сообщить классу, что он незарегистрирован). Это полностью исключает обратные вызовы, и фактическая реализация EventHandler будет иметь возможность хранения других данных (или ссылок на другие данные).

+0

Ну, это будет иметь больше смысла, чем моя текущая идея обратного вызова, но как насчет передачи параметров/данных? Конкретная реализация «EventHandler» по умолчанию должна знать класс приемника, который в порядке, но если данные должны быть переданы, он также должен знать источник данных (вероятно, класс, который добавил событие в «EventSystem» в первую очередь), которые свяжут их вместе. – jms

+0

Не уверен, что вы имеете в виду - в моих мыслях, класс приемника _is_ - экземпляр «EventHandler». –

+0

То, что, как я думал, вы подразумеваете в своем первоначальном ответе, состоит в том, что 'Registration' был сделан абстрактным базовым классом с именем« EventHandler », а система обратного вызова в« Registration »/« EventHandler »была заменена виртуальной функцией, используемой производными классами которые реализуют требуемый ответ, например, вызов 'ReceiverObject-> doStuff()'. Мое первоначальное намерение состояло в том, что объекту-получателю принадлежит идентификатор «Registration», который обрабатывает взаимодействие между приемником и системой событий, поэтому из вашего ответа я подумал, что объект-получатель также будет иметь экземпляр «EventHandler». – jms

1

Для выдачи параметров - Вы можете определить базовый класс ParamList которого дериваты являются шаблонными:

class ParamList 
{ 
    const ID_Type& GetParamsType(); 
    ... 
}; 
template <typename T> 
class Params: public ParamList 
{ 
    ... 
} 

Теперь обратными вызовы будут типа: недействительных (* Callback) (ParamList &); для безопасного использования вы можете добавить в класс Регистрация const ID_Type & GetExpectedParamsType(); и использовать его перед вызовом обратного вызова

SAMES идет для уведомлений: недействительного addNotificationToQueue (интермедиат ID, ParamList & пар); void addNotificationToSchedule (int ID, ParamList & param, int notificationTime);

Смежные вопросы