Cocos2d-x 事件处理EventDispatcher
声明:本文分析的是cocos2d-x-3.12的代码
Cocos2d-x 3.x中一共有六种事件,如类图所示:
单点触摸事件 (EventListenerTouchOneByOne)
多点触摸事件 (EventListenerTouchAllAtOnce)
键盘响应事件 (EventListenerKeyboard)
鼠标响应事件 (EventListenerMouse)
自定义事件 (EventListenerCustom)
加速记录事件 (EventListenerAcceleration)
Cocos2d中所有的事件统一由EventDispatcher类管理,和Action动作、Scheduler调度器的管理一样,分发器EventDispatcher类只在Director类中有一共实例EventDispatcher* Director:: _eventDispatcher。
Cocos2d中事件采用订阅的方式,当需要处某种类型的事件时,可以向分发器EventDispatcher类订阅,当对应的事件Event发生时,EventDispatcher类会调用回调函数,通知订阅者。
当要处理某事件时,首先要创建一共监听器,并设置好回调函数,然后通过EventDispatcher类中的addEventListenerXXX()函数订阅事件,有事件发生时会调用回调函数。
例如,触摸事件:
//创建监听器
auto touchLis = EventListenerTouchOneByOne::create();
//设置回调函数
touchLis->onTouchBegan= [](Touch *t, Event *e) ->bool { returntrue; };
touchLis->onTouchMoved= [](Touch *t, Event *e) { };
touchLis->onTouchEnded= [](Touch *t, Event *e) {};
//订阅事件
_eventDispatcher->addEventListenerWithFixedPriority(touchLis, 1);
事件处理都是采用C++11的回调函数,可以通过std::bind绑定事件处理的额外参数,如this指针等等。Cocos提供了以下几个宏(调用了std::bind)绑定参数
CC_CALLBACK_0、CC_CALLBACK_1、CC_CALLBACK_2、CC_CALLBACK_3
EventListenerTouchOneByOne 单点触控事件,每次触控都只有一个触点的点击事件。
触控事件的回调函数
回调函数类型
typedef std::function<bool(Touch*, Event*)> ccTouchBeganCallback;
typedef std::function<void(Touch*, Event*)> ccTouchCallback;
参数Touch,存放了触点信息,可以通过Touch::getLocation函数获取触点的世界坐标。可以通过Node::convertToNodeSpace函数转换成节点的本地坐标
参数Event,这个参数是一个EventTouch类,可以强制转换成EventTouch类。EventTouch类存放了关于触摸事件的信息,包括Touch参数。
ccTouchBeganCallback onTouchBegan; //点击按下事件
事件有一个bool返回值
如果返回ture 后面才会接收到onTouchMoved、onTouchEnded、onTouchCancelled事件。返回true的情况下并且监听器设置了_needSwallow参数为ture,则回停止传递事件,后续的EventListenerTouchOneByOne则接收不到相应的事件。
如果返回false 后续的onTouchMoved、onTouchEnded、onTouchCancelled都会接收不到,且事件也回继续往后传递。
ccTouchCallback onTouchMoved; //点击后移动事件
ccTouchCallback onTouchEnded; //点击抬起事件
ccTouchCallback onTouchCancelled; //点击取消事件,如手机来电话
void EventListenerTouchOneByOne::setSwallowTouches(bool needSwallow)
该函数可以设置是否停止传递触摸事件
EventListenerTouchAllAtOnce 多点触控事件,一个点触摸时也会收到该事件。
回调函数
typedef std::function<void(conststd::vector<Touch*>&, Event*)>
参数std::vector
参数Event,这个参数是一个EventTouch类,可以强制转换成EventTouch类。EventTouch类存放了关于触摸事件的信息,包括std::vector
ccTouchesCallback;
ccTouchesCallback onTouchesBegan; //点击按下事件
ccTouchesCallback onTouchesMoved; //点击后移动事件
ccTouchesCallback onTouchesEnded; //点击抬起事件
ccTouchesCallback onTouchesCancelled; //点击取消事件,如手机来电话
回调函数
std::function<void(EventKeyboard::KeyCode, Event*)> onKeyPressed; //按键按下
std::function<void(EventKeyboard::KeyCode, Event*)> onKeyReleased; //按键弹起
参数KeyCode,EventKeyboard::KeyCode枚举值,代表了那个按键
参数Event,实际是一共EventKeyboard类,可以强制转换。
回调函数
std::function<void(EventMouse* event)> onMouseDown; //鼠标按下
std::function<void(EventMouse* event)> onMouseUp; //鼠标弹起
std::function<void(EventMouse* event)> onMouseMove; //鼠标移动
std::function<void(EventMouse* event)> onMouseScroll; //鼠标滚轮滚动
参数EventMouse,包含了鼠标点击的信息
getMouseButton获取鼠标按键 -1代表没有按键信息,0代表左键,1代表右键,2代表滚轮键
getLocation获取鼠标位置
getCursorY获取滚轮竖向滚动量
getCursorX获取滚轮横向滚动量
加速器事件
回调函数
std::function<void(Acceleration*, Event*)> onAccelerationEvent;
参数Acceleration,加速器信息
参数Event,实际是一个EventAcceleration类,可以强制转换。
自定义事件,用户可以自己定义一些事件然后,可以通过EventDispatcher::dispatchEvent函数发送自定义事件。
回掉函数
std::function<void(EventCustom*)> _onCustomEvent;
EventListenerCustom在创建是需要指定一个字符串类型的关键字,这个关键字用来区分自定义事件的类型。
参数EventCustom
getEventName函数获取自定义事件的类型
setUserData函数 设置额外的事件参数
getUserData函数 获取事件的额外参数
系统中默认会在绘制场景时发送一些自定义事件,通知绘制信息
主要如以下自定义事件:
_eventProjectionChanged转换矩阵发生变化时发送
_eventAfterDraw 绘制节点后发送
_eventAfterVisit 遍历场景后时发送
_eventBeforeUpdate 处理调度器前发送
_eventAfterUpdate 处理调度器后发送
_eventResetDirector 导演类被重置时发送
Event类有六个派生类EventAcceleration、EventCustom、EventKeyboard、EventMouse、EventTouch分别对应相应的事件,在Listener中会传递相应的事件类型给回调函数。
函数
获取事件类型 TOUCH、KEYBOARD、ACCELERATION、MOUSE、CUSTOM等等
inlineType getType() const { return_type; };
停止事件传递,设置后事件回停止传递给后续监听器
inline void stopPropagation() { _isStopped = true; };
设置所属节点,如果是fixedPriority监听器则返回null。否则返回addEventListenerWithSceneGraphPriority参数对应的node节点。
inline Node* getCurrentTarget() { return_currentTarget; };
Cocos中所有的事件都由EventDispatcher类管理,在Director类中有唯一的一个初始化实例 EventDispatcher* Director::_eventDispatcher;,在Director::init函数中进行了初始化。
_eventDispatcher = new (std::nothrow) EventDispatcher();
函数接口
订阅事件,添加监听器
//以节点node的绘制顺序的优先级作为处理事件优先级添加监听器,绘制在屏幕前面的节点优先级更高。该监听器会在节点node释放是移除。Node类的析构函数中调用的移除监听器函数。
void addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node);
//以固定优先级添加监听器。fixedPriority小于0,则优先级高于SceneGraphPriority;大于0,则低于SceneGraphPriority。不可用等于0,等于0留给了SceneGraphPriority用。
void addEventListenerWithFixedPriority(EventListener* listener, intfixedPriority);
以固定优先级1,添加一个定制事件监听器。函数内部会创建一个定制事件监听器,类监听器要手动移除。
EventListenerCustom* addCustomEventListener(conststd::string &eventName, conststd::function<void(EventCustom*)>& callback);
移除监听器
void removeEventListener(EventListener* listener);
void removeEventListenersForType(EventListener::TypelistenerType);
void removeEventListenersForTarget(Node* target, boolrecursive = false);
void removeCustomEventListeners(conststd::string& customEventName);
void removeAllEventListeners();
暂停监听器事件
void pauseEventListenersForTarget(Node* target, boolrecursive = false);
恢复监听器
void resumeEventListenersForTarget(Node* target, boolrecursive = false);
禁用/开启事件
void setEnabled(boolisEnabled);
发送事件
所有的事件都可以通过该函数方式,更加Event的类型发送相应类型的事件。
void dispatchEvent(Event* event);
发送自定义事件
void dispatchCustomEvent(const std::string &eventName, void *optionalUserData = nullptr);
监听器的管理
EventDispatcher类有一个按事件类型分类的Map变量存储了所有注册的listener。
std::unordered_map<EventListener::ListenerID, EventListenerVector*> _listenerMap;
这个Map的数据变量EventListenerVector,里面会有两个EventListener数组。
std::vector<EventListener*>*_fixedListeners;
std::vector<EventListener*>*_sceneGraphListeners;
这两个数组分别存储了固定优先级的监听器和SceneGraphPriority的监听器。
当调用addEventListenerWithSceneGraphPriority函数时会添加到sceneGraphListeners中,调用addEventListenerWithFixedPriority函数和addCustomEventListener函数时会添加到fixedListeners中。添加完后会设置一个标识代表要下次访问需要排序。流程图如下:
事件的发送
事件发送是通过EventDispatcher::dispatchEvent函数实现的,函数会先检查是否要排序,如果需要则会对监听器进行排序。排序完成后再按排序后的监听器逐一分发事件。具体流程图大致如下: