最新文章
Cocos2d-x游戏开发实例详解7:对象释放时机
03-25 13:59
Cocos2d-x游戏开发实例详解6:自动释放池
03-25 13:55
Cocos2d-x游戏开发实例详解5:神奇的自动释放
03-25 13:49
Cocos2d-x游戏开发实例详解4:游戏主循环
03-25 13:44
Cocos2d-x游戏开发实例详解3:无限滚动地图
03-25 13:37
Cocos2d-x游戏开发实例详解2:开始菜单续
03-25 13:32
cocos2d-x 禁用触摸
Cocos2d-x 从 3.0 版本以来,触摸机制有所改变。尽管实现原理未变,但本文将简单复习 3.0 的事件分发机制,并分享在研究过程中遇到的问题。这里采用的是最简单、最直接的方法,整体思路是设置触摸监听器吞噬触摸,在回调函数 onTouchBegan 中返回 true,同时确保该层的触摸优先级大于要屏蔽的层的优先级。若此方法无法满足需求,可自行研究、查看其他博客或留言共同探讨。
在实际实现中,常见的需求是屏蔽菜单,使其不可点击。下面将创建一个简单的场景,添加菜单,并加入一个层来屏蔽下层的触摸。
主场景代码
bool CreateGame::init()
{
if (!Layer::init())
return false;
// UI
auto size = Director::getInstance()->getWinSize();
Vector<MenuItem *> itemVector;
for (int i = 1; i < 4; i++)
{
auto item = MenuItemImage::create("no_people.png", "people.png");
item->setTag(i);
itemVector.pushBack(item);
}
auto menu1 = Menu::createWithArray(itemVector);
menu1->alignItemsHorizontallyWithPadding(10);
menu1->setPositionY(size.height * 0.75);
this->addChild(menu1);
auto swallowTouch = SwallowTouch::create();
this->addChild(swallowTouch);
return true;
}
在上述代码的最后添加了一个层 swallowTouch,用于屏蔽触摸。需注意该层的添加位置,它是最后添加的,会显示在最前面,这与后续的触摸屏蔽相关。
屏蔽触摸层的 init 函数
bool SwallowTouch::init()
{
if (!LayerColor::initWithColor(Color4B(100, 100, 100, 100)))
return false;
auto label = Label::createWithTTF("touch!", "fonts/Marker Felt.ttf", 32);
label->setPosition(Point(350, 800));
this->addChild(label);
auto callback = [](Touch *, Event *)
{
return true;
};
auto listener = EventListenerTouchOneByOne::create();
listener->onTouchBegan = callback;
listener->setSwallowTouches(true);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
// _eventDispatcher->addEventListenerWithFixedPriority(listener, -1);
return true;
}
上述代码很简单,添加了一个事件监听器,设置其吞噬触摸,并在 onTouchBegan 回调函数中返回 true。关键在于将事件监听器添加到分发器的代码,有两种添加方式:
方式一:addEventListenerWithSceneGraphPriority
此方法需要将事件监听器与一个 node 绑定。绑定后,node 对触摸的处理与 listener 一致,如触摸优先级、触摸处理代码等。此时,Listener 的优先级就是绑定的 node 的显示优先级,即显示在场景前面的 node 先接收触摸消息。因此,在主场景代码中,要将创建该层的代码放在后面,以保证其优先级高,能首先收到触摸消息。
方式二:addEventListenerWithFixedPriority
调用该函数时无需绑定 node,其优先级通过第二个参数传递。通过改变该优先级可查看不同效果:
- 设置为
-1时,可成功屏蔽下层的菜单点击。 - 设置为
0时,程序运行会报错,因为0这个优先级已被占用,设置优先级时不能为0。 - 设置为
1时,无法屏蔽下层的触摸。
若只是要屏蔽下层按钮的点击,可将代码整合到一个类中:
bool CreateGame::init()
{
if (!Layer::init())
return false;
// UI
auto size = Director::getInstance()->getWinSize();
Vector<MenuItem *> itemVector;
for (int i = 1; i < 4; i++)
{
auto item = MenuItemImage::create("no_people.png", "people.png");
item->setTag(i);
itemVector.pushBack(item);
}
auto menu1 = Menu::createWithArray(itemVector);
menu1->alignItemsHorizontallyWithPadding(10);
// 设置菜单为不可点击
// menu1->setEnabled(false);
menu1->setPositionY(size.height * 0.75);
auto item = (MenuItem *)menu1->getChildByTag(1);
item->selected();
this->addChild(menu1);
auto layer = LayerColor::create(Color4B(100, 100, 100, 100));
auto callback = [](Touch *, Event *)
{
return true;
};
auto listener = EventListenerTouchOneByOne::create();
listener->onTouchBegan = callback;
listener->setSwallowTouches(true);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, layer);
// _eventDispatcher->addEventListenerWithFixedPriority(listener, -1);
this->addChild(layer);
return true;
}
注意事项
如果使用固定优先值的监听器添加到一个节点(addEventListenerWithFixedPriority),当该节点被移除时,必须手动移除这个监听器。而使用显示优先监听器添加到节点(addEventListenerWithSceneGraphPriority)则无需手动移除,因为监听器和节点已绑定,节点的析构函数被调用时,监听器会自动移除。
若屏蔽了触摸,在某些逻辑处需要取消触摸屏蔽,例如弹出对话框,用户点击关闭按钮后应取消对下层的屏蔽。若使用固定优先级,仅移除对话框无法取消屏蔽,因为事件监听器未移除,触摸事件仍会分发到该监听器。此时,需手动移除事件监听器。