当游戏里面的元素多起来后,对象之间产生的事件将会越发复杂,比如主角走路,会对周围所有的角色发送当前坐标信息;比如主角打怪,除了通知其他人打怪的状态信息,还要通知怪物,您被虐了;还有很多都可以用事件来做,操作系统的事件驱动的方式就明显优于轮询方式。事件系统相当于将所有这些事件统一起来,让开发者易于扩展及维护。
一、简单的事件系统
参考:
http://www.gamedev.net/reference/articles/article2141.asp
直接看代码IEventHandler.h
1. 首先定义事件结构体:
// The event structure
struct Event {
int Type;
int arg1, arg2;
};
一个类型,加上两个参数。(越简单越好,能阐述问题)
2. 为了type好辨认,通过enum管理所有的消息类型
// Defination of all the different type of events
enum EventType
{
// Mouse button events. Each of the next six events gets passed
// the absolute position of the mouse
E_MOUSELEFTBUTTONPRESS,
E_MOUSELEFTBUTTONRELEASE,
// Start new games
E_NEWGAMEEASY,
E_NEWGAMENORMAL,
E_NEWGAMEHARD,
// Game play related
E_INCREMENTSCORE,
E_PAUSEGAME,
E_CONTINUEGAME,
E_GAMEOVER,
// Close the App
E_APPCLOSE
};
还是很简单,鼠标键盘消息,游戏难度选项,游戏状态等
3. 定义一个接口,事件处理器(EventHandler)
class IEventHandler {
public:
virtual void EventHandler(const Event &e) = 0; // 纯虚函数,继承类必须实现
// Mutator and selector
IEventHandler * GetNextHandler(void) {return _nextHandler;}
void SetNextHandler(IEventHandler *next) {_nextHandler = next;}
// 构造函数使得对象一创建就自动注册到了时间分发器中,因为消息的分发主要靠事件分发器
// 遍历所有的事件处理器来发送事件。 而分发器是单例的,相对于事件处理器是一对多的关系,
// 后面会接着分析
IEventHandler() : _nextHandler(0) {
EventDispatcher::Get()->RegisterHandler(this);
}
protected:
void SendEvent(int eventType, int arg1 = 0, int arg2 = 0) {
EventDispatcher::Get()->SendEvent(eventType, arg1, arg2);
}
private:
IEventHandler *_nextHandler; // 将会有多个事件处理器,这里用链表来实现
};
4. 接着看EventDispatcher.h
class IEventHandler; // 类的前向声明
class EventDispatcher {
// definitions necessary to handle a singleton class
public:
//returns a pointer to the EventDispatcher singleton instance
static EventDispatcher* Get(); // 提供一个得到对象指针的机会 , 与外界沟通的桥梁
private:
// These are private so that only instance will ever be created
// the _deviceList pointer is initialized to null as the linked list
// is null-terminated
EventDispatcher(void) : _deviceList(0) {;} // 构造函数私有化,不能用new来创建对象
~EventDispatcher(void) {;} // 析构函数私有化,不能在栈上创建对象 , 应该添加一个释放资源的函数,要不没机会释放了
// pointer to singleton class
static EventDispatcher* _instanceOf;
// definitions of methods which handle events
public:
void RegisterHandler(IEventHandler *device); // 注册事件处理器,放入链表
// Sends the event to all the devices registered to listen
void SendEvent(int eventType, int arg1 = 0, int arg2 = 0); // 遍历所有事件处理器,发送消息
private:
IEventHandler *_deviceList; // 这里体现出来关系了,一对多。
};
5. 最后来看看实现吧,EventDispatcher.cpp
#include "EventDispatcher.h"
#include "IEventHandler.h"
// pointer to singleton instance
EventDispatcher* EventDispatcher::_instanceOf = 0; // 初始化单例 对象
// 通过这个方法来创建单例对象,有点太懒了^_^,多个函数都不愿意写
EventDispatcher* EventDispatcher::Get() {
if (_instanceOf)
return _instanceOf;
return _instanceOf = new EventDispatcher();
}
// 链表
void EventDispatcher::RegisterHandler(IEventHandler *device) {
device->SetNextHandler(_deviceList);
_deviceList = device;
}
void EventDispatcher::SendEvent(int eventType, int arg1, int arg2) {
Event e;
e.Type = eventType;
e.arg1 = arg1;
e.arg2 = arg2;
IEventHandler * curDevice = _deviceList;
// 遍历
for (; curDevice; curDevice = curDevice->GetNextHandler())
curDevice->EventHandler(e);
}
6. 使用 main.cpp
// 继承接口,实现虚函数
class A : public IEventHandler {
public:
// 真正处理消息的函数,可以只针对相应的类型
void EventHandler(const Event &e) {
switch (e.Type) {
case E_NEWGAMEEASY:
cout << "Class A handling E_NEWGAMEEASY event" << endl;
cout << "Class A sending a E_INCREMENTSCORE event" << endl;
SendEvent(E_INCREMENTSCORE);
break;
case E_PAUSEGAME:
cout << "Class A handling E_PAUSEGAME event" << endl;
break;
}
}
};
class B : public IEventHandler {
public:
void EventHandler(const Event &e) {
switch (e.Type) {
case E_INCREMENTSCORE:
cout << "Class B handling E_INCREMENTSCORE event" << endl;
break;
case E_PAUSEGAME:
cout << "Class B handling E_PAUSEGAME event" << endl;
break;
}
}
};
void main () {
A a1; // 定义就被放入了分发器中
B b1;
cout << "Main fct sending E_NEWGAMEEASY event" << endl;
EventDispatcher::Get()->SendEvent(E_NEWGAMEEASY); // 直接发送消息,群发
cout << "Main fct sending E_PAUSEGAME event" << endl;
EventDispatcher::Get()->SendEvent(E_PAUSEGAME);
char c;
cout << "Press any key followed by [Enter] to exit" << endl;
cin >> c;
exit(0);
}
是不是有很多想法,那就对了,接着写吧。