在使用時,首先建立一個事件監聽器,事件監聽器包含如下幾種:node
觸摸事件 (EventListenerTouch)函數
鍵盤響應事件 (EventListenerKeyboard)this
加速記錄事件 (EventListenerAcceleration)spa
鼠標響應事件 (EventListenerMouse)code
自定義事件 (EventListenerCustom)繼承
以上事件監聽器統一由 _eventDispatcher
來進行管理。它的工做須要三部分組成:遊戲
事件分發器 EventDispatcher事件
事件類型 EventTouch, EventKeyboard 等圖片
事件監聽器 EventListenerTouch, EventListenerKeyboard 等ci
監聽器實現了各類觸發後的邏輯,在適當時候由 事件分發器分發事件類型,而後調用相應類型的監聽器。
如今將要實如今一個界面中添加三個按鈕,三個按鈕將會互相遮擋,而且可以觸發觸摸事件,如下是具體實現
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);
// 建立一個事件監聽器 OneByOne 爲單點觸摸 auto listener1 = EventListenerTouchOneByOne::create(); // 設置是否吞沒事件,在 onTouchBegan 方法返回 true 時吞沒 listener1->setSwallowTouches(true); // 使用 lambda 實現 onTouchBegan 事件回調函數 listener1->onTouchBegan = [](Touch* touch, Event* event){ // 獲取事件所綁定的 target 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){ auto target = static_cast<Sprite*>(event->getCurrentTarget()); // 移動當前按鈕精靈的座標位置 target->setPosition(target->getPosition() + touch->getDelta()); }; // 點擊事件結束處理 listener1->onTouchEnded = [=](Touch* touch, Event* event){ auto target = static_cast<Sprite*>(event->getCurrentTarget()); log("sprite onTouchesEnded.. "); target->setOpacity(255); // 從新設置 ZOrder,顯示的先後順序將會改變 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);
_eventDispatcher 是 Node 的屬性,經過它管理當前節點(如 場景 、層、精靈等 )的全部事件分發狀況。可是它自己是一個單例模式值的引用,在 CCNode 構造函數中,經過 "Director::getInstance()->getEventDispatcher();" 獲取,有了這個屬性,咱們能更爲方便的調用。
注意: 這裏當咱們再次使用 listener1 的時候,須要使用 clone()
方法建立一個新的克隆,由於在使用 addEventListenerWithSceneGraphPriority
或者 addEventListenerWithFixedPriority
方法時,會對當前使用的事件監聽器添加一個已註冊的標記,這使得它不可以被添加屢次。另外,有一點很是重要,FixedPriority listener添加完以後須要手動remove,而SceneGraphPriority listener是跟node綁定的,在node的析構函數中會被移除。具體的示例用法能夠參考引擎自帶的tests。
以上的步驟看似相對 2.x 版本觸摸機制實現時,複雜了點,在老的版本中繼承一個 delegate ,裏面定義了 onTouchBegan 等方法,而後在裏面判斷點擊的元素,進行邏輯處理。而這裏將事件處理邏輯獨立出來,封裝到一個 Listener 中,而以上的邏輯實現瞭如下功能:
經過添加事件監聽器,將精靈以顯示優先級 (SceneGraphPriority) 添加到事件分發器。這就是說,當咱們點擊精靈按鈕時,根據屏幕顯示的「遮蓋」實際狀況,進行有序的函數回調(即:如圖中黃色按鈕首先進入 onTouchBegan 邏輯處理)。
在事件邏輯處理時,根據各類條件處理觸摸後的邏輯,如點擊範圍判斷,設置被點擊元素爲不一樣的透明度,達到點擊效果。
由於設置了 listener1->setSwallowTouches(true);
而且在 onTouchBegan 中作相應的判斷,以決定其返回值是 false 仍是 true,用來處理觸摸事件是否依據顯示的順序關係向後傳遞。
注意:與 SceneGraphPriority 所不一樣的是 FixedPriority 將會依據手動設定的 Priority
值來決定事件相應的優先級,值越小優先級越高。
除了觸摸事件響應以外,還有如下模塊使用了相同的處理方式。
除了鍵盤,還能夠是終端設備的各個菜單,他們使用同一個監聽器來進行處理。
// 初始化並綁定 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); }
在使用加速計事件監聽器以前,須要先啓用此硬件設備:
Device::setAccelerometerEnabled(true);
而後建立對應的監聽器,在建立回調函數時,可使用 lambda 表達式建立匿名函數,也能夠綁定已有的函數邏輯實現,以下:
auto listener = EventListenerAcceleration::create(CC_CALLBACK_2(AccelerometerTest::onAcceleration, this)); _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this); // 加速計回調函數原型實現 void AccelerometerTest::onAcceleration(Acceleration* acc, Event* event) { // 這裏處理邏輯 }
在 3.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<char*>(event->getUserData()); str += buf; str += " times"; statusLabel->setString(str.c_str()); }); _eventDispatcher->addEventListenerWithFixedPriority(_listener, 1);
以上定義了一個 「自定義事件監聽器」,實現了一些邏輯,而且添加到事件分發器。那麼以上邏輯是在什麼狀況下響應呢?請看以下:
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
,而且設置了其 UserData 數據,手動的經過 _eventDispatcher->dispatchEvent(&event);
將此事件分發出去,從而觸發以前所實現的邏輯。
咱們能夠經過如下方法移除一個已經被添加了的監聽器。
_eventDispatcher->removeEventListener(listener);
也可使用以下方法,移除當前事件分發器中全部監聽器。
_eventDispatcher->removeAllEventListeners();
當使用 removeAll
的時候,此節點的全部的監聽將被移除,推薦使用 指定刪除的方式。
注意:removeAll 以後 菜單 也不能響應。由於它也須要接受觸摸事件。