1、概述
游戏也好,程序也好,只有能与用户交互才有意义。手机上的交互大致可以分为两部分:点击和输入。其中点击更为重要,几乎是游戏中全部的交互。在Cocos2d-x 3.0中,更改了dispatch机制。同时加入了两种新的交互形式:listener 和touchEvent回调。加上先前版本中的点击函数回调,与重写layer层的touch消息响应,构成了一个相对完整的交互模式。
2、三种点击
1、函数回调
函数回调是最简单的响应形式,一直以来被用于MenuItem中的点击处理。在新版本中,此处发生了些小改变。我们可以看到在生成的程序中相关代码是这样的:
// a selector callback void menuCloseCallback(Object* pSender); auto closeItem = MenuItemImage::create("CloseNormal.png","CloseSelected.png", CC_CALLBACK_1(HelloWorld::menuCloseCallback, this)); void HelloWorld::menuCloseCallback(Object* pSender) { Director::getInstance()->end(); #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) exit(0); #endif }
其中CC_CALLBACK_1宏是将函数与对象绑定在一起,1表示这个函数有一个参数。当点击这个按钮时,会调用这个回调函数。
除了基于c++11的这个形式的改变,使用方法与先前相同
2、TouchEvent响应
这是新加入的响应方式。它主要是使用在Widget上的。可以将其看做是函数回调的一个扩展,为更多的响应处理提供可能。使用方法大致是:
//声明 void touchButton(Object* object,TouchEventType type); //挂接到控件上 uiButton->addTouchEventListener(this,toucheventselector(HelloWorld::touchButton)); //实现 void HelloWorld::touchButton(Object* object,TouchEventType type) { LabelTTF* label; switch (type) { case TouchEventType::TOUCH_EVENT_BEGAN: label = static_cast<LabelTTF*>(getChildByTag(11)); label->setString("按下按钮"); break; case TouchEventType::TOUCH_EVENT_MOVED: label = static_cast<LabelTTF*>(getChildByTag(11)); label->setString("按下按钮移动"); break; case TouchEventType::TOUCH_EVENT_ENDED: label = static_cast<LabelTTF*>(getChildByTag(11)); label->setString("放开按钮"); break; case TouchEventType::TOUCH_EVENT_CANCELED: label = static_cast<LabelTTF*>(getChildByTag(11)); label->setString("取消点击"); break; default: break; } }
通常作为UI的Widget都会在最上层,所以可以“基本上”认为这种使用方式会优先于其他方式处理点击消息。因为UILayer也会有层级的改变,比如它和MenuItem之间的关系。所以说“基本上”。
3、Listener消息响应方式
这种实现也是新加入的。它更像是点击的一个层次过滤器。点击时,在listener队里中进行过滤。每一个listener检查自己保存的touch消息响应是否会被触发。一层一层过滤,最后在到Layer的touch消息响应。
它被设计成一个全局点击响应控制。具体的用法大致是这样:
auto dispatcher = Director::getInstance()->getEventDispatcher(); auto myListener = EventListenerTouchOneByOne::create(); //如果不加入此句消息依旧会向下传递 myListener->setSwallowTouches(true); myListener->onTouchBegan = [=](Touch* touch,Event* event) { //some check if (pass) { return true; } return false; }; myListener->onTouchMoved = [=](Touch* touch,Event* event) { //do something }; myListener->onTouchEnded = [=](Touch* touch,Event* event) { //do something }; dispatcher->addEventListenerWithSceneGraphPriority(myListener,mySprite1); dispatcher->addEventListenerWithSceneGraphPriority(myListener,mySprite2);
其原理是在dispatcher中检查listener列表,例如myListener或加进来的其他listener。然后每个listener检查自己中的Item看能否达到检查条件,例如:mySprite1,mySprite2。然后执行相应的操作。但这样的话,当控件很多的时候,每一次事件都进行这种双链表的检查操作不知会不会影响些性能?