天天看點

Cocos2d-x 3.0 新特性體驗-觸摸事件處理機制

在cocos2d-x 2.x版本中,相信大家都抱怨過其中的觸摸機制;在3.0版本中,采用了全新的觸摸事件處理機制。

下面,我将通過引擎中自帶的sample來探索一下這個新的觸摸事件處理機制。

注:例子來自test cpp/neweventdispatchertest

一、例子1

Cocos2d-x 3.0 新特性體驗-觸摸事件處理機制

(1)建立三個精靈

Cocos2d-x 3.0 新特性體驗-觸摸事件處理機制

auto sprite1 = sprite::create("images/cyansquare.png");  

    sprite1->setposition(origin+point(size.width/2, size.height/2) + point(-80, 80));  

    addchild(sprite1, 10); //其中 10 表示 zoreder  

    auto sprite2 = sprite::create("images/magentasquare.png");  

    sprite2->setposition(origin+point(size.width/2, size.height/2));  

    addchild(sprite2, 20);  

    auto sprite3 = sprite::create("images/yellowsquare.png");  

    sprite3->setposition(point(0, 0));  

    sprite2->addchild(sprite3, 1); //注意 sprite3 是添加到 sprite2 上的  

(2)建立一個單點觸摸事件監聽器,處理觸摸事件邏輯

Cocos2d-x 3.0 新特性體驗-觸摸事件處理機制

// make sprite1 touchable  

auto listener1 = eventlistenertouchonebyone::create();//建立一個觸摸監聽  

listener1->setswallowtouches(true); //設定是否想下傳遞觸摸  

//通過 lambda 表達式 直接實作觸摸事件的回掉方法  

listener1->ontouchbegan = [](touch* touch, event* event){  

    auto target = static_cast<sprite*>(event->getcurrenttarget());  

    point locationinnode = target->converttonodespace(touch->getlocation());  

    size s = target->getcontentsize();  

    rect rect = rect(0, 0, s.width, s.height);  

    if (rect.containspoint(locationinnode))  

    {  

        log("sprite began... x = %f, y = %f", locationinnode.x, locationinnode.y);  

        target->setopacity(180);  

        return true;  

    }  

    return false;  

};  

listener1->ontouchmoved = [](touch* touch, event* event){  

    target->setposition(target->getposition() + touch->getdelta());  

listener1->ontouchended = [=](touch* touch, event* event){  

    log("sprite ontouchesended.. ");  

    target->setopacity(255);  

    if (target == sprite2)  

        sprite1->setzorder(100);  

    else if(target == sprite1)  

        sprite1->setzorder(0);  

_eventdispatcher->addeventlistenerwithscenegraphpriority(listener1, sprite1);  

_eventdispatcher->addeventlistenerwithscenegraphpriority(listener1->clone(), sprite2);  

_eventdispatcher->addeventlistenerwithscenegraphpriority(listener1->clone(), sprite3);  

①其中的觸摸監聽類型為:eventlistenertouchonebyone 表示的是單點觸摸;而eventlistenertouchallatonce 表示的就是多點觸摸。

Cocos2d-x 3.0 新特性體驗-觸摸事件處理機制

class eventlistenertouchonebyone : public eventlistener  

{  

public:  

    static const std::string listener_id;  

    static eventlistenertouchonebyone* create();  

    virtual ~eventlistenertouchonebyone();  

    void setswallowtouches(bool needswallow);  

    /// overrides  

    virtual eventlistenertouchonebyone* clone() override;  

    virtual bool checkavailable() override;  

    //  

    std::function<bool(touch*, event*)> ontouchbegan;  

    std::function<void(touch*, event*)> ontouchmoved;  

    std::function<void(touch*, event*)> ontouchended;  

    std::function<void(touch*, event*)> ontouchcancelled;  

private:  

    eventlistenertouchonebyone();  

    bool init();  

    std::vector<touch*> _claimedtouches;  

    bool _needswallow;  

    friend class eventdispatcher;  

class eventlistenertouchallatonce : public eventlistener  

    static eventlistenertouchallatonce* create();  

    virtual ~eventlistenertouchallatonce();  

    virtual eventlistenertouchallatonce* clone() override;  

    std::function<void(const std::vector<touch*>&, event*)> ontouchesbegan;  

    std::function<void(const std::vector<touch*>&, event*)> ontouchesmoved;  

    std::function<void(const std::vector<touch*>&, event*)> ontouchesended;  

    std::function<void(const std::vector<touch*>&, event*)> ontouchescancelled;  

    eventlistenertouchallatonce();  

看起來很是熟悉吧,和cocos2dx 2.x 版本中的 target touch 和 standard touch 差不多吧!隻是使用的形式不太一樣罷了。還有在3.0版本中,不需要注冊觸摸事件代理delegate了。

② _eventdispatcher

事件監聽器包含以下幾種:

觸摸事件 (eventlistenertouch)

鍵盤響應事件 (eventlistenerkeyboard)

加速記錄事件 (eventlisteneracceleration)

滑鼠響應事件 (eventlistenermouse)

自定義事件 (eventlistenercustom)

以上事件監聽器統一由 <code>_eventdispatcher</code> 來進行管理。

_eventdispatcher 是 node 的屬性,通過它管理目前節點(如

場景 、層、精靈等 )的所有事件分發情況。但是它本身是一個單例模式值的引用,在 node 構造函數中,通過 "director::getinstance()-&gt;geteventdispatcher();" 擷取,有了這個屬性,我們能更為友善的調用。

有兩種方式将 事件監聽器 listener1 添加到 事件排程器_eventdispatcher 中:

Cocos2d-x 3.0 新特性體驗-觸摸事件處理機制

void eventdispatcher::addeventlistenerwithscenegraphpriority(eventlistener* listener, node* node)  

void eventdispatcher::addeventlistenerwithfixedpriority(eventlistener* listener, int fixedpriority)  

看看這兩種方式的實作代碼:

Cocos2d-x 3.0 新特性體驗-觸摸事件處理機制

    ccassert(listener &amp;&amp; node, "invalid parameters.");  

    ccassert(!listener-&gt;isregistered(), "the listener has been registered.");  

    if (!listener-&gt;checkavailable())  

        return;  

    listener-&gt;setscenegraphpriority(node);  

    listener-&gt;setfixedpriority(0);  

    listener-&gt;setregistered(true);  

    addeventlistener(listener);  

}  

    ccassert(listener, "invalid parameters.");  

    ccassert(fixedpriority != 0, "0 priority is forbidden for fixed priority since it's used for scene graph based priority.");  

    listener-&gt;setscenegraphpriority(nullptr);  

    listener-&gt;setfixedpriority(fixedpriority);  

    listener-&gt;setpaused(false);  

從中我們可以知道: 其中的 addeventlistenerwithscenegraphpriority 的事件監聽器優先級是 0 ;而且在 addeventlistenerwithfixedpriority 中的事件監聽器的優先級不可以設定為 0,因為這個是保留給 scenegraphpriority

使用的。

注意:(1) 這裡當我們再次使用 listener1 的時候,需要使用 <code>clone()</code> 方法建立一個新的克隆,因為在使用 <code>addeventlistenerwithscenegraphpriority</code> 或者 <code>addeventlistenerwithfixedpriority</code> 方法時,會對目前使用的事件監聽器添加一個已注冊的标記,這使得它不能夠被添加多次。

看看clone()方法的代碼:

Cocos2d-x 3.0 新特性體驗-觸摸事件處理機制

eventlistenertouchonebyone* eventlistenertouchonebyone::clone()  

    auto ret = new eventlistenertouchonebyone();  

    if (ret &amp;&amp; ret-&gt;init())  

        ret-&gt;autorelease();  

        ret-&gt;ontouchbegan = ontouchbegan;  

        ret-&gt;ontouchmoved = ontouchmoved;  

        ret-&gt;ontouchended = ontouchended;  

        ret-&gt;ontouchcancelled = ontouchcancelled;  

        ret-&gt;_claimedtouches = _claimedtouches;  

        ret-&gt;_needswallow = _needswallow;  

    else  

        cc_safe_delete(ret);  

    return ret;  

(2)另外,有一點非常重要,fixedpriority listener添加完之後需要手動remove,而scenegraphpriority listener是跟node綁定的,在node的析構函數中會被移除。

Cocos2d-x 3.0 新特性體驗-觸摸事件處理機制

_eventdispatcher-&gt;cleantarget(this);  

   cc_safe_release(_eventdispatcher);  

二、例子2

在上面的例子中,使用的是 addeventlistenerwithscenegraphpriority 添加觸摸監聽器,也就是單點觸摸。其結點的觸摸優先級都是相同的 0 。那麼上層的結點 是比 下層的結點 先處理觸摸事件的。

下面看看如何使用 addeventlistenerwithfixedpriority 自定義結點的觸摸優先級。

Cocos2d-x 3.0 新特性體驗-觸摸事件處理機制

(1)首先自定義精靈,其中可以設定精靈接受觸摸的優先級。

Cocos2d-x 3.0 新特性體驗-觸摸事件處理機制

class touchablespritewithfixedpriority : public sprite  

    create_func(touchablespritewithfixedpriority);  

    touchablespritewithfixedpriority()  

    : _listener(nullptr)  

    , _fixedpriority(0)  

    , _usenodepriority(false)  

    void setpriority(int fixedpriority) { _fixedpriority = fixedpriority; _usenodepriority = false; };  

    void setprioritywiththis(bool usenodepriority) { _usenodepriority = usenodepriority; _fixedpriority = true; }  

    void onenter() override  

        sprite::onenter();  

        auto listener = eventlistenertouchonebyone::create();  

        listener-&gt;setswallowtouches(true);  

        listener-&gt;ontouchbegan = [=](touch* touch, event* event){  

            point locationinnode = this-&gt;converttonodespace(touch-&gt;getlocation());  

            size s = this-&gt;getcontentsize();  

            rect rect = rect(0, 0, s.width, s.height);  

            if (rect.containspoint(locationinnode))  

            {  

                this-&gt;setcolor(color3b::red);  

                return true;  

            }  

            return false;  

        };  

        listener-&gt;ontouchmoved = [=](touch* touch, event* event){  

            //this-&gt;setposition(this-&gt;getposition() + touch-&gt;getdelta());  

        listener-&gt;ontouchended = [=](touch* touch, event* event){  

            this-&gt;setcolor(color3b::white);  

        if (_usenodepriority)  

        {  

            _eventdispatcher-&gt;addeventlistenerwithscenegraphpriority(listener, this);  

        }  

        else  

            _eventdispatcher-&gt;addeventlistenerwithfixedpriority(listener, _fixedpriority);  

        _listener = listener;  

    void onexit() override  

        _eventdispatcher-&gt;removeeventlistener(_listener);  

        sprite::onexit();  

    eventlistener* _listener;  

    int _fixedpriority;  

    bool _usenodepriority;  

(2)分别建立三個精靈,可以自定義設定每一個精靈的觸摸優先級。注意:優先級值小的,接受觸摸優先。

Cocos2d-x 3.0 新特性體驗-觸摸事件處理機制

auto sprite1 = touchablespritewithfixedpriority::create();  

    sprite1-&gt;settexture("images/cyansquare.png");  

    sprite1-&gt;setpriority(30);  

    sprite1-&gt;setposition(origin+point(size.width/2, size.height/2) + point(-80, 40));  

    addchild(sprite1, 10);  

    auto sprite2 = touchablespritewithfixedpriority::create();  

    sprite2-&gt;settexture("images/magentasquare.png");  

    sprite2-&gt;setpriority(20);  

    auto sprite3 = touchablespritewithfixedpriority::create();  

    sprite3-&gt;settexture("images/yellowsquare.png");  

    sprite3-&gt;setpriority(10);  

    sprite2-&gt;addchild(sprite3, 1);  

三、删除觸摸監聽器的方法:

Cocos2d-x 3.0 新特性體驗-觸摸事件處理機制

/** remove a listener 

  *  @param listener the specified event listener which needs to be removed. 

  */  

 void removeeventlistener(eventlistener* listener);  

 /** removes all listeners with the same event listener type */  

 void removeeventlisteners(eventlistener::type listenertype);  

前者隻是删除某一個事件監聽器,而後者是删除某一類事件監聽器(使用了 clone 克隆)

繼續閱讀