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
在这里插入图片描述
每个EventListener由数个回调函数、一个订阅者类型type,以及一个listenerlD组成。当然,有些事件类型对应多个处理函数,例如EventListenerTouchOneByOne就根据其键的触摸按下(onTouchBegan),触摸移动(onTouchMoved),触摸取消(onTouchCancelled)和触摸结束(onTouchEnded)状态提供四个回调函数。

 EventListenerTouchOneByOne  - - -单点触摸
 typedef std::function<bool(Touch*, Event*)> ccTouchBeganCallback;
 typedef std::function<void(Touch*, Event*)> ccTouchCallback;
 ccTouchBeganCallback onTouchBegan;
 ccTouchCallback onTouchMoved;
 ccTouchCallback onTouchEnded;
 ccTouchCallback onTouchCancelled;
 
 EventListenerTouchAllAtOnce  - - - 多点触摸
 typedef std::function<void(const std::vector<Touch*>&, Event*)> ccTouchesCallback;
 ccTouchesCallback onTouchesBegan;
 ccTouchesCallback onTouchesMoved;
 ccTouchesCallback onTouchesEnded;
 ccTouchesCallback onTouchesCancelled;
 
 EventListenerMouse - - - 鼠标
 std::function<void(EventMouse* event)> onMouseDown;
 std::function<void(EventMouse* event)> onMouseUp;
 std::function<void(EventMouse* event)> onMouseMove;
 std::function<void(EventMouse* event)> onMouseScroll;
 
 EventListenerKeyboard - - - 键盘
 std::function<void(EventKeyboard::KeyCode, Event*)> onKeyPressed;
 std::function<void(EventKeyboard::KeyCode, Event*)> onKeyReleased;
 
 EventListenerFocus - - - 焦点
 std::function<void(ui::Widget*, ui::Widget*)> onFocusChanged;
 
 EventListenerCustom - - 自定义事件
 std::function<void(EventCustom*)> _onCustomEvent;
 
 EventListenerAcceleration - - - 加速器事件
std::function<void(Acceleration*, Event*)> onAccelerationEvent;

EventListenerController - - - 游戏控制器
std::function<void(Controller*, Event*)> onConnected;
std::function<void(Controller*, Event*)> onDisconnected;
std::function<void(Controller*, int, Event*)> onKeyDown;
std::function<void(Controller*, int, Event*)> onKeyUp;
std::function<void(Controller*, int, Event*)> onKeyRepeat;
std::function<void(Controller*, int, Event*)> 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<void(EventCustom*)>& 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<EventTouch*>(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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值