2

我正在计划一个事件驱动的游戏引擎。基本思想是,不是让一切都与一切对话,而是一切都与事件系统对话,事件系统会将消息转发给他们的接收者,而不会将接收者耦合到通知者或其他方式。

  • 对象将自己注册到事件系统。通知 ID 和指向回调函数的指针作为每个注册命令的参数传递。
  • 对象将通知添加到事件系统。通知的 ID 作为每个通知的参数传递。通知被添加到包含所有待处理通知的队列中。
  • 此外,事件系统支持预定通知。通知 ID 和未来执行时间作为每个通知的参数传递。然后将调度的通知存储在按未来执行时间的顺序保存调度通知的数据结构(“调度”)中。
  • 调用者对象命令事件系统处理所有排队的通知。事件系统按顺序获取通知,并调用每个已注册自己的对象的回调函数,其 ID 与当前通知相同。
  • 调用者对象命令事件系统处理一个预定通知。从调度中获取最旧的通知,并调用使用相同通知 ID 注册的所有对象的回调。

.

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();
}

但是我对这个解决方案并不完全满意,主要是因为系统不允许将参数轻松传递给接收者,而且我对成员函数的回调持怀疑态度,这是一个好的设计实践吗?我想出的方法/类/变量名呢?欢迎对问题提出建设性的批评、指导和替代方法。

4

3 回答 3

5

它与您的问题并不严格相关,但谈到设计,我会避免使用“针对所有内容”的全局通知系统,因为我过去曾看到过不良后果。在一个对象只会调用另一个对象的某些方法的地方,您只会倾向于使用繁重的事件系统。

专门的模板化系统工作得更好,即允许您控制对象的生命周期并旨在处理特定类型的事件和其中的已知参数的系统。

无论如何,您会发现很难解决诸如等待交付给已经被杀死的收件人的未决事件之类的问题。

于 2013-07-30T13:17:11.400 回答
1

我个人会有一个接口类EventHandler,你可以注册它,它会有一个虚action函数(也许还有一个函数让事件系统告诉类它正在被取消注册)。这完全避免了回调,并且实际的实现EventHandler将有可能持有其他数据(或对其他数据的引用)。

于 2013-07-30T12:29:11.660 回答
1

对于参数问题 - 您可以定义其派生模板化的基类 ParamList:

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

现在回调的类型为:void (*Callback)(ParamList&); 为了安全起见,您可以添加到类 Registration const ID_Type& GetExpectedParamsType(); 并在回调之前使用它

同样适用于通知: void addNotificationToQueue(int ID, ParamList& param); void addNotificationToSchedule(int ID, ParamList& param,int notificationTime);

于 2013-07-30T14:14:45.150 回答