cocos基础教程(12)点击交互的三种处理 1、概述 2、三种点击

游戏也好,程序也好,只有能与用户交互才有意义。手机上的交互大致可以分为两部分:点击和输入。其中点击更为重要,几乎是游戏中全部的交互。在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。然后执行相应的操作。但这样的话,当控件很多的时候,每一次事件都进行这种双链表的检查操作不知会不会影响些性能?