cocos2dx_事件分发机制解析

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
cocos2dx_事件分发机制解析_第1张图片
每个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

你可能感兴趣的:(游戏开发)