【cocos2d-x 源码解析】事件系统

本文深入解析cocos2d-x 3.x的事件系统,包括事件(Event)、事件监听器(EventListener)和事件分发器(EventDispatcher)。事件主要由事件分发器派发,事件监听器接收到事件后执行回调函数处理。事件监听器包含固定优先级和依赖节点的监听器,事件分发器负责管理监听器的注册、移除、暂停和恢复。文章详细介绍了事件的创建、监听器的注册与回调,以及事件分发过程。
摘要由CSDN通过智能技术生成

事件系统

cocos2d-x 3.x 的事件比 2.x 要好用很多,也比较简单,主要由三部分组成:事件分发器 EventDispatcher、事件监听器 EventListener 和事件 Event

cocos2d-x 3.x 所有与事件相关的文件都在 cocos/base 模块下。

事件 Event

事件在现实中的意思是发生了一件事,当事情发生之后会有人去做一些处理;在应用程序中事件可以看作是一个带有数据的实体,而事件监听器就是事情发生之后做一些处理工作的人;派发事件就是将事件这个实体派发出去,告诉大家发生了这个事情,然后事件监听器会监听到事件触发,接收事件这个实体,取到事件中的数据,做相应的处理。

在这里事件可以简单地看成一个数据体,它在整个事件系统中的作用是传送数据,事件派发器将事件派发出去,事件监听器接收到这个数据,然后把事件中的数据取出来,进行下一步操作。事件的基类是 Event,它的定义很简单,只定义了几个属性

class CC_DLL Event : public Ref
{
public:
    /** Type Event type.*/
    enum class Type
    {
        TOUCH,
        KEYBOARD,
        ACCELERATION,
        MOUSE,
        FOCUS,
        GAME_CONTROLLER,
        CUSTOM
    };

public:
    //function
protected:
    Type _type;     ///< Event type
    bool _isStopped;       ///< whether the event has been stopped.
    Node* _currentTarget;  ///< Current target

    friend class EventDispatcher;
};

Event 类只声明了事件类型 _type,事件是否停止 _isStopped,事件附加的目标结点 _currentTarget 这三个属性。其中,事件类型共有七种,分别是 触摸事件、键盘事件、加速计事件、鼠标事件、焦点事件、手柄事件和自定义事件。具体事件类在 Event 的基础上多定义了几个属性而已,比如键盘事件 EventKeyboard 就多定义了两个属性,_keyCode 表示按下的键值,_isPressed 表示是按下还是松开

class CC_DLL EventKeyboard : public Event
{
public:
    //function
protected:
    KeyCode _keyCode;
    bool _isPressed;

    friend class EventListenerKeyboard;
};

事件监听器 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;

public:
    bool init(Type t, const ListenerID& listenerID, const std::function<void(Event*)>& callback);
    virtual bool checkAvailable() = 0;
    //...

protected:
    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;
};
  • _fixedPriority 表示事件的优先级,_node 表示事件依赖的场景结点,这两个属性是对立的,分别对应两种添加事件监听器的方式,一种是事件监听器依赖于场景结点,则需要保存这个结点(_node),这时事件不需要额外定义优先级;另一种是事件监听器注册在全局中,则需要定义事件的优先级(_fixedPriority)
  • _paused 表示当前监听器是否被暂停,_isEnabled 表示监听器是否启用,_isRegistered 表示监听器是否已注册,每个监听器只能注册一次,如果要重复注册,必须使用 clone 方法创建一个新的副本
  • _type 表示事件监听器的类型,这个类型和事件的类型要区分开,事件监听器有自己的一个类型,事件也有自己的类型,这两个类型有对应关系,但它们是两个不同的数据;_listenerID 表示监听器的唯一 ID,这是个字符串,在保存监听器到 map 时用作 key 值,这个 ID 在具体的事件监听器子类中定义
  • _onEvent 是事件回调函数,当事件触发时,这个函数将被调用

EventListener 也比较简单,比较重要的两个函数是 init 方法和 checkAvaliable 方法

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;
}

bool EventListener::checkAvailable()
{ 
    return (_onEvent != nullptr);
}

init 方法初始化几个属性,checkAvaliable 方法检查回调函数是否为空,如果为空,则不能注册监听器,因为没有回调函数,监听器即使监听到事件发生也无法处理事件。这两个方法在基类没看到调用的地方,init 方法在子类初始化的时候会调用,而 checkAvaliable 方法则在注册事件监听器的时候调用。

下面还是以键盘事件为例,看一下具体的事件监听器类做了哪些事件

class CC_DLL EventListenerKeyboard : public EventListener
{
public:
    static const std::string LISTENER_ID;

    /** Create a keyboard event listener.
     * 
     * @return An autoreleased EventListenerKeyboard object.
     */
    static EventListenerKeyboard* create();

    /// Overrides
    virtual EventListenerKeyboard* clone() override;
    virtual bool checkAvailable() override;

    std::function<void(EventKeyboard::KeyCode, Event*)> onKeyPressed;
    std::function<void(EventKeyboard::KeyCode, Event*)> onKeyReleased;
CC_CONSTRUCTOR_ACCESS:
    EventListenerKeyboard();
    bool init();
};

EventListenerKeyboard 新定义了两个子回调事件 onKeyPressed 和 onKeyReleased,这个函数参数是对外公开的,由外部进行赋值;这两个子回调函数将由父类定义的回调函数 _onEvent 调用(看下面的 init 方法)。

EventListenerKeyKeyboard 的 init 方法首先会定义回调函数,然后调用父类的 init 方法,将事件类型、事件 ID 和回调函数传过去,做父类属性初始化。

bool EventListenerKeyboard::init()
{
    auto listener = [this](Event* event){
        auto keyboardEvent = static_cast<EventKeyboard*>(event);
        if (keyboardEvent->_isPressed)
        {
            if (onKeyPressed != nullptr)
                onKeyPressed(keyboardEvent->_keyCode, event);
        }
        else
        {
            if (onKeyReleased != nullptr)
                onKeyReleased(keyboardEvent->_keyCode, event);
        }
    };

    if (EventListener::init(Type::KEYBOARD, LISTENER_ID, listener))
    {
        return true;
    }

    return false;
}

init 方法定义了回调函数 _onEvent,_onEvent 会调用 onKeyPressed 或者 onKeyReleased 方法,所以 checkAvailable 方法不用再检查 _onEvent 是否为空,而是检查这两个子回调函数

bool EventListenerKeyboard::checkAvailable()
{
    if (onKeyPressed == nullptr && onKeyReleased == nullptr)
    {
        CCASSERT(false, "Invalid EventListenerKeyboard!");
        return false;
    }

    return true;
}
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值