1、概述
游戏也好,程序也好。仅仅有能与用户交互才有意义。手机上的交互大致能够分为两部分:点击和输入。
当中点击更为重要,差点儿是游戏中所有的交互。在Cocos2d-x 3.0中,更改了dispatch机制。同一时候增加了两种新的交互形式:listener 和touchEvent回调。加上先前版本号中的点击函数回调。与重写layer层的touch消息响应。构成了一个相对完整的交互模式。先上一张Demo的图:
2、四种点击
1、函数回调
函数回调是最简单的响应形式,一直以来被用于MenuItem中的点击处理。在新版本号中,此处发生了些小改变。我们能够看到在生成的程序中相关代码是这种:
-
-
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表示这个函数有一个參数。
当点击这个button时,会调用这个回调函数。
除了基于c++11的这个形式的改变。用法与先前同样。
此种方式已经被舍弃,能够使用第四种方法做替代。
2、Layer的touch消息响应
尽管重写了底层的dispatch。但对这层的使用影响并不大。我们相同须要重写:
-
-
virtual bool onTouchBegan(Touch* touch, Event *event) override;
-
virtual void onTouchMoved(Touch* touch, Event *event) override;
-
virtual void onTouchEnded(Touch* touch, Event *event) override;
-
virtual void onTouchCancelled(Touch *touch, Event *event) override;
-
-
-
virtual bool onTouchesBegan(Touch* touch, Event *event) override;
-
virtual void onTouchesMoved(Touch* touch, Event *event) override;
-
virtual void onTouchesEnded(Touch* touch, Event *event) override;
-
virtual void onTouchesCancelled(Touch *touch, Event *event) override;
重写这些函数来对layer的点击做处理。当然。我们须要:
此外有个小修改。对于单点触控响应。能够调用:
-
-
setTouchMode(Touch::DispatchMode::ONE_BY_ONE);
-
-
setTouchMode(Touch::DispatchMode::ALL_AT_ONCE);
进行设置,而不须要再用设置Delegate的方式来做了。
3、TouchEvent响应
这是新增加的响应方式。它主要是使用在UIWidget上的。能够将其看做是函数回调的一个扩展。为很多其它的响应处理提供可能。用法大致是:
-
-
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("按下button");
-
break;
-
case TouchEventType::TOUCH_EVENT_MOVED:
-
label = static_cast<LabelTTF*>(getChildByTag(11));
-
label->setString("按下button移动");
-
break;
-
case TouchEventType::TOUCH_EVENT_ENDED:
-
label = static_cast<LabelTTF*>(getChildByTag(11));
-
label->setString("放开button");
-
break;
-
case TouchEventType::TOUCH_EVENT_CANCELED:
-
label = static_cast<LabelTTF*>(getChildByTag(11));
-
label->setString("取消点击");
-
break;
-
default:
-
break;
-
}
-
}
由于全部的UIWidget都要加入到UILayer上,而UILayer通常作为UI的Widget都会在最上层。所以能够“基本上”觉得这样的使用方式会优先于其它方式处理点击消息。
由于UILayer也会有层级的改变,比方它和MenuItem之间的关系。所以说“基本上”。
4、Listener消息响应方式
这样的实现也是新增加的。它更像是点击的一个层次过滤器。
点击时,在listener队里中进行过滤。每个listener检查自己保存的touch消息响应是否会被触发。一层一层过滤,最后在到Layer的touch消息响应。
我认为它的设计的初衷是为随意sprite提供一套自己制定的点击响应。
但这种实现仍然要写非常多条件推断,没有可以控件化。
可能我的理解有些偏差,欢迎讨论。
它被设计成一个全局点击响应控制。详细的使用方法大致是这样:
-
-
-
auto dispatcher = Director::getInstance()->getEventDispatcher();
-
auto myListener = EventListenerTouchOneByOne::create();
-
-
-
myListener->setSwallowTouches(true);
-
-
myListener->onTouchBegan = [=](Touch* touch,Event* event)
-
{
-
-
if (pass)
-
{
-
return true;
-
}
-
return false;
-
};
-
-
myListener->onTouchMoved = [=](Touch* touch,Event* event)
-
{
-
-
};
-
-
myListener->onTouchEnded = [=](Touch* touch,Event* event)
-
{
-
-
};
-
-
dispatcher->addEventListenerWithSceneGraphPriority(myListener,mySprite1);
-
dispatcher->addEventListenerWithSceneGraphPriority(myListener,mySprite2);
其原理是在dispatcher中检查listener列表,比如myListener或加进来的其它listener。
然后每一个listener检查自己中的Item看是否能达到检查条件,比如:mySprite1。mySprite2。然后运行对应的操作。但这种话,当控件非常多的时候,每一次事件都进行这种双链表的检查操作不知会不会影响些性能?
3、实例
光说不练假把式。于是就动手写了一个上面的Demo:
点击背景区域能够移动整个场景。点击蓝色小方块能够半透明移动它。点击蓝色button能够更改文字,显示状态。点击右下角button退出程序。
项目配置可參照:
4、总结