cocos2d-x v3.0 事件派发机制

前端之家收集整理的这篇文章主要介绍了cocos2d-x v3.0 事件派发机制前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

于cocos2d-x v3.0alpha0加入

http://blog.csdn.net/huang_hws/article/details/18554749

介绍

cocos2d-x 3.0介绍了一种新的响应用户事件的机制,本文档介绍它的使用。
基础:
  • 事件监听器封装了事件处理代码
  • 事件派发器通知用户事件的监听器
  • 事件对象包含了事件的相关信息
要响应事件就先必须创建有EventListener,总共有五类EventListener:
  • 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
创建好EventListener之后,将你的事件处理代码添加到相关的事件监听器中(如:onTouchBegan添加到EventListenerTouch监听器,或者onKeyPressed添加键盘事件监听器)。
接下来就是用EventDispatcher注册你的事件监听器。
当事件发生时(用户触碰屏幕,键盘输入等),EventDispatcher就会分配一个事件对象(如EventTouch,EventKeyboard)到适当的事件监听器来调用你的回调函数。每一个事件对象都包含有该事件的相关信息(如触碰事件的座标轴)。

例子

在下面的例子中,我们将在场景中添加三个互相重叠的按钮。每一个都会响应触碰事件。

创建三个sprite作为图片按钮

  1. auto sprite1 = Sprite::create("Images/CyanSquare.png");
  2. sprite1->setPosition(origin+Point(size.width/2, size.height/2) + Point(-80, 80));
  3. addChild(sprite1,153)">10);
  4.  
  5. auto sprite2 = Sprite::create("Images/MagentaSquare.png");
  6. sprite2->setPosition(origin+Point(size.width/2));
  7. addChild(sprite2,153)">20);
  8.  
  9. auto sprite3 = Sprite::create("Images/YellowSquare.png");
  10. sprite3->setPosition(Point(0,153)">0));
  11. sprite2->addChild(sprite3,153)">1);


创建一个单一的事件监听器并且实现回调

(注意到下面的例子中使用了C++11的lambda表达式来实现回调,再往下的键盘事件例子用另外一种方式来写回调函数,使用的就是CC_CALLBACK_N宏)
PS:lambda表达式看起来碉堡的样子,翻译者表达除了知道它跟js中的匿名函数一样之外,不知道它还有啥其他的特点。
  1. //Create a "one by one" touch event listener (processes one touch at a time)
  2. auto listener1 = EventListenerTouchOneByOne::create();
  3. // When "swallow touches" is true,then returning 'true' from the onTouchBegan method will "swallow" the touch event,preventing other listeners from using it.
  4. listener1->setSwallowTouches(true);
  5.  
  6. // Example of using a lambda expression to implement onTouchBegan event callback function
  7. listener1->onTouchBegan = [](Touch* touch, Event* event){
  8. // event->getCurrentTarget() returns the *listener's* sceneGraPHPriority node.
  9. auto target = static_cast<Sprite*>(event->getCurrentTarget());
  10.  
  11. //Get the position of the current point relative to the button
  12. Point locationInNode = target->convertToNodeSpace(touch->getLocation());
  13. Size s = target->getContentSize();
  14. Rect rect = Rect(s.width, s.height);
  15.  
  16. //Check the click area
  17. if (rect.containsPoint(locationInNode))
  18. {
  19. log("sprite began... x = %f,y = %f", locationInNode.x, locationInNode.y);
  20. target->setOpacity(180);
  21. return true;
  22. }
  23. return false;
  24. };
  25.  
  26. //Trigger when moving touch
  27. listener1->onTouchMoved = [](Touch* touch, Event* event){
  28. auto target = static_cast<Sprite*>(event->getCurrentTarget());
  29. //Move the position of current button sprite
  30. target->setPosition(target->getPosition() + touch->getDelta());
  31. };
  32.  
  33. //Process the touch end event
  34. listener1->onTouchEnded = [=](Touch* touch, Event* event){
  35. auto target = static_cast<Sprite*>(event->getCurrentTarget());
  36. log("sprite onTouchesEnded.. ");
  37. target->setOpacity(255);
  38. //Reset zOrder and the display sequence will change
  39. if (target == sprite2)
  40. {
  41. sprite1->setZOrder(100);
  42. }
  43. else if(target == sprite1)
  44. {
  45. sprite1->setZOrder(0);
  46. }
  47. };

添加一个事件监听器到事件派发器

  1. //Add listener
  2. _eventDispatcher->addEventListenerWithSceneGraPHPriority(listener1, sprite1);
  3. _eventDispatcher->addEventListenerWithSceneGraPHPriority(listener1->clone(), sprite2);
  4. _eventDispatcher->addEventListenerWithSceneGraPHPriority(listener1->clone(), sprite3);
_eventDispatcher是Node的一个属性,使用它来管理当前节点(Scene,Layer和Sprite)不同事件的分配。
注意:上面的例子中在第二次以后调用addEventListenerWithSceneGraPHPriority使用clone()方法是因为每一个事件监听器只能被添加一次,addEventListenerWithSceneGraPHPriority函数和addEventListenerWithFixedPriority函数会在添加事件监听器时设置一个已注册标识,一旦设置了标识,就不能再用于注册事件监听了。
还有一个需要注意的:如果是固定优先值的监听器添加到一个节点(addEventListenerWithFixedPriority),那当这个节点被移除时必须同时手动移除这个监听器,但是添加场景图优先监听器到节点(addEventListenerWithSceneGraPHPriority)就不用这么麻烦,监听器和节点是绑定好的,一旦节点的析构函数调用,监听器也会同时被移除。

新的触碰制

乍看之下新的事件处理机制好像比2.x版本的事件机制更加繁琐。但是在旧版本中,当你继承了一个被代理的类,例如一个定义了onTouchBegan()方法的类,那你的事件处理将会进入这些被代理的方法里。
新的事件机制移除掉了代理的事件处理逻辑,将事件处理封装到监听器里,通过以下步骤来实现监听逻辑:
  1. 一个sprite可以将事件监听器添加到SceneGraPHPriority的事件派发器(场景图优先事件派发器),一个事件触发时,回调函数将按它们的绘制次序来调用,即在场景前的优先调用
  2. 处理事件逻辑时,根据不同的情景来调用回调(识别点击区域,设置点击元素的透明度)来播放点击效果

FixedPriority和SceneGraPHPriority

事件派发器通过优先权来决定先执行哪个监听器。
  • FixedPriority 整形值。低权值的事件监听器将优于高权值的事件监听器
  • SceneGraPHPriority Node的指针。Node的z顺序高的(绘制于顶部的)节点将优于z顺序低的节点。这将保证了诸如触碰事件的自顶向下传播

其他的事件派发处理模块

除了触碰事件响应,现在的模块也使用相同的事件处理方式。

键盘事件

除了键盘,设备上的菜单也可以使用这个监听器来处理事件。
上面的触碰事件(原文作者走神,说是鼠标事件),我们用了lambda表达式创建回调函数。我们同时也可以用下面的方法使用CC_CALLBACK_N来绑定已定义的函数
  1. //Initializing and binding
  2. auto listener = EventListenerKeyboard::create();
  3. listener->onKeyPressed = CC_CALLBACK_2(KeyboardTest::onKeyPressed, this);
  4. listener->onKeyReleased = CC_CALLBACK_2(KeyboardTest::onKeyReleased, this);
  5.  
  6. _eventDispatcher->addEventListenerWithSceneGraPHPriority(listener, this);
  7.  
  8. // Implementation of the keyboard event callback function prototype
  9. void KeyboardTest::onKeyPressed(EventKeyboard::KeyCode keyCode, Event* event)
  10. {
  11. log("Key with keycode %d pressed", keyCode);
  12. }
  13.  
  14. void KeyboardTest::onKeyReleased(EventKeyboard::KeyCode keyCode,68)">"Key with keycode %d released", keyCode);
  15. }

加速计事件

使用加速计事件,需要先启用设备的加速计:
Device::setAccelerometerEnabled(true);
然后创建相关的监听器:
  1. auto listener = EventListenerAcceleration::create(CC_CALLBACK_2(AccelerometerTest::onAcceleration, this));
  2. _eventDispatcher->addEventListenerWithSceneGraPHPriority(listener,136)">// Implementation of the accelerometer callback function prototype
  3. void AccelerometerTest::onAcceleration(Acceleration* acc, Event* event)
  4. {
  5. // Processing logic here
  6. }

鼠标事件

v3.0中新增加了鼠标点击事件分发。它适用于多平台,提升了用户体验。
像其他的事件类型一样,首先需要创建事件监听器:
  1. _mouseListener = EventListenerMouse::create();
  2. _mouseListener->onMouseMove = CC_CALLBACK_1(MouseTest::onMouseMove, this);
  3. _mouseListener->onMouseUp = CC_CALLBACK_1(MouseTest::onMouseUp, this);
  4. _mouseListener->onMouseDown = CC_CALLBACK_1(MouseTest::onMouseDown, this);
  5. _mouseListener->onMouseScroll = CC_CALLBACK_1(MouseTest::onMouseScroll, this);
  6.  
  7. _eventDispatcher->addEventListenerWithSceneGraPHPriority(_mouseListener, this);
然后实现每一个回调函数
  1. void MouseTest::onMouseDown(Event *event)
  2. {
  3. EventMouse* e = (EventMouse*)event;
  4. string str = "Mouse Down detected,Key: ";
  5. str += tostr(e->getMouseButton());
  6. // ...
  7. }
  8.  
  9. void MouseTest::onMouseUp(Event *event)
  10. {
  11. EventMouse* e = (EventMouse*)event;
  12. string str = "Mouse Up detected,136)">void MouseTest::onMouseMove(Event *event)
  13. {
  14. EventMouse* e = (EventMouse*)event;
  15. string str = "MousePosition X:";
  16. str = str + tostr(e->getCursorX()) + " Y:" + tostr(e->getCursorY());
  17. void MouseTest::onMouseScroll(Event *event)
  18. {
  19. EventMouse* e = (EventMouse*)event;
  20. string str = "Mouse Scroll detected,X: ";
  21. str = str + tostr(e->getScrollX()) + " Y: " + tostr(e->getScrollY());
  22. // ...
  23. }

自定义事件

上面的事件类型都是系统定义的,所以事件由系统自动触发。作为额外的,你可以自定义不由系统触发的事件,代码类似下面:
  1. _listener = EventListenerCustom::create("game_custom_event1", [=](EventCustom* event){
  2. std::string str("Custom event 1 received,");
  3. char* buf = static_cast<char*>(event->getUserData());
  4. str += buf;
  5. str += " times";
  6. statusLabel->setString(str.c_str());
  7. });
  8.  
  9. _eventDispatcher->addEventListenerWithFixedPriority(_listener,153)">1);
自定义的事件监听器正如上面所示,提供一个响应函数注册到事件分发器。不过你还需要通过下面的代码来实现事件的触发:
  1. static int count = 0;
  2. ++count;
  3. char* buf = new char[10];
  4. sprintf(buf, "%d", count);
  5. EventCustom event("game_custom_event1");
  6. event.setUserData(buf);
  7. _eventDispatcher->dispatchEvent(&event);
  8. CC_SAFE_DELETE_ARRAY(buf);
上面的例子创建了一个EventCustom对象并且设置了UserData,然后调用代码 _eventDispatcher->dispatchEvent(&event);手工地分发事件。这样就能触发前面定义的回调函数

移除事件监听器

下面的代码移除一个监听器:
  1. _eventDispatcher->removeEventListener(listener);
移除所有的监听器可以用下面的代码
  1. _eventDispatcher->removeAllEventListeners();
当使用removeAll时,当前node里的所有已注册监听器将被移除,删除特定的监听器才是推荐的方式。
注意:使用removeAll后,菜单事件也不会响应,因为它需要接收触碰事件。


原文地址:http://www.cocos2d-x.org/docs/manual/framework/native/input/event-dispatcher/en

猜你在找的Cocos2d-x相关文章