zoukankan      html  css  js  c++  java
  • cocos2d 3.0自定义事件答疑解惑

    疑惑一:在事件分发中修改订阅者

    ,对于这个的理解。

    事件的分发是可以嵌套的,cocos2dx使用_inDispatch来保存当前嵌套的深度,当调用第一个dispatchEvent的时候,_inDispatch=0,表示之前没有事件在分发,这是第一次

    进行事件的分发。先看一下

    dispatchEvent(Event* event)的源码,这个方法就是开始分发事件的时候的方法

     

     

    代码的大体步骤就是

    1

     updateDirtyFlagForSceneGraph();,这个我也没研究,暂时不用管它,不影响理解

    DispatchGuard guard(_inDispatch);

    这个就是对_inDispatch的加一,也就是每次调用

    dispatchEvent,就加一,dispatchEvent运行完之后,因为guard是局部变量,会自动释放,_inDispatch会自动减一。

    3.

     

        if (event->getType() == Event::Type::TOUCH)

        {

            dispatchTouchEvent(static_cast<EventTouch*>(event));

            return;

        }

    对于最复杂的触摸,另外写了一个方法,这里也不说

     auto listenerID = __getListenerID(event);

    sortEventListeners(listenerID);

    找到对应event事件的名字,就是个字符串名,他是作为一个key,是唯一的,他的值是一个数组,数组里是订阅了这个key事件的listener,

    sort,对这个事件的所有订阅者进行排序,确定谁先分发,谁后分发

    5

     if (iter != _listenerMap.end())

        {

            auto listeners = iter->second;

            

            auto onEvent = [&event](EventListener* listener) -> bool{

                event->setCurrentTarget(listener->getAssociatedNode());

                listener->_onEvent(event);

                return event->isStopped();

            };

            

            dispatchEventToListeners(listeners, onEvent);

        }

    遍历该事件的所有订阅者,进行事件分发

    6,

     updateListeners(event);

    在分发的最后,重新对事件的订阅者进行排序

    下面这个图片,就是解释了他的作用,进行排序的前提是_inDispatch必须为0

     

     

    上面是对分发事件方法的解释也就是 dispatchEvent方法

    下面再说说订阅事件的方法,这里只说自定义的,系统事件也是一个道理的,就不说了

     _eventDispatcher->addEventListenerWithFixedPriority(listenerA, 1);

    看源码:

    大体上就这三块代码

    addEventListener方法中,分两种情况

    当嵌套为0的时候,走第三个方法,就是吧listener加入到订阅者字典里

    _listenerMap,key为listnerID,也就是事件的名字,比如触摸事件的onBegan或者onEnded,value就是对这个事件的所有订阅者。

    当嵌套不为0的时候,加入到

    _toAddedListeners.push_back(listener);

    这个可以看做是一个临时的储存订阅者的数组,当所有嵌套事件分发结束,_indispatch=0, updateListeners(event)这个方法把这个

    数组里的订阅者都加到_listenerMap中。

    这里什么时候把_toAddedListeners里的订阅者加到_listenerMap中,这个顺序会引发一些问题和疑惑,下面通过设置几个场景,对其进行分析

    情景一:注册了事件A和事件B,而且每一个事件都有一个订阅者,然后在事件A的分发中的回调函数中,分发B,看代码

     EventListenerCustom*  listenerA = EventListenerCustom::create("game_custom_eventA", [=](EventCustom* event){
            printf("game_custom_eventA
    ");
            EventCustom event2("game_custom_eventB");
            _eventDispatcher->dispatchEvent(&event2);
         });
        
        
         EventListenerCustom*  listenerB  = EventListenerCustom::create("game_custom_eventB", [=](EventCustom* event){
            
            printf("game_custom_eventB
    ");
        });
        _eventDispatcher->addEventListenerWithFixedPriority(listenerA, 1);
        _eventDispatcher->addEventListenerWithFixedPriority(listenerB, 2);
        
        EventCustom event("game_custom_eventA");
        
        _eventDispatcher->dispatchEvent(&event);

    运行结果:

    game_custom_eventA

    game_custom_eventB

    分析:这个很明显了

    先把listnerA加入到_listenerMap,再把listnerB加入到_listenerMap,然后先触发eventA,在A的回调中在dispatch B,

    dispatchEvent方法中只对_listenerMap进行查找,然后派发,等全部派发完毕之后,才

     updateListeners(event);并且还要保证_indispatch为0.

    因为listnerA和B已经在开始加到了 _listnermap中,所以这样调用没有问题。

     

    情景二:先注册了事件A,A有一个订阅者,然后在事件A的分发中的回调函数中,注册B,并分发B,代码

      EventListenerCustom*  listenerB  = EventListenerCustom::create("game_custom_eventB", [=](EventCustom* event){
            
            printf("game_custom_eventB
    ");
        });
     
        EventListenerCustom*  listenerA = EventListenerCustom::create("game_custom_eventA", [=](EventCustom* event){
            printf("game_custom_eventA
    ");
            
             _eventDispatcher->addEventListenerWithFixedPriority(listenerB, 2);
            EventCustom event2("game_custom_eventB");
            _eventDispatcher->dispatchEvent(&event2);
         });
        
        
       
        _eventDispatcher->addEventListenerWithFixedPriority(listenerA, 1);
       
        
        EventCustom event("game_custom_eventA");
        
        _eventDispatcher->dispatchEvent(&event);
        

    运行结果:

    game_custom_eventA

    为什么这次eventB没有执行呢。通过调试分析得出原因:

    在eventA的回调函数执行的时候,dispatcheventA这个方法还没有执行完毕,所以_dispatch还没有减一,所以肯定是大于0的,所以在此之前的

    updateListeners(event);,_listenerMap里面不会有eventB的listener,所以

    EventCustom event2("game_custom_eventB");
     _eventDispatcher->dispatchEvent(&event2);

    中,不会检索到eventB,自然也就不会派发事件给订阅者了。

    要想listnerB的回调函数起作用,只能是EventA的disptch全部派发完毕,

    情景三:在事件分发过程中修改订阅者优先级。注册了一个事件EventA,然后有三个订阅者,顺序为1,2,3,然后在1的回调函数在修改2,3的优先级,看看是否会改变派发顺序

      
        EventListenerCustom*  listenerB  = EventListenerCustom::create("game_custom_eventA", [=](EventCustom* event){
            
            printf("game_custom_eventB
    ");
        });
        
        EventListenerCustom*  listenerC= EventListenerCustom::create("game_custom_eventA", [=](EventCustom* event){
            printf("game_custom_eventC
    ");
            
        });
    
        
        EventListenerCustom*  listenerA = EventListenerCustom::create("game_custom_eventA", [=](EventCustom* event){
            printf("game_custom_eventA
    ");
             _eventDispatcher->setPriority(listenerB, 3);
            _eventDispatcher->setPriority(listenerC, 2);
        
        });
        
        _eventDispatcher->addEventListenerWithFixedPriority(listenerA, 1);
        _eventDispatcher->addEventListenerWithFixedPriority(listenerB, 2);
        _eventDispatcher->addEventListenerWithFixedPriority(listenerC, 3);
       
        
        EventCustom event("game_custom_eventA");
        
        _eventDispatcher->dispatchEvent(&event);

    结果

    game_custom_eventA

    game_custom_eventB

    game_custom_eventC

    看来改变优先级对这次并没有起作用.是在当前嵌套深度内,任何导致对订阅者优先级的修改不会影响到后面订阅者的分发顺序。因为对订阅者的重新排序是在disaptchEvent

    中开始执行分发之前进行的。

    但是,对优先级的修改将在下一个嵌套深度内生效,因为新的事件分发会对订阅者重新排序,注意这里情形二不同,虽然都是在嵌套内进行的,但是情形二是重新注册事件,而情形三是针对优先级的,注意区别。

    看看修改了的代码:

      
        EventListenerCustom*  listenerB  = EventListenerCustom::create("game_custom_eventA", [=](EventCustom* event){
            
            printf("game_custom_eventB
    ");
        });
        
        EventListenerCustom*  listenerC= EventListenerCustom::create("game_custom_eventA", [=](EventCustom* event){
            printf("game_custom_eventC
    ");
            
        });
        
        EventListenerCustom*  listenerA = EventListenerCustom::create("game_custom_eventA", [=](EventCustom* event){
            printf("game_custom_eventA
    ");
             _eventDispatcher->setPriority(listenerB, 3);
            _eventDispatcher->setPriority(listenerC, 2);
        
        });
        EventListenerCustom*  listenerD= EventListenerCustom::create("game_custom_eventA", [=](EventCustom* event){
            printf("game_custom_eventD
    ");
            
            _eventDispatcher->removeEventListener(listenerA);
           
            
            _eventDispatcher->dispatchEvent(event);
    
            
        });
        
    
        _eventDispatcher->addEventListenerWithFixedPriority(listenerA, 1);
        _eventDispatcher->addEventListenerWithFixedPriority(listenerB, 2);
        _eventDispatcher->addEventListenerWithFixedPriority(listenerC, 3);
        _eventDispatcher->addEventListenerWithFixedPriority(listenerD, 4);
        
        EventCustom event("game_custom_eventA");
        
        _eventDispatcher->dispatchEvent(&event);

    运行结果:

    game_custom_eventA

    game_custom_eventB

    game_custom_eventC

    game_custom_eventD

    game_custom_eventC

    game_custom_eventB

    。。。。

    死循环

    因为实在举不出合适的例子了,我在这里主要表达的是对优先级的修改将在下个个嵌套层次深度内生效,即使最开始的那个dispatchEvent没有结束,里面在进行的dispatchEvent,也会体现出改变后的优先级效果。

    目前就先添加这几种令人疑惑的情形,以后如果在遇到新的问题,会持续更新,关键是要去看懂源码,知道分发原理,这样在遇到各种奇怪的分发问题的时候,都能找到原因和解决方案。正所谓  敌有千变万化 我有一定之规

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    五秒自动刷新页面
    浅析MySQL中exists与in的使用 (写的非常好)
    基于shiro授权过程
    shiro认证
    jsp中<c:if>与<s:if>的区别
    链表数据结构相关
    队列Queue中add()和offer()的区别?
    Java中有几种线程池?
    解决idea使用jstl标签报错和不自动提示
    事务
  • 原文地址:https://www.cnblogs.com/xiaonanxia/p/4866478.html
Copyright © 2011-2022 走看看