[cocos2dx]事件分發機制(一)
事件分發機制
什麼是事件? Event及子類EventXXX,核心成員_type
誰監聽事件? EventListener及子類EventListenerXXX,核心成員_onEvent
誰派發事件? EventDispatcher分發器,核心方法dispatchEvent,(導演類全域性例項化了一個EventDispatcher)
基本概念:
- 事件監聽器:封裝了事件處理的程式碼;
- 事件排程器(或分發器):通知使用者事件的監聽器;
- 事件物件:包含了關於事件的資訊。
事件分發本質是採用的觀察者模式: 註冊觀察者,事件產生並回調,取消註冊
事件監聽器的5種類型
- 觸控響應事件 (EventListenerTouch)
- 鍵盤響應事件 (EventListenerKeyboard)
- 滑鼠響應事件 (EventListenerMouse)
- 自定義響應事件 (EventListenerCustom)
- 加速響應事件 (EventListenerAcceleration)
事件
觸控事件
在手遊中,最重要的事件是觸控事件。它們很容易被建立來提供通用的功能。首先我們要明確什麼是觸控事件。當你觸控移動裝置的螢幕時,螢幕會接收到這個觸控行為,並檢查你觸摸了哪裡以及決定你觸控到了什麼。然後你的觸控行為就會被響應。你所觸控的或許不是響應物件,但很有可能是它下面的東西。通常會給觸控事件分配優先順序,優先順序最高的就是被先響應的。以下程式碼建立了一個基本的觸控事件監聽器:
// Create a "one by one" touch event listener // (processes one touch at a time) auto listener1 = EventListenerTouchOneByOne::create(); // trigger when you push down listener1->onTouchBegan = [](Touch* touch, Event* event){ // your code return true; // if you are consuming it }; // trigger when moving touch listener1->onTouchMoved = [](Touch* touch, Event* event){ // your code }; // trigger when you let up listener1->onTouchEnded = [=](Touch* touch, Event* event){ // your code }; // Add listener _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, this);
鍵盤事件
//Initializing and binding
auto listener = EventListenerKeyboard::create();
listener->onKeyPressed = CC_CALLBACK_2(KeyboardTest::onKeyPressed, this);
listener->onKeyReleased = CC_CALLBACK_2(KeyboardTest::onKeyReleased, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
// Implementation of the keyboard event callback function prototype
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);
}
滑鼠事件
_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());
// ...
}
加速計事件
很些移動裝置都配備了加速度計。加速計是一個感測器,可以測量重力和方向上的變化。例如,來回移動你的電話來模擬平衡。Cocos2d-x也支援這些事件並且建立起來很簡單。在使用加速計事件之前,你需要在裝置上啟用這個事件:
Device::setAccelerometerEnabled(true);
// creating an accelerometer event
auto listener = EventListenerAcceleration::create(CC_CALLBACK_2(
AccelerometerTest::onAcceleration, this));
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
// Implementation of the accelerometer callback function prototype
void AccelerometerTest::onAcceleration(Acceleration* acc, Event* event)
{
// Processing logic here
}
自定義事件
上面的事件型別都是系統定義的,所以事件由系統自動觸發。作為額外的,你可以自定義不由系統觸發的事件,程式碼類似下面:
_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);手工地分發事件。這樣就能觸發前面定義的回撥函式。
事件分配器
使用分配器註冊事件
使用事件分配器可以很容易的註冊事件。以上文的觸控事件監視器為例:
// Add listener
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1,sprite1);
值得注意的是,每個物件都只能註冊一個觸控事件。如果多個物件需要使用相同的監聽器,你需要使用clone()方法.
// Add listener
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1,sprite1);
// Add the same listener to multiple objects.
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), sprite2);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), sprite3);
從分配器中移除事件
使用如下方法可以移除一個已有的監聽器:
_eventDispatcher->removeEventListener(listener);
儘管這看起來很特殊,但是內建的Node物件使用事件分發機制與我們所講的方式是相同的。以Menu為例,當點選帶有MenuItems屬性的 Menu時,你就已經分配到了一個事件。同樣也可對內建的Node物件使用removeEventListener方法。
cocos2dx+lua註冊事件函式
為了降低模組間的耦合, 很多系統使用事件派發機制, 接收方無需知道派發者是誰.在Qt中,這個系統被稱作Slot&Signal.- registerScriptTouchHandler 註冊觸屏事件
- registerScriptTapHandler 註冊點選事件
- registerScriptHandler 註冊基本事件 包括 觸屏 層的進入 退出 事件
- registerScriptKeypadHandler 註冊鍵盤事件
- registerScriptAccelerateHandler 註冊加速事件
registerScriptTouchHandler 詳解(可以設定單點或多點)
function gameWindow:addLayerTouchEventMethod1()
local function onTouchEvent(eventType, x, y)
--log("eventType = "..tostring(eventType))
if eventType == "began" then
--需要返回true
return onTouchBegan(touch, event)
elseif eventType == "moved" then
onTouchMoved(touch, event)
elseif eventType == "ended" then
onTouchEnded(touch, event)
end
end
config.bottomLayer:setTouchEnabled(true)
config.bottomLayer:registerScriptTouchHandler(onTouchEvent)
end
registerScriptTapHandler 註冊點選事件
function gameWindow:addBtn()
local btn = cc.MenuItemImage:create("white.png", "black.png", "black.png")
btn:setPosition(320, 160)
local function btnClick()
log("btnClick")
end
btn:registerScriptTapHandler(btnClick)
local menu = cc.Menu:create()
config.bottomLayer:addChild(menu)
menu:setPosition(cc.p(0,0))
menu:addChild(btn)
end
registerScriptHandler 註冊基本事件
function gameWindow:addLayerTouchEventMethod2()
--建立一個單點觸屏事件
local listener = cc.EventListenerTouchOneByOne:create()
--註冊觸屏開始事件
listener:registerScriptHandler(onTouchBegan, cc.Handler.EVENT_TOUCH_BEGAN)
--註冊觸屏移動事件
listener:registerScriptHandler(onTouchMoved, cc.Handler.EVENT_TOUCH_MOVED)
--註冊觸屏結束事件
listener:registerScriptHandler(onTouchEnded, cc.Handler.EVENT_TOUCH_ENDED)
--獲取層的事件派發器
local eventDispatcher = config.bottomLayer:getEventDispatcher()
--事件派發器 註冊一個node事件
eventDispatcher:addEventListenerWithSceneGraphPriority(listener, config.bottomLayer)
end
註冊layer的進入/退出事件用法
function gameWindow:addLayerEnterAndExitEvent()
local function onNodeEvent(eventType)
if eventType == "enter" then
log("enter")
elseif eventType == "exit" then
log("exit")
end
end
config.bottomLayer:registerScriptHandler(onNodeEvent)
end
事件分發機制