Cocos2d-x 3.x事件机制源码分析

整个事件机制,由以下几个部分构成:
  • 事件 : 描述事件。

  • 监听器 : 描述事件接收者与事件的“兴趣关系”。

  • 分发器 : 针对所产生的事件,将事件分发给符合事件类型的事件接收者。

整个事件机制采用观察者模式,GL视图负责感应事件的产生,并包装事件,然后主动调用分发器的dispatchEvent函数用于分发。GL视图是观察者,图层或者说场景是被观察者。GL视图观察到事件,然后通过事件分发器通知被观察者。监听器,这么称呼有点名不副实,它并不是用于监听事件,而是作为一个桥梁为事件接收者(场景、图层、精灵等等)和事件分发者之间建立联系,当一个图层(或者精灵)关联到某一类型的监听器,则表明该图层(或精灵)对此类事件感兴趣,而监听器则是描述了图层(或精灵)与事件之间的这种“兴趣关系”,将监听器添加到分发器中,就是将这种“兴趣关系”告诉分发器,以便分发器能更好更合理的分发事件。

1、事件

事件,定义了事件的类型,如触摸、鼠标、键盘等等事件类型。每个事件类型都继承自Event。Event类封装了事件的一些共有属性。

class CC_DLL Event : public Ref
{
public:
       //事件的类型主要有:触摸、键盘、加速计、鼠标、游戏手柄
    enum class Type
    {
        TOUCH,
        KEYBOARD,
        ACCELERATION,
        MOUSE,
        FOCUS,
        GAME_CONTROLLER,
        CUSTOM
    };

CC_CONSTRUCTOR_ACCESS:
        Event(Type type);
public:

    virtual ~Event();

    inline Type getType() const { return _type; };

    //停止事件的传播
    inline void stopPropagation() { _isStopped = true; };

    //检测当前事件是否停止了传播
    inline bool isStopped() const { return _isStopped; };

    //获得触发当前事件的对象
    inline Node* getCurrentTarget() { return _currentTarget; };

protected:
    inline void setCurrentTarget(Node* target) { _currentTarget = target; };

    Type _type;     ///< Event type

    bool _isStopped;       ///< whether the event has been stopped.
    Node* _currentTarget;  ///< Current target

    //EventDispatcher为友元类,EventDispatcher可以使用Event的方法
    friend class EventDispatcher;
};

以下以触摸事件为例:

触摸事件

Touch类封装了所产生的触摸事件的信息,触摸事件通过_id来区别。EventTouch管理触摸事件,封装了触摸事件的属性。
class CC_DLL EventTouch : public Event
{
public:
    static const int MAX_TOUCHES = 15;

    /** EventCode Touch event code.*/
    //触摸过程:开始、移动、末尾、不触摸
    enum class EventCode
    {
        BEGAN,
        MOVED,
        ENDED,
        CANCELLED
    };

    EventTouch();

    inline EventCode getEventCode() const { return _eventCode; };

    inline const std::vector<Touch*>& getTouches() const { return _touches; };
#if TOUCH_PERF_DEBUG

    void setEventCode(EventCode eventCode) { _eventCode = eventCode; };

    void setTouches(const std::vector<Touch*>& touches) { _touches = touches; };
#endif

private:
    EventCode _eventCode;
    //Touch类封装了 触摸事件的一些信息:触摸点、增量等等信息
    std::vector<Touch*> _touches;
    //GLView为友元类,GLView可以使用EventTouch的方法
    friend class GLView;
};
class CC_DLL Touch : public Ref
{
public:
    enum class DispatchMode {
        ALL_AT_ONCE, /** All at once. */
        ONE_BY_ONE,  /** One by one. */
    };

    Touch() 
        : _id(0),
        _startPointCaptured(false)
    {}

    //获得当前触摸点的坐标
    Vec2 getLocation() const;

    //获得前一个触摸点的坐标
    Vec2 getPreviousLocation() const;

    //获得开始触摸时的坐标
    Vec2 getStartLocation() const;

    //获得前后两个触摸点之间增量
    Vec2 getDelta() const;

    Vec2 getLocationInView() const;

    Vec2 getPreviousLocationInView() const;

    Vec2 getStartLocationInView() const;

    //设置触摸点信息
    void setTouchInfo(int id, float x, float y)
    {
        _id = id;
        _prevPoint = _point;
        _point.x   = x;
        _point.y   = y;
        if (!_startPointCaptured)
        {
            _startPoint = _point;
            _startPointCaptured = true;
            _prevPoint = _point;
        }
    }

    int getID() const
    {
        return _id;
    }
private:
    int _id;
    bool _startPointCaptured;
    Vec2 _startPoint;
    Vec2 _point;
    Vec2 _prevPoint;
};

2、事件监听器

由于事件分为多种,有触摸事件、鼠标事件等等。因此,cocos2d中也定义了多种事件监听器。但是所有的事件监听器都派生自EventListener。监听器负责为事件接收者 与 事件分发者 之间建立联系。
class CC_DLL EventListener : public Ref
{
public:
    /** Type Event type.*/
    enum class Type
    {
        UNKNOWN,
        TOUCH_ONE_BY_ONE,
        TOUCH_ALL_AT_ONCE,
        KEYBOARD,
        MOUSE,
        ACCELERATION,
        FOCUS,
        GAME_CONTROLLER,
        CUSTOM
    };
    typedef std::string ListenerID;
CC_CONSTRUCTOR_ACCESS:

    EventListener();

    bool init(Type t, const ListenerID& listenerID, const std::function<void(Event*)>& callback);
public:

    virtual ~EventListener();

    virtual bool checkAvailable() = 0;

    virtual EventListener* clone() = 0;

    //设置本监听器是否接受事件:true,接收;false,不接受
    inline void setEnabled(bool enabled) { _isEnabled = enabled; };

    inline bool isEnabled() const { return _isEnabled; };
protected:

    inline void setPaused(bool paused) { _paused = paused; };
    inline bool isPaused() const { return _paused; };
   inline void setRegistered(bool registered) { _isRegistered = registered; };

    //判断本监听器是否注册到分发器中
    inline bool isRegistered() const { return _isRegistered; };

    //获得当前事件类型
    inline Type getType() const { return _type; };

    inline const ListenerID& getListenerID() const { return _listenerID; };

    inline void setFixedPriority(int fixedPriority) { _fixedPriority = fixedPriority; };

    inline int getFixedPriority() const { return _fixedPriority; };

    //获得被本监听器 所 监听的所有对象
    inline Node* getAssociatedNode() const { return _node; };

    std::function<void(Event*)> _onEvent;   /// Event callback function
    Type _type;                             /// Event listener type
    ListenerID _listenerID;                 /// Event listener ID
    bool _isRegistered;                     /// Whether the listener has been added to dispatcher.
    int   _fixedPriority;   // The higher the number, the higher the priority, 0 is for scene graph base priority.
    Node* _node;            // scene graph based priority
    bool _paused;           // Whether the listener is paused
    bool _isEnabled;        // Whether the listener is enabled
    friend class EventDispatcher;
};

触摸事件监听器

单点触摸事件监听器。
class CC_DLL EventListenerTouchOneByOne : public EventListener
{
public:
    static const std::string LISTENER_ID;

    /** Create a one by one touch event listener. */
    static EventListenerTouchOneByOne* create();

    /** * Destructor. * @js NA */
    virtual ~EventListenerTouchOneByOne();

    /** Whether or not to swall touches. * * @param needSwallow True if needs to swall touches. */
    //设置是否向下传递触摸,若设置为true,则不向下传递触摸
    void setSwallowTouches(bool needSwallow);
    /** Is swall touches or not. * * @return True if needs to swall touches. */
    bool isSwallowTouches();

    /// Overrides
    virtual EventListenerTouchOneByOne* clone() override;
    virtual bool checkAvailable() override;
public:
    typedef std::function<bool(Touch*, Event*)> ccTouchBeganCallback;
    typedef std::function<void(Touch*, Event*)> ccTouchCallback;
    ccTouchBeganCallback onTouchBegan;
    ccTouchCallback onTouchMoved;
    ccTouchCallback onTouchEnded;
    ccTouchCallback onTouchCancelled;

CC_CONSTRUCTOR_ACCESS:
    EventListenerTouchOneByOne();
    bool init();

private:
    std::vector<Touch*> _claimedTouches;
    bool _needSwallow;

    friend class EventDispatcher;
};

3、事件分发器

- 事件分发器维护了一个队列,这个队列是各种监听器的集合:

- 这个类管理事件监听器的订阅和事件的分发
- 事件监听器列表以这样的方式来进行管理:当事件正在分发的过程中,事件监听器可以被添加或者移除,包括事件监听器内部的监听器


   以下这段代码
{
public:
    // Adds event listener.

    /** 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'.
     */
    //为一个特定的事件添加一个事件监听器,该监听器的优先级是基于Node节点的绘图顺序。
    void addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node);

    //监听器的优先级由fixedPriority给出
    void addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority);

    EventListenerCustom* addCustomEventListener(const std::string &eventName, const std::function<void(EventCustom*)>& callback);

    // 移除 某个事件监听器
    void removeEventListener(EventListener* listener);

    //移除 特定类型的所有事件监听器
    void removeEventListenersForType(EventListener::Type listenerType);
    /** Removes all listeners which are associated with the specified target. * * @param target A given target node. * @param recursive True if remove recursively, the default value is false. */
    //移除 与target关联的所有事件监听器
    void removeEventListenersForTarget(Node* target, bool recursive = false);

    /** Removes all custom listeners with the same event name. * * @param customEventName A given event listener name which needs to be removed. */
    void removeCustomEventListeners(const std::string& customEventName);
    /** Removes all listeners. */
    //移除所有的事件监听器
    void removeAllEventListeners();
    /////////////////////////////////////////////

    // Pauses / Resumes event listener

    /** Pauses all listeners which are associated the specified target. * * @param target A given target node. * @param recursive True if pause recursively, the default value is false. */
    void pauseEventListenersForTarget(Node* target, bool recursive = false);

    /** Resumes all listeners which are associated the specified target. * * @param target A given target node. * @param recursive True if resume recursively, the default value is false. */
    void resumeEventListenersForTarget(Node* target, bool recursive = false);

    /////////////////////////////////////////////

    /** Sets listener's priority with fixed value. * * @param listener A given listener. * @param fixedPriority The fixed priority value. */
    void setPriority(EventListener* listener, int fixedPriority);
    /** Whether to enable dispatching events. * * @param isEnabled True if enable dispatching events. */
    void setEnabled(bool isEnabled);
    /** Checks whether dispatching events is enabled. * * @return True if dispatching events is enabled. */
    bool isEnabled() const;
    /////////////////////////////////////////////

    /** Dispatches the event. * Also removes all EventListeners marked for deletion from the * event dispatcher list. * * @param event The event needs to be dispatched. */
    //分发事件:主要接口(内部通过判别event的类型,来调用不同的分发函数)
    void dispatchEvent(Event* event);
    //...部分省略
/** Whether the dispatcher isdispatching event */
    int _inDispatch;

    /** Whether to enable dispatching event */
    bool _isEnabled;

    int _nodePriorityIndex;

    std::set<std::string> _internalCustomListenerIDs;
};

小结

GL视图负责感应事件,当有事件作用于GL视图的时候,GL视图将调用分发器EventDispatcher的接口(dispatchEvent)分发事件。而具体分发给谁,分发器将首先根据分发器中所维护的监听器队列来选择,因为可能有多个层希望接收这一类型事件,但是这些层的绘图顺序是不一样的,所以当选定了需要向哪些类型的监听器分发消息后,还将先把消息传递给最上层的图层(即本帧最后绘制的图层)。

你可能感兴趣的:(Cocos2d-x 3.x事件机制源码分析)