【Cocos2d-x 3.x】 事件处理机制学习
在游戏中,触摸是最基本的,必不可少的。Cocos2d-x 3.x中定义了一系列事件,同时也定义了负责监听这些事件的监听器,另外,cocos定义了事件分发类,用来将事件派发出去以便可以实现相应的事件。
触摸事件EventCocos2d-x 3.x定义了事件基类Event,基于Event,引擎派生出几种事件: enum class Type { TOUCH,// 触摸事件 KEYBOARD,// 键盘事件 ACCELERATION,// 加速度事件 MOUSE,// 鼠标事件 FOCUS,// 焦点事件 CUSTOM // 自定义事件 }; EventTouchEventTouch是触摸事件中非常重要的一类事件,它定义了四种touch操作: enum class EventCode { BEGAN,MOVED,ENDED,CANCELLED }; 它还定义了一个 static int值,表示最大的触摸点数为15点: static const int MAX_TOUCHES = 15; 事件监听器EventListenerEventListener是事件监听器的基类,派生出的监听器对应各种触摸事件: enum class Type { UNKNOWN,TOUCH_ONE_BY_ONE,TOUCH_ALL_AT_ONCE,KEYBOARD,MOUSE,ACCELERATION,FOCUS,CUSTOM };
重要的成员变量:
1.onEvent,是绑定于该Listener的callback function,该func的声明使用了c++11
2.type与Event类似,增加一个Unknown的属性。
3.isRegistered变量非常重要,如果他没有被注册,则他的事件不会触发。
4.优先级代表了响应一个事件时的顺序,该值越低,越先响应。
5.node 代表了与该listener相关的node,用于 scene graph类的事件响应,具体的在Dispatcher里面有进行介绍。
6.ListenerID,这是该类型事件的标识符。除了EventCustomListener的ListerID是与name相关的,其余的ListenerID都是固定的,用于标识该类EventListener。
另外: 1.一个Listener想接收事件必须是enabled true 并且 paused false。 2.值得注意的是,pause的变量专门是为了scenGraph类的事件存在的(后续有说明),而且一个Node的onEnter和onExit 事件会影响到与Node相关的该类事件的pause状态。
EventListenerTouchOneByOne单点触摸方式,实现它时需要重写父类的四种触摸方式的函数: /// Overrides virtual EventListenerTouchOneByOne* clone() override; virtual bool checkAvailable() override; // public: std::function<bool(Touch*,Event*)> onTouchBegan; std::function<void(Touch*,Event*)> onTouchMoved; std::function<void(Touch*,Event*)> onTouchEnded; std::function<void(Touch*,Event*)> onTouchCancelled; 另外, 单点触摸当onTouchBegan函数不是nullptr时,它就是可用的: bool EventListenerTouchOneByOne::checkAvailable() { // EventDispatcher will use the return value of 'onTouchBegan' to determine whether to pass following 'move','end' // message to 'EventListenerTouchOneByOne' or not. So 'onTouchBegan' needs to be set. if (onTouchBegan == nullptr) { CCASSERT(false,"Invalid EventListenerTouchOneByOne!"); return false; } return true; } 还有,EventListenerTouchOneByOne可以设置吞噬。
EventListenerAllAtOnce多点触摸,当四种触摸方式函数都不为nullptr时,EventListenerAllAtOnce时可用的:
bool EventListenerTouchAllAtOnce::checkAvailable() { if (onTouchesBegan == nullptr && onTouchesMoved == nullptr && onTouchesEnded == nullptr && onTouchesCancelled == nullptr) { CCASSERT(false,"Invalid EventListenerTouchAllAtOnce!"); return false; } return true; } EventListenerCustomEventListenerID是根据独特的Name进行命名的,值得注意的是请确保你的name是具有唯一性的,否在在DispatchCustomEvent时会有问题。
事件分发器EventDispatcher首先,先看一个内嵌类EventListenerVector:
class EventListenerVector { public: EventListenerVector(); ~EventListenerVector(); size_t size() const; bool empty() const; void push_back(EventListener* item); void clearSceneGraphListeners(); void clearFixedListeners(); void clear(); inline std::vector<EventListener*>* getFixedPriorityListeners() const { return _fixedListeners; }; inline std::vector<EventListener*>* getSceneGraphPriorityListeners() const { return _sceneGraphListeners; }; inline ssize_t getGt0Index() const { return _gt0Index; }; inline void setGt0Index(ssize_t index) { _gt0Index = index; }; private: std::vector<EventListener*>* _fixedListeners; std::vector<EventListener*>* _sceneGraphListeners; ssize_t _gt0Index; };
fixedListeners和sceneGraphListeners
是两个非常非常重要的变量,这是两个截然不同的Listener列表。
1. sceneGraph类型的事件,是与当前正在运行的scene下node相关的事件,也就是说一个事件(比如说触摸事件),需要按照一定的响应序列,依次对这些Node进行事件响应,所以该类型的事件都会绑定一个与此相关联的node,并且响应顺序是与node在scene下的zorder相关的。该类型下的事件优先级统一为0.
2. fixed类型的事件相对就比较简单了,但是有一个限制就是其优先级不能为0.
在EventDispatcher的成员变量中有一个map :std::unordered_map<EventListener::ListenerID,EventListenerVector*> _listenerMap; 一种ListenerID对应了一个Vector。
EventDispatcher有三种添加事件的方式:addEventListenerWithSceneGraphPriority、addEventListenerWithFixedPriority和addCustomEventListener void EventDispatcher::addEventListenerWithSceneGraphPriority(EventListener* listener,Node* node) { CCASSERT(listener && node,"Invalid parameters."); CCASSERT(!listener->isRegistered(),"The listener has been registered."); //检查Listener可用性 if (!listener->checkAvailable()) return; //设置listener相关属性 listener->setAssociatedNode(node); listener->setFixedPriority(0); listener->setRegistered(true); addEventListener(listener); } addEventListenerWithSceneGraphPriority将监听器和node节点关联起来,addEventListenerWithSceneGraphPriority不需要手动移除监听器,因为在node的析构函数中会自动移除的,还有,addEventListenerWithSceneGraphPriority设置监听器的优先权为0。 优先权值越小,越先派发。
void EventDispatcher::addEventListenerWithFixedPriority(EventListener* listener,int fixedPriority) { CCASSERT(listener,"Invalid parameters."); //一个事件只能被注册一次 CCASSERT(!listener->isRegistered(),"The listener has been registered."); //Fixed类型的事件优先级不能是0 CCASSERT(fixedPriority != 0,"0 priority is forbidden for fixed priority since it's used for scene graph based priority."); //检查可用性 if (!listener->checkAvailable()) return; //设置关联属性 listener->setAssociatedNode(nullptr); listener->setFixedPriority(fixedPriority); listener->setRegistered(true); listener->setPaused(false); addEventListener(listener); } addEventListenerWithFixedPriority不能将监听器的优先权设置为0。 EventListenerCustom* EventDispatcher::addCustomEventListener(const std::string &eventName,const std::function<void(EventCustom*)>& callback) { //custom类的事件添加是通过eventName 和 eventcallBack来进行添加的 EventListenerCustom *listener = EventListenerCustom::create(eventName,callback); //custom的事件优先级被默认为1 addEventListenerWithFixedPriority(listener,1); return listener; } addCustomEventListener通过eventName和eventcallBack来添加的,自定以监听器优先权默认为1.。
这三种方法都使用了addEventListener函数来进行实际操作: void EventDispatcher::addEventListener(EventListener* listener) { //如果当前Dispatcher正在进行事件Dispatch,则放到toAddList中。 if (_inDispatch == 0) { forceAddEventListener(listener); } else { // std::vector _toAddedListeners.push_back(listener); } listener->retain(); } 如果当前派发器没有进行事件的派发,则强制进行,并添加该事件监听器,该操作由forceAddEventListener来完成: void EventDispatcher::forceAddEventListener(EventListener* listener) { EventListenerVector* listeners = nullptr; EventListener::ListenerID listenerID = listener->getListenerID(); //找到该类eventlistener的vector,此处的vector是EventVector auto itr = _listenerMap.find(listenerID); //如果没有找到,则需要向map中添加一个pair if (itr == _listenerMap.end()) { listeners = new EventListenerVector(); _listenerMap.insert(std::make_pair(listenerID,listeners)); } else { listeners = itr->second; } //将该类别listenerpush_back进去(这个函数调用的是EventVector的pushback哦) listeners->push_back(listener); //如果优先级是0,则设置为graph。 if (listener->getFixedPriority() == 0) { //设置该listenerID的DirtyFlag //(setDirty函数可以这样理解,每个ListenerID都有特定的dirtyFlag,每次进行add操作后,都要更新该ID的flag) setDirty(listenerID,DirtyFlag::SCENE_GRAPH_PRIORITY); //如果是sceneGraph类的事件,则需要处理两个方面: //1.将node 与event 关联 //2.如果该node是运行中的,则需要恢复其事件(因为默认的sceneGraph listener的状态时pause) //增加该listener与node的关联 auto node = listener->getAssociatedNode(); CCASSERT(node != nullptr,"Invalid scene graph priority!"); associateNodeAndEventListener(node,listener); //恢复node的运行状态 if (node->isRunning()) { resumeEventListenersForTarget(node); } } else { setDirty(listenerID,DirtyFlag::FIXED_PRIORITY); } } 如果优先级是0,则还有将监听器与node关联起来的操作: void EventDispatcher::associateNodeAndEventListener(Node* node,EventListener* listener) { //将listener与node关联,先从map中找到与该node相关的listener vector std::vector<EventListener*>* listeners = nullptr; auto found = _nodeListenersMap.find(node); if (found != _nodeListenersMap.end()) { listeners = found->second; } else { listeners = new std::vector<EventListener*>(); _nodeListenersMap.insert(std::make_pair(node,listeners)); } //vector内添加该listener,这里的vector 是std::vector listeners->push_back(listener); } DispatchEvent(核心)
|