cocos2d-x v3.0alpha0加入
介绍
cocos2d-x 3.0介绍了一种新的响应用户事件的机制,本文档介绍它的使用。
基础:
-
EventListenerTouch
- responds to touch events -
EventListenerKeyboard
- responds to keyboard events -
EventListenerAcceleration
- reponds to accelerometer events -
EventListenMouse
- responds to mouse events -
EventListenerCustom
- responds to custom events
接下来就是用EventDispatcher注册你的事件监听器。
当事件发生时(用户触碰屏幕,键盘输入等),EventDispatcher就会分配一个事件对象(如EventTouch,EventKeyboard)到适当的事件监听器来调用你的回调函数。每一个事件对象都包含有该事件的相关信息(如触碰事件的座标轴)。
例子
在下面的例子中,我们将在场景中添加三个互相重叠的按钮。每一个都会响应触碰事件。
创建三个sprite作为图片按钮
auto sprite1 = Sprite::create("Images/CyanSquare.png"); sprite1->setPosition(origin+Point(size.width/2, size.height/2) + Point(-80, 80)); addChild(sprite1,153)">10); auto sprite2 = Sprite::create("Images/MagentaSquare.png"); sprite2->setPosition(origin+Point(size.width/2)); addChild(sprite2,153)">20); auto sprite3 = Sprite::create("Images/YellowSquare.png"); sprite3->setPosition(Point(0,153)">0)); sprite2->addChild(sprite3,153)">1);
创建一个单一的事件监听器并且实现回调
PS:lambda表达式看起来碉堡的样子,翻译者表达除了知道它跟js中的匿名函数一样之外,不知道它还有啥其他的特点。
//Create a "one by one" touch event listener (processes one touch at a time) auto listener1 = EventListenerTouchOneByOne::create(); // When "swallow touches" is true,then returning 'true' from the onTouchBegan method will "swallow" the touch event,preventing other listeners from using it. listener1->setSwallowTouches(true); // Example of using a lambda expression to implement onTouchBegan event callback function listener1->onTouchBegan = [](Touch* touch, Event* event){ // event->getCurrentTarget() returns the *listener's* sceneGraPHPriority node. auto target = static_cast<Sprite*>(event->getCurrentTarget()); //Get the position of the current point relative to the button Point locationInNode = target->convertToNodeSpace(touch->getLocation()); Size s = target->getContentSize(); Rect rect = Rect(s.width, s.height); //Check the click area if (rect.containsPoint(locationInNode)) { log("sprite began... x = %f,y = %f", locationInNode.x, locationInNode.y); target->setOpacity(180); return true; } return false; }; //Trigger when moving touch listener1->onTouchMoved = [](Touch* touch, Event* event){ auto target = static_cast<Sprite*>(event->getCurrentTarget()); //Move the position of current button sprite target->setPosition(target->getPosition() + touch->getDelta()); }; //Process the touch end event listener1->onTouchEnded = [=](Touch* touch, Event* event){ auto target = static_cast<Sprite*>(event->getCurrentTarget()); log("sprite onTouchesEnded.. "); target->setOpacity(255); //Reset zOrder and the display sequence will change if (target == sprite2) { sprite1->setZOrder(100); } else if(target == sprite1) { sprite1->setZOrder(0); } };
添加一个事件监听器到事件派发器
//Add listener _eventDispatcher->addEventListenerWithSceneGraPHPriority(listener1, sprite1); _eventDispatcher->addEventListenerWithSceneGraPHPriority(listener1->clone(), sprite2); _eventDispatcher->addEventListenerWithSceneGraPHPriority(listener1->clone(), sprite3);
_eventDispatcher是Node的一个属性,使用它来管理当前节点(Scene,Layer和Sprite)不同事件的分配。
注意:上面的例子中在第二次以后调用addEventListenerWithSceneGraPHPriority使用clone()方法是因为每一个事件监听器只能被添加一次,addEventListenerWithSceneGraPHPriority函数和addEventListenerWithFixedPriority函数会在添加事件监听器时设置一个已注册标识,一旦设置了标识,就不能再用于注册事件监听了。
还有一个需要注意的:如果是固定优先值的监听器添加到一个节点(addEventListenerWithFixedPriority),那当这个节点被移除时必须同时手动移除这个监听器,但是添加场景图优先监听器到节点(addEventListenerWithSceneGraPHPriority)就不用这么麻烦,监听器和节点是绑定好的,一旦节点的析构函数被调用,监听器也会同时被移除。
新的触碰制
新的事件机制移除掉了代理的事件处理逻辑,将事件处理封装到监听器里,通过以下步骤来实现监听逻辑:
FixedPriority和SceneGraPHPriority
事件派发器通过优先权来决定先执行哪个监听器。
- FixedPriority 整形值。低权值的事件监听器将优于高权值的事件监听器
- SceneGraPHPriority Node的指针。Node的z顺序高的(绘制于顶部的)节点将优于z顺序低的节点。这将保证了诸如触碰事件的自顶向下传播
其他的事件派发处理模块
除了触碰事件响应,现在的模块也使用相同的事件处理方式。
键盘事件
//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,68)">"Key with keycode %d released", keyCode); }
加速计事件
使用加速计事件,需要先启用设备的加速计:
Device::setAccelerometerEnabled(true);
然后创建相关的监听器:
auto listener = EventListenerAcceleration::create(CC_CALLBACK_2(AccelerometerTest::onAcceleration, this)); _eventDispatcher->addEventListenerWithSceneGraPHPriority(listener,136)">// Implementation of the accelerometer callback function prototype void AccelerometerTest::onAcceleration(Acceleration* acc, Event* event) { // Processing logic here }
鼠标事件
像其他的事件类型一样,首先需要创建事件监听器:
_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,136)">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,153)">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后,菜单事件也不会响应,因为它需要接收触碰事件。