1:事件分发原理
cocos2dx中事件由触发到完成响应,主要由以下三部分组成:
事件分发器EventDispatcher;
事件EventTouch、EventKeyboard等;
事件监听器EventListenerTouch、EventListenerKeyboard等。
在cocos2dx中添加事件处理的流程
Event是由系统产生的消息。Listener是程序中的消息处理函数,来处理分发的Event 。最后将Listener添加到事件分发中心EventDispatcher中,实现Event和Listener的之间映射关系。
举个例子,你媳妇在家喊了一声“快去做饭”,你的媳妇相当于是事件分发器,“快去做饭”就是Event。这是你媳妇,你做出反应,“好的,马上去”或者“点外卖吧”,这就相当于Listener。如果陌生人的话就没有映射关系,不会做出响应。
2:Event 事件
首先来看看CCEvent类
class CC_DLL Event : public Ref
{
public:
enum class Type
{
TOUCH, //触摸事件
KEYBOARD, //键盘事件
ACCELERATION, //加速器事件
MOUSE, //鼠标事件
FOCUS, //焦点事件
GAME_CONTROLLER, //游戏控制器
CUSTOM //自定义事件
};
public:
inline Type getType() const { return _type; };
inline void stopPropagation() { _isStopped = true; };
inline bool isStopped() const { return _isStopped; };
inline Node* getCurrentTarget() { return _currentTarget; };
protected:
Type _type; ///< Event type
bool _isStopped; ///< whether the event has been stopped.
Node* _currentTarget; ///< Current target
friend class EventDispatcher;
};
当前有七种类型的EventType,从上面的代码可以看出Event具有Type属性,事件绑定的target以及事件分发是否停止。
Event这部分系统会处理,EventDispatcher会负责Event的分发。
2:EventListener 事件监听器
先来看下哪些类继承了事件监听器的基类EventListener
每个EventListener由数个回调函数、一个订阅者类型type,以及一个listenerlD组成。当然,有些事件类型对应多个处理函数,例如EventListenerTouchOneByOne就根据其键的触摸按下(onTouchBegan),触摸移动(onTouchMoved),触摸取消(onTouchCancelled)和触摸结束(onTouchEnded)状态提供四个回调函数。
EventListenerTouchOneByOne - - -单点触摸
typedef std::function ccTouchBeganCallback;
typedef std::function ccTouchCallback;
ccTouchBeganCallback onTouchBegan;
ccTouchCallback onTouchMoved;
ccTouchCallback onTouchEnded;
ccTouchCallback onTouchCancelled;
EventListenerTouchAllAtOnce - - - 多点触摸
typedef std::function&, Event*)> ccTouchesCallback;
ccTouchesCallback onTouchesBegan;
ccTouchesCallback onTouchesMoved;
ccTouchesCallback onTouchesEnded;
ccTouchesCallback onTouchesCancelled;
EventListenerMouse - - - 鼠标
std::function onMouseDown;
std::function onMouseUp;
std::function onMouseMove;
std::function onMouseScroll;
EventListenerKeyboard - - - 键盘
std::function onKeyPressed;
std::function onKeyReleased;
EventListenerFocus - - - 焦点
std::function onFocusChanged;
EventListenerCustom - - 自定义事件
std::function _onCustomEvent;
EventListenerAcceleration - - - 加速器事件
std::function onAccelerationEvent;
EventListenerController - - - 游戏控制器
std::function onConnected;
std::function onDisconnected;
std::function onKeyDown;
std::function onKeyUp;
std::function onKeyRepeat;
std::function onAxisEvent;
3:绑定event和Listener
void addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node);
将listener和node进行绑定,listener的分发顺序排序方式为SCENE_GRAPH_PRIORITY,node的ZOrder决定事件的分发顺序。
void addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority);
添加listener,listener的分发排序方式为FIXED_PRIORITY, FIXED_PRIORITY方式的分发顺序高于SCENE_GRAPH_PRIORITY方式。
fixedPriority决定listener的分发顺序,越小优先级越高。
EventListenerCustom* addCustomEventListener(const std::string &eventName, const std::function& callback);
这个要慎用,它也是FIXED_PRIORITY。
在开发过程中,基本使用第一个的,第一个是将Listener和node绑定起来,只能当node创建成功才会接收到消息,并且是根据图层顺序来排序的。
后面两者和node无关,在我的理解中只有顶层统一处理一类消息才会用吧,场景啥的不会用。
4:分发过程解析
有了Event和EventListener,绑定两者的关系,就能实现事件处理,接下来解析下事件分发的过程。
//先来看看EventDispatcher中的dispatchEvent函数
void EventDispatcher::dispatchEvent(Event* event)
{
if (!_isEnabled)
return;
updateDirtyFlagForSceneGraph();
DispatchGuard guard(_inDispatch);
if (event->getType() == Event::Type::TOUCH)
{
dispatchTouchEvent(static_cast(event));
return;
}
auto listenerID = __getListenerID(event);
//根据EventType获取ListenerID
sortEventListeners(listenerID);
//根据ListenerID将事件分发到监听器的顺序就行排序。
auto pfnDispatchEventToListeners = &EventDispatcher::dispatchEventToListeners;
if (event->getType() == Event::Type::MOUSE) {
pfnDispatchEventToListeners = &EventDispatcher::dispatchTouchEventToListeners;
}
auto iter = _listenerMap.find(listenerID);
if (iter != _listenerMap.end())
{
auto listeners = iter->second;
auto onEvent = [&event](EventListener* listener) -> bool{
event->setCurrentTarget(listener->getAssociatedNode());
listener->_onEvent(event);
return event->isStopped();
};
(this->*pfnDispatchEventToListeners)(listeners, onEvent);
}
updateListeners(event);
}
我们可以从代码中看到一个事件的分发过程,
updateDirtyFlagForSceneGraph();
对Touch事件进行特殊处理。
根据事件类型获得ListenerID。
根据ListenerID对分发顺序进行排序。
分发事件。
大家可以自己去看下sortEventListeners(listenerID);这样也能加强对sceneGraphPrority和FixedPrority多点了解。
这部分的内容有点多,今天走路去景山公园逛了一圈,太累了,先休息啦。争取明天能针对TouchEvent和CustomEvent这两个在手游中使用比较多的事件进行实例分析。然后还差一个周报。
本文参考了https://blog.csdn.net/hkchenhao/article/details/51871212