Cocos2d-x 3.X 事件分發機制

介紹
Cocos2d-X 3.X 引入了一種新的響應用戶事件的機制。

涉及三個基本的方面:

Event listeners 封裝你的事件處理代碼
Event dispatcher 向 listener 分發用戶事件
Event 對象 包含關於事件的信息
爲了響應事件,首先你要創建一個 EventListener,有五種不同的 EventListener.

EventListenerTouch 響應觸控事件
EventListenerKeyboard 響應鍵盤事件
EventListenerAcceleration 響應加速器事件
EventListenMouse 響應鼠標事件
EventListenerCustom 響應定製的事件
然後,將你的時間處理代碼連接到適當的事件監聽回調方法中。( 例如 EventListenerTouch 的 onTouchBegan ,或者 EventListenerKeyboard 的 onKeyPressed )

接着,使用 EventDispatcher 註冊你的 EventListener。

當事件觸發之後 ( 例如,用戶觸摸了屏幕,或者敲擊樂鍵盤 ),EventDispatcher 通過調用適當的 EventListener 的回調來分發 Event 對象 ( 例如 EventTouch, 或者 EventKeyboard ),每個事件對象包含對應的事件信息 ( 例如包含觸控的座標 )。

示例
在下面的代碼中,我們在場景中添加三個按鈕,每一個都可以響應觸控事件。

複製代碼

auto sprite1 = Sprite::create("Images/CyanSquare.png");
sprite1->setPosition(origin+Point(size.width/2, size.height/2) + Point(-80, 80));
addChild(sprite1, 10);

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

複製代碼
如圖所示

創建一個觸控的事件監聽器和回調代碼
(注意,在下面的代碼中,我們使用 C++11 的 Lambda 表達式來實現回調,後面的鍵盤事件使用另外一種方式,使用 CC_CALLBACK_N 宏來實現)

複製代碼
// 創建一個排隊的觸控事件監聽器 ( 同時僅僅處理一個觸控事件 )

auto listener = EventListenerTouchOneByOne::create();

// 當 “swallow touches” 設置爲 true, 然後,在 onTouchBegan 方法發返回 ‘true’ 將會吃掉觸控事件, 防止其他監聽器使用這個事件.

listener->setSwallowTouches(true);

// 使用 lambda 表達式實現 onTouchBegan 事件的回調函數

listener->onTouchBegan = [](Touch* touch, Event* event){
    // event->getCurrentTarget() 返回 *listener's* sceneGraphPriority 節點.
    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;
};

// 當移動觸控的時候

listener->onTouchMoved = [](Touch* touch, Event* event){
    auto target = static_cast<Sprite*>(event->getCurrentTarget());
// 移動當前的精靈
target->setPosition(target->getPosition() + touch->getDelta());

};

// 結束

listener->onTouchEnded = [=](Touch* touch, Event* event){
    auto target = static_cast<Sprite*>(event->getCurrentTarget());
    log("sprite onTouchesEnded.. ");
    target->setOpacity(255);
    };

複製代碼
添加事件監聽器到事件分發器
// 註冊監聽器

_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, sprite1);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), sprite2);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), sprite3);

_eventDispatcher 是 Node 的屬性,我們使用它來管理當前節點的所有事件分發 ( 還有像 Scene, Layer, Sprite 等等 )。

注意,在上面的例子中,我們在調用第二和第三個 addEventListenerWithSceneGraphPriority 中使用 clone() 方法,這是因爲每個事件監聽器只能被添加一次。addEventListenerWithSceneGraphPriority 方法和 addEventListenerWithFixedPriority 在事件監聽器中設置一個註冊標誌,如果已經設置了標誌,就不能再次添加了。

還有需要記住的就是,如果你添加了一個 fixed priority listener 到節點,當節點被刪除的時候,你需要手動刪除這個監聽器,而綁定到節點的 scene graph priority listener,當節點被析構的時候,監聽器將會被自動析構。

新的觸控機制
上面的處理過程與 2.X版本比較,看起來比較難,在舊版中,你需要從 delegate 類派生,其中定義了 onTouchBegan 等等方法,你的事件處理代碼會放到這些委託方法中。

新的事件處理機制將事件處理邏輯從 delegate 中移到了監聽器中,上面的邏輯實現瞭如下功能。

通過使用事件監聽器,精靈可以使用 SceneGraphPriority 添加到事件分發器,也就是說,當點擊精靈的時候,回調函數可以以顯示的順序來調用。( 也就是說,顯示在前面的精靈優先得到事件 )
當處理事件邏輯的時候,基於不同的狀況來處理觸控的邏輯 ( 比如),顯示點擊的效果。
由於 listener1->setSwallowTouches(true) 設置了,還有在 onTouchBegan 中的處理邏輯,不管何種顯示順序都可以被處理。
FixedPriority 對 SceneGraphPriority
EventDispatcher 使用優先級來決定監聽器對事件的分發。

FixedPriority ,一個整數,低的 EventListeners 優先高的 EventListenters.

SceneGraphPriority ,一個節點的指針,高的 Z Order 的節點優先於低的 Z Order 節點,這樣確保前面的元素獲取觸控事件。

其它事件處理模式
下面代碼使用另外的機制。

你也可以使用 CC_CALLBACK_N 宏來實現類似機制,下面的代碼演示鍵盤處理。

複製代碼
// 創建鍵盤監聽器

    auto listener = EventListenerKeyboard::create();
    listener->onKeyPressed = CC_CALLBACK_2(KeyboardTest::onKeyPressed, this);
    listener->onKeyReleased = CC_CALLBACK_2(KeyboardTest::onKeyReleased, this);

    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
// 實現鍵盤迴調的宏
 void KeyboardTest::onKeyPressed(EventKeyboard::KeyCode keyCode, Event* event)
    {
        log("Key with keycode %d pressed", keyCode);
    }

    void KeyboardTest::onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event)
    {
        log("Key with keycode %d released", keyCode);
    }   

複製代碼
Accelerometer 事件
在使用加速器事件之前,需要在設備上啓用加速器。

Device::setAccelerometerEnabled(true);
使用監聽器

複製代碼

    auto listener = EventListenerAcceleration::create(CC_CALLBACK_2(AccelerometerTest::onAcceleration, this));
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
    // 實現回調函數
    void AccelerometerTest::onAcceleration(Acceleration* acc, Event* event)
    {
        //  Processing logic here 
    }

複製代碼
鼠標事件
在 V3.0 中添加了鼠標點擊事件分發,支持多平臺,豐富了用戶的遊戲體驗。

與所有的事件類型一樣,首先需要創建事件監聽器

複製代碼

    _mouseListener = EventListenerMouse::create();
    _mouseListener->onMouseMove = CC_CALLBACK_1(MouseTest::onMouseMove, this);
    _mouseListener->onMouseUp = CC_CALLBACK_1(MouseTest::onMouseUp, this);
    _mouseListener->onMouseDown = CC_CALLBACK_1(MouseTest::onMouseDown, this);
    _mouseListener->onMouseScroll = CC_CALLBACK_1(MouseTest::onMouseScroll, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(_mouseListener, this);

複製代碼
然後,一個一個實現監聽器的回調函數

複製代碼

void MouseTest::onMouseDown(Event *event)
{
    EventMouse* e = (EventMouse*)event;
    string str = "Mouse Down detected, Key: ";
    str += tostr(e->getMouseButton());
    // ...
}

void MouseTest::onMouseUp(Event *event)
{
    EventMouse* e = (EventMouse*)event;
    string str = "Mouse Up detected, Key: ";
    str += tostr(e->getMouseButton());
    // ...
}

void MouseTest::onMouseMove(Event *event)
{
    EventMouse* e = (EventMouse*)event;
    string str = "MousePosition X:";
    str = str + tostr(e->getCursorX()) + " Y:" + tostr(e->getCursorY());
    // ...
}

void MouseTest::onMouseScroll(Event *event)
{
    EventMouse* e = (EventMouse*)event;
    string str = "Mouse Scroll detected, X: ";
    str = str + tostr(e->getScrollX()) + " Y: " + tostr(e->getScrollY());
    // ...
}

複製代碼
定製事件
上面的事件是系統定義的事件,事件由系統自動觸發,額外的,你也可以定製不是由系統自動觸發的事件,通過你自己的代碼來實現。

複製代碼
_`listener = EventListenerCustom::create(“game_custom_event1”, [=](EventCustom* event){
std::string str(“Custom event 1 received, “);
char* buf = static_cast

   static int count = 0;
    ++count;

    char* buf = new char[10];
    sprintf(buf, "%d", count);

    EventCustom event("game_custom_event1");
    event.setUserData(buf);

    _eventDispatcher->dispatchEvent(&event);

    CC_SAFE_DELETE_ARRAY(buf);

複製代碼
上面的代碼創建了一個 EventCustom 對象,設置了用戶數據,通過手工調用 _eventDispatcher 的 dispatchEvent 方法觸發,這就會觸發前面定義的處理器。

刪除事件監聽器
已經添加的事件監聽器可以如下刪除。

_eventDispatcher->removeEventListener(listener);

使用下面的代碼,刪除所有的事件監聽器。

_eventDispatcher->removeAllEventListeners();

當掉用 removeAllEventListeners 的時候,這個節點所有的監聽器都被刪除了,建議刪除特定的監聽器。

注意,當調用 removeAll 之後,菜單會停止響應,因爲它也需要接收觸控事件。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章