Cocos2d-x v3.x官方文档: 事件分发机制




新事件分发机制:在2.x 版本事件处理时,将要触发的事件交给代理(delegate)处理,再通过实现代理里面的onTouchBegan等方法接收事件,最后完成事件的响应。而在新的事件分发机制中,只需通过创建一个事件监听器-用来实现各种触发后的逻辑,然后添加到事件分发器_eventDispatcher,所有事件监听器有这个分发器统一管理,即可完成事件响应。

 

事件监听器有以下几种:

1. 触摸事件 (EventListenerTouch)

2. 键盘响应事件 (EventListenerKeyboard)

3. 鼠标响应事件 (EventListenerMouse)

4. 自定义事件 (EventListenerCustom)

5. 加速记录事件 (EventListenerAcceleration)

 

_eventDispatcher的工作由三部分组成:

1. 事件分发器 EventDispatcher

2. 事件类型 EventTouch, EventKeyboard 等

3. 事件监听器 EventListenerTouch, EventListenerKeyboard 等

4. 监听器实现了各种触发后的逻辑,在适当时候由事件分发器分发事件类型,然后调用相应类型的监听器。

 

用户输入事件

触摸事件

在处理触摸事件时,既可以重写三个方法onTouchBegan,onTouchMoved和onTouchEnded,也可以直接通过Lambda表达式完成响应逻辑。

 

在2.x版本中,开启多点触摸需要在AppController.mm中的application didFinishLaunchingWithOptions:launchOptions中添加[__glView setMultipleTouchEnabled: YES],另外还需重载5个相应函数:

 
 
  1. virtual void registerWithTouchDispatcher(void); 
  2. virtual void ccTouchesBegan(cocos2d::CCSet pTouches, cocos2d::CCEvent pEvent); 
  3. virtual void ccTouchesMoved(cocos2d::CCSet pTouches, cocos2d::CCEvent pEvent); 
  4. virtual void ccTouchesEnded(cocos2d::CCSet pTouches, cocos2d::CCEvent pEvent); 
  5. virtual void ccTouchesCancelled(cocos2d::CCSet pTouches, cocos2d::CCEvent pEvent); 

 

而在3.0中,只需创建多点触摸事件监听器,并将其添加到事件分发器中即可。

 

以下代码在一个界面中添加三个按钮,三个按钮相互遮挡,并且都能触发触摸事件:

 
 
  1. // 创建按钮精灵 
  2. auto sprite1 = Sprite::create("Images/CyanSquare.png"); 
  3. sprite1->setPosition(origin+Point(size.width/2, size.height/2) + Point(-80, 80)); 
  4. addChild(sprite1, 10); 
  5.  
  6. // sprite2 
  7.     ... 
  8.  
  9. // sprite3 
  10.     ... 

 

创建好按钮精灵后,创建单点触摸事件监听器,并完成相应逻辑处理。

 
 
  1. // 创建一个事件监听器类型为 OneByOne 的单点触摸  
  2. auto listener1 = EventListenerTouchOneByOne::create(); 
  3.  
  4. // 设置是否吞没事件,在 onTouchBegan 方法返回 true 时吞没 
  5. listener1->setSwallowTouches(true); 
  6.  
  7. // 使用 lambda 实现 onTouchBegan 事件回调函数 
  8. listener1->onTouchBegan = [](Touch* touch, Event* event) -> bool { 
  9.  
  10. // 获取事件所绑定的 target  
  11. auto target = static_cast<Sprite*>(event->getCurrentTarget()); 
  12.  
  13. // 获取当前点击点所在相对按钮的位置坐标  
  14. Point locationInNode = target->convertToNodeSpace(touch->getLocation());  
  15. Size s = target->getContentSize(); 
  16. Rect rect = Rect(0, 0, s.width, s.height); 
  17.  
  18. // 点击范围判断检测 
  19. if (rect.containsPoint(locationInNode)) 
  20. log("sprite began... x = %f, y = %f", locationInNode.x, locationInNode.y); 
  21. target->setOpacity(180); 
  22.    return true
  23. return false
  24. }; 
  25.  
  26. // 触摸移动时触发  
  27. listener1->onTouchMoved = [](Touch* touch, Event* event){...}; 
  28.  
  29. // 点击事件结束处理  
  30. listener1->onTouchEnded = [=](Touch* touch, Event* event){...}; 

 

最后需要将事件监听器添加到事件分发器。

 
 
  1. // 添加监听器 
  2. _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, sprite1); 
  3. _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), sprite2); 
  4. _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), sprite3); 

 

以上代码中_eventDispatcher是Node的属性,通过它管理当前节点(场景、层、精灵等)的所有事件的分发。但它本身是一个单例模式值的引用,在Node的构造函数中,通过Director::getInstance()->getEventDispatcher(); 获取,有了这个属性,就能方便的处理事件。

 

注意:当再次使用 listener1 的时候,需要使用clone()方法创建一个新的克隆,因为在使用addEventListenerWithSceneGraphPriority或者addEventListenerWithFixedPriority方法时,会对当前使用的事件监听器添加一个已注册的标记,这使得它不能够被添加多次。另外,有一点非常重要,FixedPriority listener添加完之后需要手动remove,而SceneGraphPriority listener是跟Node绑定的,在Node的析构函数中会被移除。具体的示例用法可以参考引擎自带的tests。

 

我们可以通过以下方法移除一个已经被添加了的监听器。

 
 
  1. _eventDispatcher->removeEventListener(listener); 

 

也可以使用如下方法,移除当前事件分发器中所有监听器。 

 
 
  1. _eventDispatcher->removeAllEventListeners(); 

 

当使用removeAll的时候,此节点的所有的监听将被移除,推荐使用 指定删除的方式。removeAll之后菜单也不能响应。因为它也需要接受触摸事件。

 

键盘响应事件

键盘响应事件和处理触摸事件使用了相同的处理方式,一下代码演示如何处理键盘响应事件:

 
 
  1. //初始化并绑定 
  2. autolistener=EventListenerKeyboard::create(); 
  3. listener->onKeyPressed=CC_CALLBACK_2(KeyboardTest::onKeyPressed,this); 
  4. listener->onKeyReleased=CC_CALLBACK_2(KeyboardTest::onKeyReleased,this); 
  5. _eventDispatcher->addEventListenerWithSceneGraphPriority(listener,this); 
  6. //键位响应函数原型 
  7. voidKeyboardTest::onKeyPressed(EventKeyboard::KeyCodekeyCode,Event*event) 
  8.   log("Keywithkeycode%dpressed",keyCode); 
  9.  
  10. voidKeyboardTest::onKeyReleased(EventKeyboard::KeyCodekeyCode,Event*event)
  11. log("Keywithkeycode%dreleased",keyCode); 

 

鼠标响应事件

在 3.0 中多了鼠标捕获事件派发,这可以在不同的平台上,丰富我们游戏的用户体验。

 

下面代码实现鼠标响应事件的实现步骤:

 
 
  1. // 创建监听器 
  2.  _mouseListener = EventListenerMouse::create(); 
  3.  
  4. // 时间响应逻辑 
  5.  _mouseListener->onMouseMove = [=](Event *event){ 
  6.     EventMouse* e = (EventMouse*)event; 
  7.     string str = "Mouse Down detected, Key: "
  8.     str += tostr(e->getMouseButton()); 
  9.     // ... 
  10. }; 
  11. _mouseListener->onMouseUp = [=](Event *event){...}; 
  12. _mouseListener->onMouseDown = [=](Event *event){...}; 
  13. _mouseListener->onMouseScroll = [=](Event *event){...}; 
  14.  
  15.  // 添加到事件分发器     
  16. _eventDispatcher->addEventListenerWithSceneGraphPriority(_mouseListener, this); 

 

自定义事件

以上是系统自带的事件类型,事件由系统内部自动触发,如 触摸屏幕,键盘响应等,除此之外,还提供了一种 自定义事件,简而言之,它不是由系统自动触发,而是人为的干涉,如下:

 
 
  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, 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. if(...)  
  8.  { 
  9.     _eventDispatcher->dispatchEvent(&event); 
  10.  
  11.  } 
  12. CC_SAFE_DELETE_ARRAY(buf); 

 

定义一个 EventCustom,并且设置了其 UserData 数据,手动的通过 _eventDispatcher->dispatchEvent(&event); 将此事件分发出去,从而触发之前所实现的逻辑。

 

加速计事件

除了触摸,移动设备上一个很重要的输入源是设备的方向,因此大多数设备都配备了加速计,用于测量设备静止或匀速运动时所受到的重力方向。

 

重力感应来自移动设备的加速计,通常支持X,Y和Z三个方向的加速度感应,所以又称为三向加速计。在实际应用中,可以根据3个方向的力度大小来计算手机倾斜的角度或方向。

 

3.0中,新的事件机制下,我们需要通过创建一个加速计监听器EventListenerAcceleration,其静态create方法中有个Acceleration的参数需要注意。Acceleration是一个类,包含了加速计获得的3个方向的加速度,相关代码如下:

 
 
  1. class Acceleration  
  2.  
  3. public
  4.     double x; 
  5.     double y; 
  6.     double z;  
  7.     double timestamp;  
  8.     Acceleration(): x(0), y(0), z(0), timestamp(0) {} 
  9.  
  10. }; 

 

该类中每个方向的加速度大小都为一个重力加速度大小。

 

在使用加速计事件监听器之前,需要先启用此硬件设备:

 
 
  1. Device::setAccelerometerEnabled(true); 

 

然后创建对应的监听器,在创建回调函数时,可以使用 lambda 表达式创建匿名函数,也可以绑定已有的函数逻辑实现,如下:

 
 
  1. auto listener = EventListenerAcceleration::create([=](Acceleration* acc, Event* event)
  2.   //逻辑代码段 }); 
  3.  
  4. _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值