【声明】
【正文】
在Cocos2d-x v3.x中,对于事件的处理已经与Cocos2d-x v2.x中有天壤之别了,当你看这篇文章的时候,请不要纠结于Cocos2d-x v2.x中的事件处理了,那就通过这篇文章来总结一下Cocos2d-x v3.x中的事件处理机制吧。
一个事件由触发到完成响应,主要由以下三部分组成:
EventDispatcher
;EventTouch
,EventKeyboard
等;EventListenerTouch
,EventListenerKeyboard
等。在Cocos2d-x v3.x中,关于事件的东西,无非就是围绕上述的三个部分展开来的,掌握了上述的三个部分,也就掌握了Cocos2d-x v3.x中事件处理的精髓。
事件分发器:事件分发器,就相当于是所有事件的“总长官”;它负责调度和管理所有的事件监听器;当有事件发生时,它负责调度对应的事件;一般调用Director
类中的getEventDispatcher
获得一个事件调度器,在游戏启动时,就会创建一个默认的EventDispatcher
对象;
事件类型:在Cocos2d-x中定义了以下几种事件类型:
enum class Type { TOUCH, // 触摸事件 KEYBOARD, // 键盘事件 ACCELERATION, // 加速器事件 MOUSE, // 鼠标事件 FOCUS, // 焦点事件 CUSTOM // 自定义事件 };
事件监听器:事件监听器实现了各种事件触发后对应的逻辑;由事件分发器调用对应的事件监听器。在Cocos2d-x中定义以下的几种事件监听器:
enum class Type { UNKNOWN, // 未知的事件监听器 TOUCH_ONE_BY_ONE, // 单点触摸事件监听器 TOUCH_ALL_AT_ONCE, // 多点触摸事件监听器 KEYBOARD, // 键盘事件监听器 MOUSE, // 鼠标事件监听器 ACCELERATION, // 加速器事件监听器 FOCUS, // 焦点事件监听器 CUSTOM // 自定义事件监听器 };
下面就对以上的这些知识点,通过具体的代码进行实践。纸上得来终觉浅,绝知此事要躬行。
在处理触摸事件时,有以下两种情况:
当然了,你也不要忘了C++11中有Lambda这个东西,也可以直接通过Lambda表达式完成响应逻辑,不熟悉C++中Lambda的同学,请看这里。
下面就来一坨代码,看看到底怎么去响应一个触摸事件。
先在场景中添加三个颜色Layer,示例代码如下:
auto pLayer1 = LayerColor::create(Color4B::RED, 100, 100); pLayer1->setPosition(Vec2(visibleSize.width / 2 - 100, visibleSize.height / 2 + 100)); addChild(pLayer1); auto pLayer2 = LayerColor::create(Color4B::GREEN, 100, 100); pLayer2->setPosition(Vec2(visibleSize.width / 2 - 60, visibleSize.height / 2 + 60)); addChild(pLayer2); auto pLayer3 = LayerColor::create(Color4B::BLUE, 100, 100); pLayer3->setPosition(Vec2(visibleSize.width / 2 - 20, visibleSize.height / 2 + 20)); addChild(pLayer3);再来添加触摸事件吧,示例代码如下:
// 创建一个事件监听器类型为OneByOne的单点触摸 auto listener1 = EventListenerTouchOneByOne::create(); // 设置是否吞没事件,在onTouchBegan方法返回true时吞没 listener1->setSwallowTouches(true); listener1->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this); listener1->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved, this); listener1->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this); // 添加监听器 _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, pLayer1); _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), pLayer2); _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(), pLayer3);事件处理函数如下:
bool HelloWorld::onTouchBegan(Touch *touch, Event *unused_event) { // 获取事件所绑定的 target auto target = static_cast<Layer*>(unused_event->getCurrentTarget()); if (target == nullptr) { return true; } // 获取当前点击点所在相对按钮的位置坐标 // getLocation得到的是openGL坐标系,也就是世界坐标系 Vec2 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; }上述代码中
_eventDispatcher
是Node的属性,通过它管理当前节点(场景、层、精灵等)的所有事件的分发。但它本身是一个单例模式值的引用,在Node的构造函数中,通过以下代码获取:
Director::getInstance()->getEventDispatcher();有了这个属性,就能方便的处理事件分发了。
clone()
方法创建一个新的克隆,因为在使用
addEventListenerWithSceneGraphPriority
或者
addEventListenerWithFixedPriority
方 法时,会对当前使用的事件监听器添加一个已注册的标记,这使得它不能够被注册多次。另外,有一点非常重要,FixedPriority listener添加完之后需要手动remove,而SceneGraphPriority listener是跟Node绑定的,在Node的析构函数中会被移除。
_eventDispatcher->removeEventListener(listener);也可以使用如下方法,移除当前事件分发器中所有监听器。
_eventDispatcher->removeAllEventListeners();
当使用removeAll的时候,此节点的所有的监听将被移除,推荐使用指定删除的方式。removeAll之后菜单也不能响应。因为它也需要接受触摸事件。
在触摸事件中,对事件的使用讲解的比较详细,接下来的部分,就只通过代码和少量的文字进行总结如何使用这些事件。
要实现键盘响应事件,需要重写以下两个虚函数:
virtual void onKeyPressed(EventKeyboard::KeyCode keyCode, Event* event); virtual void onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event);实现如下:
auto keyListener = EventListenerKeyboard::create(); keyListener->onKeyPressed = [](EventKeyboard::KeyCode keyCode, Event* event){ log("Key %d pressed.", keyCode); }; keyListener->onKeyReleased = [](EventKeyboard::KeyCode keyCode, Event* event){ log("Key %d released.", keyCode); }; // 添加监听器 _eventDispatcher->addEventListenerWithSceneGraphPriority(keyListener, this);
在Cocos2d-x v3.0中多了鼠标捕获事件派发,这可以在不同的平台上,丰富我们游戏的用户体验;哦,其它平台,带鼠标的游戏平台,PC机么?还是看代码吧。
auto mouseListener = EventListenerMouse::create(); mouseListener->onMouseDown = [](Event* event){ log("Mouse Down"); }; mouseListener->onMouseUp = [](Event* event){ log("Mouse Up"); }; mouseListener->onMouseMove = [](Event* event){ log("Mouse Move"); }; mouseListener->onMouseScroll = [](Event* event){ log("Mouse Scroll"); }; _eventDispatcher->addEventListenerWithSceneGraphPriority(mouseListener, this);
貌似不能使用重写虚函数的方法去完成,只能使用Lambda表达式,晕死。
除了触摸,移动设备上一个很重要的输入源是设备的方向,因此大多数设备都配备了加速计,用于测量设备静止或匀速运动时所受到的重力方向。
不知道大家有没有《神庙大逃亡》这样的跑酷游戏,你控制手机向左偏移或者向右偏移,游戏中的主角就能感知到,然后就向左跑或者向右跑,这就是基于移动设备的加速器实现的。
重力感应来自移动设备的加速计,通常支持X,Y和Z三个方向的加速度感应,所以又称为三向加速计。在实际应用中,可以根据3个方向的力度大小来计算手机倾斜的角度或方向。
看看Cocos2d-x中关于加速器的代码:
class Acceleration { public: double x; double y; double z; double timestamp; Acceleration(): x(0), y(0), z(0), timestamp(0) {} };该类中每个方向的加速度大小都为一个重力加速度大小。在使用加速计事件监听器之前,需要使用以下代码先启用此硬件设备:
Device::setAccelerometerEnabled(true);然后创建对应的监听器,在创建回调函数时,可以使用Lambda表达式创建匿名函数,也可以绑定已有的函数逻辑实现,如下:
auto listener = EventListenerAcceleration::create([=](Acceleration* acc, Event* event){ //逻辑代码段 }); _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
以上是系统自带的事件,如果系统自带的事件无法满足你,那怎么办?还好,Cocos2d-x中提供饿了自定义事件,让你“随心所欲”。
系统事件由系统内部自动触发,如触摸屏幕,键盘响应等;而自定义事件它不是由系统自动触发,而是人为的干涉。示例代码如下:
// 定义一个名为JellyThinkCustomListener的自定义事件监听器 auto customListener = EventListenerCustom::create("JellyThinkCustomListener", [=](EventCustom* event) { char* buf = static_cast<char*>(event->getUserData()); pLabel2->setString(buf); }); _eventDispatcher->addEventListenerWithSceneGraphPriority(customListener, this);点击按钮触发自定义事件:
void HelloWorld::menuCloseCallback(Ref* pSender) { static int count = 0; ++count; char *buf = new char[50]; memset(buf, 0, 50); sprintf(buf, "http://www.jellythink.com +%d", count); EventCustom event("JellyThinkCustomListener"); event.setUserData(buf); // 触发自定义事件 _eventDispatcher->dispatchEvent(&event); CC_SAFE_DELETE_ARRAY(buf); }效果图略。
看起来貌似很简单,是的,用起来就是这么简单,这就是Cocos2d-x流行的道理了,让你用起来很舒服,做起来更舒服。
又走完一步了,就这样一篇一篇的总结下去,总结就会有收获了。这些文章写的都比较基础,都是总结的如何去使用Cocos2d-x,关于Cocos2d-x源码的研究,等总结完了使用,再开始研究Cocos2d-x的源码。
这里提供全文的PDF版下载:单击这里下载