Cocos2d-X3.0 刨根問底(七)----- 事件機制Event源碼分析

這一章,咱們來分析Cocos2d-x 事件機制相關的源碼, 根據Cocos2d-x的工程目錄,咱們能夠找到全部關於事件的源碼都存在放在下圖所示的目錄中。node

4

從這個event_dispatcher目錄中的文件命名上分析 cocos2d-x與事件相關的類一共有四種, Event, EventListener,EventDispatcher, Touch分別爲 事件,事件偵聽器,事件分發器,觸摸設計模式

咱們先從Event類開始。數組

打開CCEvent.h文件安全

/**
 *   Base class of all kinds of events.
 */
class Event : public Ref
{
public:
    enum class Type
    {
        TOUCH,
        KEYBOARD,
        ACCELERATION,
        MOUSE,
        CUSTOM
    };
    
protected:
    /** Constructor */
    Event(Type type);
public:
    /** Destructor */
    virtual ~Event();

    /** Gets the event type */
    inline Type getType() const { return _type; };
    
    /** Stops propagation for current event */
    inline void stopPropagation() { _isStopped = true; };
    
    /** Checks whether the event has been stopped */
    inline bool isStopped() const { return _isStopped; };
    
    /** @brief Gets current target of the event
     *  @return The target with which the event associates.
     *  @note It onlys be available when the event listener is associated with node. 
     *        It returns 0 when the listener is associated with fixed priority.
     */
    inline Node* getCurrentTarget() { return _currentTarget; };
    
protected:
    /** Sets current target */
    inline void setCurrentTarget(Node* target) { _currentTarget = target; };
    
    Type _type;     ///< Event type
    
    bool _isStopped;       ///< whether the event has been stopped.
    Node* _currentTarget;  ///< Current target
    
    friend class EventDispatcher;
};

這個類而且不復雜,先看一下類的註釋,Event類是全部事件類的基類。數據結構

類定義的最上面有一個枚舉,定義了事件的類型ide

enum class Type
    {
        TOUCH,
        KEYBOARD,
        ACCELERATION,
        MOUSE,
        CUSTOM
    };

事件的各種分別爲 ,觸摸事件, 鍵盤事件, 加速器事件,鼠標事件, 用戶自定義事件。函數

再看一下Event類的成員變量優化

_type 描述當前對象的事件類型。this

_isStopped 描述當前事件是否已經中止spa

_currentTarget 是偵聽事件的Node類型的對象

就這三個成員變量,含義很簡單,Event類的幾個方法也沒什麼特別的,就是成員變量的get set方法。

下面咱們再看一下用戶自定義事件 EventCustom  類的定義,來了解一下。

class EventCustom : public Event
{
public:
    /** Constructor */
    EventCustom(const std::string& eventName);
    
    /** Sets user data */
    inline void setUserData(void* data) { _userData = data; };
    
    /** Gets user data */
    inline void* getUserData() const { return _userData; };
    
    /** Gets event name */
    inline const std::string& getEventName() const { return _eventName; };
protected:
    void* _userData;       ///< User data
    std::string _eventName;
};

 

在自定義事件類中,多出了兩個成員變量 一個是 _userData用來記錄用戶自定義數據,另外一個_eventName用戶給這個事件起的別名。

其它的關於Event的子類,你們能夠本身看一下,內容都差很少。

 

下面咱們來分析 EventListener 這個類。

這個類看着挺長,其實沒什麼內容,先看下類的定義。

EventListener也一樣定義了一個類型

enum class Type
    {
        UNKNOWN,
        TOUCH_ONE_BY_ONE,
        TOUCH_ALL_AT_ONCE,
        KEYBOARD,
        MOUSE,
        ACCELERATION,
        CUSTOM
    };

這個類型與Event的類型有一小點不一樣,就是將觸摸事件類型分紅了 One by One (一個接一個) 與  All At Once (同時一塊兒)兩種。

再看 EventListener 的屬性

std::function<void(Event*)> _onEvent;   // 用來記錄偵聽器回調函數

    Type _type;                             // 偵聽器的類型
    ListenerID _listenerID;                 // 偵聽器的ID 實際上是個字符串
    bool _isRegistered;                     // 標記當前偵聽器是否已經加入到了事件分發器中的狀態變量

    int   _fixedPriority;   // 偵聽器的優先級別,數值越高級別越高.默認爲0
    Node* _node;            // 場景結點(這裏這個變量的做用還沒能理解好,後面咱們再進行分析)
    bool _paused;           // 標記此偵聽器是否爲暫停狀態。
    bool _isEnabled;        // 標記此偵聽器是否有效
上面分析了屬性的功能 。EventListener的很簡單,大部分都是屬性的讀寫方法(get/set)這裏就很少說了,下面咱們重點看一下init方法及實現。
bool EventListener::init(Type t, const ListenerID& listenerID, const std::function<void(Event*)>& callback)
{
    _onEvent = callback;
    _type = t;
    _listenerID = listenerID;
    _isRegistered = false;
    _paused = true;
    _isEnabled = true;
    
    return true;
}

這個init函數 也很簡單,就是一些成員變量的賦值操做。

在EventListener定義中有兩個純虛函數,咱們看一下。

/** Checks whether the listener is available. */
    virtual bool checkAvailable() = 0;

    /** Clones the listener, its subclasses have to override this method. */
    virtual EventListener* clone() = 0;

經過註釋瞭解這兩個函數 一個是驗證listener是否有效 別一個是clone方法。

EventListener是抽象類,那麼我們找一個它的子類的具體實現。

咱們看一下EventListenerCustom這個類的定義。

class EventListenerCustom : public EventListener
{
public:
    /** Creates an event listener with type and callback.
     *  @param eventType The type of the event.
     *  @param callback The callback function when the specified event was emitted.
     */
    static EventListenerCustom* create(const std::string& eventName, const std::function<void(EventCustom*)>& callback);
    
    /// Overrides
    virtual bool checkAvailable() override;
    virtual EventListenerCustom* clone() override;
    
CC_CONSTRUCTOR_ACCESS:
    /** Constructor */
    EventListenerCustom();
    
    /** Initializes event with type and callback function */
    bool init(const ListenerID& listenerId, const std::function<void(EventCustom*)>& callback);
    
protected:
    std::function<void(EventCustom*)> _onCustomEvent;
    
    friend class LuaEventListenerCustom;
};

EventListenerCustom又增長了一個成員變量,_onCustomEvent 接收一個EventCustom類型的事件爲參數的回調函數。

咱們能夠看到,這個類的構造函數不是public形式的,因此這個類不能直接被實例化,

找到了EventListenerCustom提供了一個靜態函數 create 因此實例化這個在的對象必定要使用這個create方法。

咱們看一下create方法。

EventListenerCustom* EventListenerCustom::create(const std::string& eventName, const std::function<void(EventCustom*)>& callback)
{
    EventListenerCustom* ret = new EventListenerCustom();
    if (ret && ret->init(eventName, callback))
    {
        ret->autorelease();
    }
    else
    {
        CC_SAFE_DELETE(ret);
    }
    return ret;
}

這個Create函數的結構,與Node的Create結構同樣,新建立的EventListener加入到了autorelease列表裏面,在Create的時候調用了init函數,咱們再看一下EventListenerCustom::init方法。

bool EventListenerCustom::init(const ListenerID& listenerId, const std::function<void(EventCustom*)>& callback)
{
    bool ret = false;
    
    _onCustomEvent = callback;
    
    auto listener = [this](Event* event){
        if (_onCustomEvent != nullptr)
        {
            _onCustomEvent(static_cast<EventCustom*>(event));
        }
    };
    
    if (EventListener::init(EventListener::Type::CUSTOM, listenerId, listener))
    {
        ret = true;
    }
    return ret;
}

這個函數也沒什麼特別的,但值得注意的是,CustomEvent的回調與基類的回調函數是怎麼關聯的。

在EventListenerCustom類中有一個成員變量_onCustomEvent它是一個函數指針來記錄事件觸發後的回調函數。

在EventListener類中也有一個_onEvent成員變量來記錄事件觸發的時候的回調函數。

EventListenerCustom::init函數中 有一個匿名函數,listener 在這個匿名函數中 判斷了_onCustomEvent是否爲空,若是不爲空那麼調用_onCustomEvent。

後面調用基類的init方法,把這個匿名函數傳遞給了基類的_onEvent,這樣基類的回調函數_onEvent與子類的_onCustomEvent就關聯起來了。

下面咱們看一下抽象方法在子類中怎麼實現的

EventListenerCustom* EventListenerCustom::clone()
{
    EventListenerCustom* ret = new EventListenerCustom();
    if (ret && ret->init(_listenerID, _onCustomEvent))
    {
        ret->autorelease();
    }
    else
    {
        CC_SAFE_DELETE(ret);
    }
    return ret;
}

clone的操做與Create方法很象,其實就是從新建立了一個對象,它的_listenerID 、 _onCustomEvent值與原對象同樣。返回的是新建立的對象指針。

事件類Event有了,偵聽器類EventListener 有了,那麼下面咱們來分析事件的分發器EventDispatcher。

打開CCEventDispatcher.h文件

/**
This class manages event listener subscriptions
and event dispatching.

The EventListener list is managed in such a way that
event listeners can be added and removed even
from within an EventListener, while events are being
dispatched.
*/
class EventDispatcher : public Ref
{

這個類也是Ref的子類,從註釋上面咱們先總體的瞭解EventDispatcher的功能及做用。這個類管理事件偵聽腳本及事件的分發處理。有一個事件偵聽器列表,來記錄全部偵聽的事件。分析這個類咱們首先仍是從這個類的屬性上開始。

    /** 保存全部事件偵聽器的列表*/
    std::unordered_map<EventListener::ListenerID, EventListenerVector*> _listenerMap;
    
    /** 有髒標記的偵聽器列表,具體什麼是髒標記後面再分析。 */
    std::unordered_map<EventListener::ListenerID, DirtyFlag> _priorityDirtyFlagMap;
    
    /** Node類型結點的事件偵聽器列表 */
    std::unordered_map<Node*, std::vector<EventListener*>*> _nodeListenersMap;
    
    /** Node結點與事件級別的列表, 看到這裏還不太明白這個列表是什麼做用,後面着重尋找答案*/
    std::unordered_map<Node*, int> _nodePriorityMap;
    
    /** 記錄結點的ZOrder與結點的指針列表 也不太知道這是幹什麼用的,後面找答案*/
    std::unordered_map<float, std::vector<Node*>> _globalZOrderNodeMap;
    
    /** 事件分必後加入的偵聽器列表*/
    std::vector<EventListener*> _toAddedListeners;
    
    /** 與場景優先級偵聽器相關聯的node結點。*/
    std::set<Node*> _dirtyNodes;
    
    /** 描述事件分發器是否在分發事件 */
    int _inDispatch;
    
    /** 標記是否開啓事件分發*/
    bool _isEnabled;
    /** 優先級索引*/ 
    int _nodePriorityIndex;
    
    /** 內部自定義偵聽器索引*/ 
    std::set<std::string> _internalCustomListenerIDs;

上面針對這個類的屬性作了字面上的分析,大部分屬性目前還不知道具體功能,不過不要緊,後面咱們一個一個去破解它的含義。

如今 咱們來分析一下這個類的構造函數

EventDispatcher::EventDispatcher()
: _inDispatch(0)
, _isEnabled(false)
, _nodePriorityIndex(0)
{
    _toAddedListeners.reserve(50);
    
    // fixed #4129: Mark the following listener IDs for internal use.
    // Therefore, internal listeners would not be cleaned when removeAllEventListeners is invoked.
    _internalCustomListenerIDs.insert(EVENT_COME_TO_FOREGROUND);
    _internalCustomListenerIDs.insert(EVENT_COME_TO_BACKGROUND);
}

構造函數不復雜,除了初始幾個變量,咱們能夠看到,向_internalCustomListenerIDs加入了兩個自定義的事件,這裏還有一行註釋,說明,在清除全部事件偵聽器的時候內容偵聽器是不會被清除的。

咱們看一下這兩個內容自定義的事件,一個是程序返回到後臺,一個是程序返回到前臺。估計這在這兩個事件裏面要作一些暫停的工做。

從EventDispatcher的成員變量上看,都是圍繞着偵聽器列表來定義的,那麼咱們就看一下把偵聽器加入到偵聽器列表的方法。

第一個add方法

/** Adds a event listener for a specified event with the priority of scene graph.
     *  @param listener The listener of a specified event.
     *  @param node The priority of the listener is based on the draw order of this node.
     *  @note  The priority of scene graph will be fixed value 0. So the order of listener item
     *          in the vector will be ' <0, scene graph (0 priority), >0'.
     */
    void addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node);

從註釋上能夠知道這個方法的做用是,將一個指定的事件偵聽器依照場景圖的優先級順序加入到偵聽器列表裏面, 這個方法與場景圖的繪製順序有關係,

場景的結點渲染順序也就是zOrder的順序,場景中的結點優先級通常都是0,偵聽器的存放順序就是 小於0  等於0 大於0這樣一個順序 。

咱們看一下這個方法的實現

void EventDispatcher::addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node)
{
    CCASSERT(listener && node, "Invalid parameters.");
    CCASSERT(!listener->isRegistered(), "The listener has been registered.");
    
    if (!listener->checkAvailable())
        return;
    
    listener->setAssociatedNode(node);
    listener->setFixedPriority(0);
    listener->setRegistered(true);
    
    addEventListener(listener);
}

這個方法內容也簡單,

1. 先檢查了偵聽器是否有效

2. 將結點與偵聽器作了關聯

3. 設置優先級爲0,從註釋上咱們已經獲得這個信息了,這個方法加入的偵聽器都是顯示對象的,因此優先級都爲0

4. 設置偵聽器已經註冊狀態

5. 調用了addEventListener方法,將偵聽器加入到EventDispatcher的偵聽器列表裏。

下面咱們看一下addEventListener方法,瞭解是將偵聽器加入到偵聽器管理列表裏的過程

void EventDispatcher::addEventListener(EventListener* listener)
{
    if (_inDispatch == 0)
    {
        forceAddEventListener(listener);
    }
    else
    {
        _toAddedListeners.push_back(listener);
    }

    listener->retain();
}

這個方法判斷了當前 是否在分發消息,若是沒有分發消息那麼就調用  forceAddEventListener 把偵聽器加入到偵聽器列表裏面。

若是_indispatch不爲0證實如今正在分發消息那麼新加入的偵聽器就放到了臨時數組_toAddedListeners裏面

無論管理器是否是在分發消息listener都有一個歸宿,那麼最後增長了listener一次引用計數。

下面咱們看一下forceAddEventListener方法。

void EventDispatcher::forceAddEventListener(EventListener* listener)
{
    EventListenerVector* listeners = nullptr;
    EventListener::ListenerID listenerID = listener->getListenerID();
    auto itr = _listenerMap.find(listenerID);
    if (itr == _listenerMap.end())
    {
        
        listeners = new EventListenerVector();
        _listenerMap.insert(std::make_pair(listenerID, listeners));
    }
    else
    {
        listeners = itr->second;
    }
    
    listeners->push_back(listener);
    
    if (listener->getFixedPriority() == 0)
    {
        setDirty(listenerID, DirtyFlag::SCENE_GRAPH_PRIORITY);
        
        auto node = listener->getAssociatedNode();
        CCASSERT(node != nullptr, "Invalid scene graph priority!");
        
        associateNodeAndEventListener(node, listener);
        
        if (node->isRunning())
        {
            resumeEventListenersForTarget(node);
        }
    }
    else
    {
        setDirty(listenerID, DirtyFlag::FIXED_PRIORITY);
    }
}

這個類裏面涉及到了一個EventDispatcher的內部類 EventListenerVector 這樣一個數據結構,

這個結構在這裏很少分析了,很簡單,這個結構裏封裝了兩個數組,_fixedListeners 與_sceneGraphListeners ,分別保存優先級不爲0的偵聽器指針與優先級爲0的偵聽器指針。

咱們看一下強制將一個偵聽器加入到管理列表的過程

  1. _listenerMap是按照偵聽器ID來作分類的,每一個偵聽器ID都有一個EventListenerVector 數組。在_listenerMap中找 listenerID與要加入的listener相同的偵聽器列表
  2. 若是沒找到就他那天個listenerID爲listener->getListenerID();項加入到_listenerMap中。找到了就拿到這個ID的列表指針。
  3. 將要加入管理的偵聽器放到列表中。
  4. 根據加入的偵聽器的優先級別是否是0進行設置髒標記操做。
  5. 當優先級標記爲0時確定這個偵聽器是與場景顯示對象對象綁定的,找到這個綁定的Node對象與listener作了關聯,調用了associateNodeAndEventListener方法,將結點與偵聽器加入到了_nodeListenersMap列表裏面。
  6. 由於偵聽器有了增長,因此原偵聽器列表就不是最新的了,cocos2d-x認爲那就是髒數據,這樣設置了關於這個偵聽器ID的髒標記。

經過上述分析,咱們能夠進一步理解到EventDispatcher類內的幾個偵聽器列表變量的做用。

_listenerMap 用以偵聽器類型(就是偵聽器的ID)索引,值是一個數組,用來儲存偵聽同一偵聽器ID的全部偵聽器對象。

_priorityDirtyFlagMap 用來標記一類ID的偵聽器列表是對象是否有變化,偵是偵聽器ID,值爲偵聽級別。

_nodeListenersMap 用來記錄結點類型數據的偵聽器列表,通俗點說就是以結點爲索引全部偵聽的事件都存在這個map裏面。

咱們注意這裏判斷了node->isRunning()屬性若是結點是在運行的結點,那麼調用了resumeEventListenersForTarget方法。下面看下這個方法都作了些什麼。

void EventDispatcher::resumeEventListenersForTarget(Node* target, bool recursive/* = false */)
{
    auto listenerIter = _nodeListenersMap.find(target);
    if (listenerIter != _nodeListenersMap.end())
    {
        auto listeners = listenerIter->second;
        for (auto& l : *listeners)
        {
            l->setPaused(false);
        }
    }
    setDirtyForNode(target);
    
    if (recursive)
    {
        const auto& children = target->getChildren();
        for (const auto& child : children)
        {
            resumeEventListenersForTarget(child, true);
        }
    }
}

這個函數兩個參數,第一個是目標結點對象,第二個參數是是否遞歸進行子對象調用。

這個函數過程,先在結點列表中找是否已經有這個結點了,找到以後將它的每一個偵聽器的暫停狀態都 取消。而後設置這個結點爲髒結點標記。

上面提到過設置髒的偵聽器,這裏看一下設置髒結點函數。

void EventDispatcher::setDirtyForNode(Node* node)
{
    // Mark the node dirty only when there is an eventlistener associated with it. 
    if (_nodeListenersMap.find(node) != _nodeListenersMap.end())
    {
        _dirtyNodes.insert(node);
    }

    // Also set the dirty flag for node's children
    const auto& children = node->getChildren();
    for (const auto& child : children)
    {
        setDirtyForNode(child);
    }
}

這個函數雖然沒有遞歸參數來控制,但從實現 上來分析這經會遞歸 node結點的子結點,都設置成了髒結點。

這裏出現了_dirtyNodes這個類成員變量,如今能夠理解什麼是髒結點了,就是偵聽器有變化的結點。

 

下面咱們分析EventDispatcher類的另外一個加入偵聽器的方法。

 

/** Adds a event listener for a specified event with the fixed priority.
     *  @param listener The listener of a specified event.
     *  @param fixedPriority The fixed priority of the listener.
     *  @note A lower priority will be called before the ones that have a higher value.
     *        0 priority is forbidden for fixed priority since it's used for scene graph based priority.
     */
    void addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority);

從註釋咱們先來一個總體的瞭解。

這個函數的做用是將一個指定優先級的偵聽器加入到管理列表裏面。

這裏強調了,0這個優先級不能被使用,由於這是顯示對象偵聽器優先級別。若是小於0的優先級那麼這個偵聽器事件會在畫面渲染以前被觸發,大於0的優先級會在顯示對象渲染以後觸發事件回調。

好了,下面看實現過程。

void EventDispatcher::addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority)
{
    CCASSERT(listener, "Invalid parameters.");
    CCASSERT(!listener->isRegistered(), "The listener has been registered.");
    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);
}

與addEventListenerWithSceneGraphPriority方法大同小異,就是對listener進行了一些參數賦值,後面仍是調用的addEventListener方法。這裏就很少說了,值得注意的一點是,這個listener初始也是設置成暫停的,上面分析到在addEventListener調用後會將暫停狀態取消的。

 

繼續向下看,還有一個自定義事件的偵聽器註冊方法。

/** Adds a Custom event listener.
     It will use a fixed priority of 1.
     @return the generated event. Needed in order to remove the event from the dispather
     */
    EventListenerCustom* addCustomEventListener(const std::string &eventName, const std::function<void(EventCustom*)>& callback);

這個用戶自定義事件的偵聽器註冊方法。參數爲一個事件名稱與一個回調函數。

從註釋裏面能夠了解這個偵聽器的優先級會被設置成1與就是在場景渲染以後事件才被處理。

EventListenerCustom* EventDispatcher::addCustomEventListener(const std::string &eventName, const std::function<void(EventCustom*)>& callback)
{
    EventListenerCustom *listener = EventListenerCustom::create(eventName, callback);
    addEventListenerWithFixedPriority(listener, 1);
    return listener;
}

這個函數返回了一個EventListenerCustom對象,而且經過實現過程能夠看出這個listener返回的對象已經被註冊到了EventDispatcher管理列表裏面。

看過了註冊偵聽器的方法,如今咱們集中看一下注銷偵聽器的幾個重載方法。

void EventDispatcher::removeEventListener(EventListener* listener) // 註銷指定的偵聽器
{
    if (listener == nullptr)
        return;

    bool isFound = false;
    
    auto removeListenerInVector = [&](std::vector<EventListener*>* listeners){// 這裏定義了一個匿名函數,做用是遍歷一個listener的數組查找是否含有指定的listener,將其從數組中刪除,釋放引用等操做。
        if (listeners == nullptr)
            return;
        
        for (auto iter = listeners->begin(); iter != listeners->end(); ++iter)
        {
            auto l = *iter;
            if (l == listener)
            {
                CC_SAFE_RETAIN(l);
                l->setRegistered(false);
                if (l->getAssociatedNode() != nullptr)
                {
                    dissociateNodeAndEventListener(l->getAssociatedNode(), l);
                    l->setAssociatedNode(nullptr);  // NULL out the node pointer so we don't have any dangling pointers to destroyed nodes.
                }
                
                if (_inDispatch == 0)
                {
                    listeners->erase(iter);
                    CC_SAFE_RELEASE(l);
                }
                
                isFound = true;
                break;
            }
        }
    };
    
    for (auto iter = _listenerMap.begin(); iter != _listenerMap.end();)// 遍歷_listenerMap 按偵聽器ID分類,一類一類遍歷
    {
        auto listeners = iter->second;
        auto fixedPriorityListeners = listeners->getFixedPriorityListeners();
        auto sceneGraphPriorityListeners = listeners->getSceneGraphPriorityListeners();

        removeListenerInVector(sceneGraphPriorityListeners); // 查找優先級爲0的列表 
        if (isFound)
        {
            // fixed #4160: Dirty flag need to be updated after listeners were removed.
            setDirty(listener->getListenerID(), DirtyFlag::SCENE_GRAPH_PRIORITY); // 若是找到了,那麼偵聽器對象有變化,設置髒標記。
        }
        else
        {
            removeListenerInVector(fixedPriorityListeners);// 查找優先級不爲0的列表  
            if (isFound)
            {
                setDirty(listener->getListenerID(), DirtyFlag::FIXED_PRIORITY);
            }
        }
        
#if CC_NODE_DEBUG_VERIFY_EVENT_LISTENERS
        CCASSERT(_inDispatch != 0 ||
                 !sceneGraphPriorityListeners ||
                 std::count(sceneGraphPriorityListeners->begin(), sceneGraphPriorityListeners->end(), listener) == 0,
                 "Listener should be in no lists after this is done if we're not currently in dispatch mode.");
            
        CCASSERT(_inDispatch != 0 ||
                 !fixedPriorityListeners ||
                 std::count(fixedPriorityListeners->begin(), fixedPriorityListeners->end(), listener) == 0,
                 "Listener should be in no lists after this is done if we're not currently in dispatch mode.");
#endif

        if (iter->second->empty())// 若是列表已經沒有其它偵聽器那麼刪除這個分類
        {
            _priorityDirtyFlagMap.erase(listener->getListenerID());
            auto list = iter->second;
            iter = _listenerMap.erase(iter);
            CC_SAFE_DELETE(list);
        }
        else
        {
            ++iter;
        }
        
        if (isFound)
            break;
    }

    if (isFound)// 找到了那麼安全刪除這個偵聽器
    {
        CC_SAFE_RELEASE(listener);
    }
    else// 若是上面過程都沒找到,那麼在將要註冊的偵聽器列表裏面再找一遍,若是存在那麼將其刪除。
    {
        for(auto iter = _toAddedListeners.begin(); iter != _toAddedListeners.end(); ++iter)
        {
            if (*iter == listener)
            {
                listener->setRegistered(false);
                listener->release();
                _toAddedListeners.erase(iter);
                break;
            }
        }
    }
}

下面咱們看一下其它重載版本的註銷偵聽器的函數。

/** 根據偵聽器的大類型來刪除偵聽器 */
    void removeEventListenersForType(EventListener::Type listenerType);

    /** 根據Node結點來刪除偵聽器. */
    void removeEventListenersForTarget(Node* target, bool recursive = false);
    
    /** 根據指定事件名稱來刪除偵聽器 */
    void removeCustomEventListeners(const std::string& customEventName);

    /** 清除全部偵聽器 */
    void removeAllEventListeners();

這些方法我就不一個一個分析了,由於過程都與第一個版本的類似,就是查找,刪除,釋放引用 這幾個操做。你們能夠自行看一下代碼。

再看一下偵聽器偵聽的暫停與恢復方法。

/** Pauses all listeners which are associated the specified target. */
    void pauseEventListenersForTarget(Node* target, bool recursive = false);
    
    /** Resumes all listeners which are associated the specified target. */
    void resumeEventListenersForTarget(Node* target, bool recursive = false);

這兩個方法也不用多說,就是設置了暫停屬性。第二個參數是用來指定是否遞歸做用於子結點的。

接下來咱們看一下 事件分發的方法,

/** Dispatches the event
 *  Also removes all EventListeners marked for deletion from the
 *  event dispatcher list.
 */
void dispatchEvent(Event* event);


從註釋上初步瞭解這個函數是分發消息而且會註銷那些被標記爲要刪除的偵聽器。

下面咱們看實現。

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);
    
    sortEventListeners(listenerID);
    
    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();
        };
        
        dispatchEventToListeners(listeners, onEvent);
    }
    
    updateListeners(event);
}

這個函數的流程爲:

  1. 調用 updateDirtyFlagForSceneGraph 這個函數咱們後面再分析實現,在這裏從命名上能夠知道這塊處理了那些髒標記。
  2. 將TOUCH事件單獨進行了處理,也就將Touch事件調用了dispatchTouchEvent這個方法。這個方法後面咱們也單獨分析。
  3. 把全部偵聽當前傳入的Event事件ID的偵聽器進行了排序。sortEventListeners方法。
  4. 針對每一個偵聽當前事件的偵聽器進行分發,使用了dispatchEventToListeners方法。
  5. 調用updateListeners 這個方法也後面分析。

其實這個dispatchEvent只是作了一個分揀操做,並無直接去執行偵聽器的回調方法。

上面過程當中提到了幾個重要的方法,咱們下面一個一個分析。

updateDirtyFlagForSceneGraph

void EventDispatcher::updateDirtyFlagForSceneGraph()
{
    if (!_dirtyNodes.empty())
    {
        for (auto& node : _dirtyNodes)
        {
            auto iter = _nodeListenersMap.find(node);
            if (iter != _nodeListenersMap.end())
            {
                for (auto& l : *iter->second)
                {
                    setDirty(l->getListenerID(), DirtyFlag::SCENE_GRAPH_PRIORITY);
                }
            }
        }
        
        _dirtyNodes.clear();
    }
}

這個方法就是遍歷了_dirtyNodes列表,看看有沒有髒結點,一個一個的結點去設置新的事件優先級。最後將髒結點從_dirtyNodes裏面刪除。

sortEventListeners

void EventDispatcher::sortEventListeners(const EventListener::ListenerID& listenerID)
{
    DirtyFlag dirtyFlag = DirtyFlag::NONE;
    
    auto dirtyIter = _priorityDirtyFlagMap.find(listenerID);
    if (dirtyIter != _priorityDirtyFlagMap.end())
    {
        dirtyFlag = dirtyIter->second;
    }
    
    if (dirtyFlag != DirtyFlag::NONE)
    {
        // Clear the dirty flag first, if `rootNode` is nullptr, then set its dirty flag of scene graph priority
        dirtyIter->second = DirtyFlag::NONE;

        if ((int)dirtyFlag & (int)DirtyFlag::FIXED_PRIORITY)
        {
            sortEventListenersOfFixedPriority(listenerID);
        }
        
        if ((int)dirtyFlag & (int)DirtyFlag::SCENE_GRAPH_PRIORITY)
        {
            auto rootNode = Director::getInstance()->getRunningScene();
            if (rootNode)
            {
                sortEventListenersOfSceneGraphPriority(listenerID, rootNode);
            }
            else
            {
                dirtyIter->second = DirtyFlag::SCENE_GRAPH_PRIORITY;
            }
        }
    }
}

這個方法做用是根據指定的事件ID來對結點進行排序。

函數過程爲:

  1. 在髒列表裏面找這個listenerID
  2. 若是髒列表裏有這個事件ID那麼才進行排序,這裏在髒列表裏面找有一個優化,若是髒列表裏面沒有,那麼證實這類開事件沒有變化,那麼就不用排序,由於上次已經排列過順序了。這塊這麼處理是按需來排序。很巧妙。
  3. 下面根據優先級權限來分別調用了sortEventListenersOfFixedPriority與sortEventListenersOfSceneGraphPriority兩個方法。
  4. 這裏要注意一點,在渲染對象中間傳遞事件其實是以當前運行的場景爲根結點來進行排序的。

咱們再看下這兩個方法。

sortEventListenersOfFixedPriority

void EventDispatcher::sortEventListenersOfFixedPriority(const EventListener::ListenerID& listenerID)
{
    auto listeners = getListeners(listenerID);

    if (listeners == nullptr)
        return;
    
    auto fixedListeners = listeners->getFixedPriorityListeners();
    if (fixedListeners == nullptr)
        return;
    
    // 根據優先級排順序
    std::sort(fixedListeners->begin(), fixedListeners->end(), [](const EventListener* l1, const EventListener* l2) {
        return l1->getFixedPriority() < l2->getFixedPriority();
    });
    
    // 由於根據優先級排列順序爲 <0   >0 下面這塊是找到第一個大於0優先級的索引。也就是分界點,優先級小於0的偵聽器在場景渲染以前觸發,大於0的偵聽器在場景渲染以後觸發。因此這個值頗有用。
    int index = 0;
    for (auto& listener : *fixedListeners)
    {
        if (listener->getFixedPriority() >= 0)
            break;
        ++index;
    }
    
    listeners->setGt0Index(index);
    
#if DUMP_LISTENER_ITEM_PRIORITY_INFO
    log("-----------------------------------");
    for (auto& l : *fixedListeners)
    {
        log("listener priority: node (%p), fixed (%d)", l->_node, l->_fixedPriority);
    }    
#endif
    
}

 

sortEventListenersOfSceneGraphPriority

void EventDispatcher::sortEventListenersOfSceneGraphPriority(const EventListener::ListenerID& listenerID, Node* rootNode)
{
    auto listeners = getListeners(listenerID);
    
    if (listeners == nullptr)
        return;
    auto sceneGraphListeners = listeners->getSceneGraphPriorityListeners();
    
    if (sceneGraphListeners == nullptr)
        return;

    // Reset priority index
    _nodePriorityIndex = 0;
    _nodePriorityMap.clear();

    visitTarget(rootNode, true);
    
    // After sort: priority < 0, > 0
    std::sort(sceneGraphListeners->begin(), sceneGraphListeners->end(), [this](const EventListener* l1, const EventListener* l2) {
        return _nodePriorityMap[l1->getAssociatedNode()] > _nodePriorityMap[l2->getAssociatedNode()];
    });
    
#if DUMP_LISTENER_ITEM_PRIORITY_INFO
    log("-----------------------------------");
    for (auto& l : *sceneGraphListeners)
    {
        log("listener priority: node ([%s]%p), priority (%d)", typeid(*l->_node).name(), l->_node, _nodePriorityMap[l->_node]);
    }
#endif
}

這個場景渲染對象事件的優先級排列與上面一個函數過程相似,很好理解。很少說了。

dispatchEventToListeners 

void EventDispatcher::dispatchEventToListeners(EventListenerVector* listeners, const std::function<bool(EventListener*)>& onEvent)
{
    bool shouldStopPropagation = false;
    auto fixedPriorityListeners = listeners->getFixedPriorityListeners();
    auto sceneGraphPriorityListeners = listeners->getSceneGraphPriorityListeners();
    
    ssize_t i = 0;
    // 先處理 priority < 0 的事件偵聽器
    if (fixedPriorityListeners)
    {
        CCASSERT(listeners->getGt0Index() <= static_cast<ssize_t>(fixedPriorityListeners->size()), "Out of range exception!");
        
        if (!fixedPriorityListeners->empty())
        {
            for (; i < listeners->getGt0Index(); ++i)
            {
                auto l = fixedPriorityListeners->at(i);
                if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l))// 這裏判斷事件是否是須要傳遞下去,前面是檢測偵聽器的狀態後面onEvent返回l是否中止的屬性。它是dispatcherEvent裏一個匿名函數 .
                {
                    shouldStopPropagation = true;
                    break;
                }
            }
        }
    }
 // 處理 priority == 0 的事件偵聽器     
    if (sceneGraphPriorityListeners)
    {
        if (!shouldStopPropagation)
        {
            // priority == 0, scene graph priority
            for (auto& l : *sceneGraphPriorityListeners)
            {
                if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l))
                {
                    shouldStopPropagation = true;
                    break;
                }
            }
        }
    }
    // 處理 priority < 0 的事件偵聽器 
    if (fixedPriorityListeners)
    {
        if (!shouldStopPropagation)
        {
            // priority > 0
            ssize_t size = fixedPriorityListeners->size();
            for (; i < size; ++i)
            {
                auto l = fixedPriorityListeners->at(i);
                
                if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l))
                {
                    shouldStopPropagation = true;
                    break;
                }
            }
        }
    }
}

dispatchTouchEvent

這個函數你們能夠本身看一下,這裏不詳細分析了,基本過程與dispatchEventToListeners 差很少 區別在於它區分了onebyone及all by once的處理方式。

觸摸事件後繼章節咱們會單獨分析。

 

updateListeners

void EventDispatcher::updateListeners(Event* event)
{
    CCASSERT(_inDispatch > 0, "If program goes here, there should be event in dispatch.");
    
    auto onUpdateListeners = [this](const EventListener::ListenerID& listenerID) // 這裏定義了一個匿名函數,做用是清理_listenerMap 裏的偵聽器,無效的都會進行註銷清除操做。
    {
        auto listenersIter = _listenerMap.find(listenerID);
        if (listenersIter == _listenerMap.end())
            return;

        auto listeners = listenersIter->second;
        
        auto fixedPriorityListeners = listeners->getFixedPriorityListeners();
        auto sceneGraphPriorityListeners = listeners->getSceneGraphPriorityListeners();
        
        if (sceneGraphPriorityListeners)
        {
            for (auto iter = sceneGraphPriorityListeners->begin(); iter != sceneGraphPriorityListeners->end();)
            {
                auto l = *iter;
                if (!l->isRegistered())
                {
                    iter = sceneGraphPriorityListeners->erase(iter);
                    l->release();
                }
                else
                {
                    ++iter;
                }
            }
        }
        
        if (fixedPriorityListeners)
        {
            for (auto iter = fixedPriorityListeners->begin(); iter != fixedPriorityListeners->end();)
            {
                auto l = *iter;
                if (!l->isRegistered())
                {
                    iter = fixedPriorityListeners->erase(iter);
                    l->release();
                }
                else
                {
                    ++iter;
                }
            }
        }
        
        if (sceneGraphPriorityListeners && sceneGraphPriorityListeners->empty())
        {
            listeners->clearSceneGraphListeners();
        }

        if (fixedPriorityListeners && fixedPriorityListeners->empty())
        {
            listeners->clearFixedListeners();
        }
    };// 匿名函數結束。

    
    if (event->getType() == Event::Type::TOUCH)// 調用匿名函數清除TOUCH類的無效偵聽器
    {
        onUpdateListeners(EventListenerTouchOneByOne::LISTENER_ID);
        onUpdateListeners(EventListenerTouchAllAtOnce::LISTENER_ID);
    }
    else// 調用匿名函數清除非TOUCH類型的無效偵聽器
    {
        onUpdateListeners(__getListenerID(event));
    }
    
    if (_inDispatch > 1)
        return;
    
    CCASSERT(_inDispatch == 1, "_inDispatch should be 1 here.");
    
    for (auto iter = _listenerMap.begin(); iter != _listenerMap.end();)// 清理listenerMap裏的空項目。
    {
        if (iter->second->empty())
        {
            _priorityDirtyFlagMap.erase(iter->first);
            delete iter->second;
            iter = _listenerMap.erase(iter);
        }
        else
        {
            ++iter;
        }
    }
    
    if (!_toAddedListeners.empty())//清理_toAddedListeners裏的空項目
    {
        for (auto& listener : _toAddedListeners)
        {
            forceAddEventListener(listener);
        }
        _toAddedListeners.clear();
    }
}

至此消息分發過程咱們分析完了。

下面看一下用戶息定義的消息是怎麼分發的

dispatchCustomEvent

void EventDispatcher::dispatchCustomEvent(const std::string &eventName, void *optionalUserData)
{
    EventCustom ev(eventName);
    ev.setUserData(optionalUserData);
    dispatchEvent(&ev);
}

參數爲一個事件名稱和一個用戶自定義的數據指針,這裏面建立了個文化EventCustom對象,而後調用了dispatchEvent(&ev);以後分發的過程與上面的同樣了。

 

好啊,今天又囉嗦這麼多,主要分析了三個東東,Cocos2d-x中的  事件、 偵聽器、事件分發器

有經驗的同窗能夠看出,其實這裏用到了一個經常使用的設計模式就是觀察者模式,採用了註冊事件,觸發採用回調函數來執行事件過程。

小魚在這裏總結一下:

  1. Cocos2d-x的事件有幾種類型,觸摸(TOUCH)、鍵盤(KEYBOARD)、重力器(ACCELERATION)、鼠標(MOUSE)、用戶自定義類型(CUSTOM)
  2. 偵聽器也有幾種 TOUCH_ONE_BY_ONE, TOUCH_ALL_AT_ONCE, KEYBOARD, MOUSE, ACCELERATION, CUSTOM
  3. 分發器將事件對象傳遞給在分發器裏註冊的事件偵聽對象,根據事件類型作匹配,匹配到合適的偵聽器後就算事件觸發了,調用 偵聽器的回調函數來執行事件過程。
  4. Cocos2d-x引擎中有一個分發器對象,就是在Direct類中的_eventDispatcher這個變量,在建立Direct對象時進行的初始化。

 

今天Cocos2d-x的事件分發機制源碼咱們分析到這裏,在event_dispatch目錄裏還有一些關於事件的類咱們就不作具體分析了,大同小異,若是理解上面的內容自行閱讀那部分源碼是沒問題的。

下一章 咱們來閱讀Cocos2d-x3.0有關場景Scene類的源碼。

相關文章
相關標籤/搜索