一般游戏都会用到弹出框(退出提示,设置,战斗胜利等),初学者都会遇到这类的问题。要做一个弹出框的效果可能相对比较简单,但在实际开发中常常会遇到一些麻烦,就是点击弹出框的部分背后的事件也会响应,仅仅是这样的效果达不到要求。
小编初学这个的时候也会遇到这类问题,常常的解决方式就是度娘。
网上的方式大都很繁琐,不易理解,还有就是版本年代久远现在不适用,(小编的版本是cocos2dx 3.10)。
比如网上流行的一种方式是创建场景,即在原场景中在创建一个场景实现弹框效果,然后改变触摸优先级的大小,优先对最外层的场景触摸。这种方法理论上会实现,但是小编作为一个初学者这种方式理解相对较难。另外创建场景的方式会多创两个文件(头文件,cpp文件),如果仅仅只是显示几个文字,这样的方式显得很累赘,无疑增加代码量。(个人见解,无意冒犯)。
我今天讲的就是用创建的层的方式创建一个弹出框,然后用层截断的方式屏蔽背后的触摸。
这里做了一个测试在原层上创建了一个精灵 ,可以点击拖动。一个(点击弹出层)的按钮,点击就会弹出第三图的灰色的层。
没有弹出层的时候可以拖动精灵的位置,弹出层出现后,屏蔽了背后的响应事件,就不能拖动了。
也许大家有疑问这与弹框有什么联系,这个弹出层的大小可以设置,可以设置成想要的大小。层上面可以添加东西,与原层的添加方式一样,这里我在弹出层上添加了一个按钮(右下角),点击可以关闭弹出层。这里设置弹出层的目的是屏蔽背后的事件响应,所有我这里的弹出层的大小与显示区域大小一样。
#include "TestCeng.h"
Scene* TestCeng::createScene()
{
auto scene = Scene::create();
auto layer = TestCeng::create();
scene->addChild(layer);
return scene;
}
bool TestCeng::init()
{
if (!Layer::init())
{
return false;
}
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
//顶部显示标签
Dictionary* strings = Dictionary::createWithContentsOfFile("string.xml");
const char * str;
//获取string.xml文件键值
str = ((String *)strings->objectForKey("cenglogo"))->getCString();
auto label = Label::createWithSystemFont(str, "", 30);
label->setPosition(Vec2(visibleSize.width / 2, visibleSize.height*0.85));
this->addChild(label, 2);
//创建可以移动的精灵
auto spr = Sprite::create("HelloWorld.png");
spr->setTag(1);
spr->setScale(0.5);
spr->setPosition(Point(visibleSize.width / 2, visibleSize.height / 2));
this->addChild(spr);
//创建底部按钮
str = ((String *)strings->objectForKey("ceng"))->getCString();
auto button = MenuItemFont::create(str, CC_CALLBACK_0(TestCeng::createLayer, this));
auto menu = Menu::create(button, NULL);
menu->setPosition(Point(visibleSize.width / 2, visibleSize.height*0.2));
this->addChild(menu, 2);
//添加触摸事件
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);
listener->onTouchBegan = CC_CALLBACK_2(TestCeng::onTouchBegan, this);
listener->onTouchMoved = CC_CALLBACK_2(TestCeng::onTouchMoved, this);
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, spr);
return true;
}
void TestCeng::createLayer()
{
Size visibleSize = Director::getInstance()->getVisibleSize();
//创建一个弹出层
auto layer = LayerColor::create(Color4B(100, 180, 180, 180));
layer->setContentSize(visibleSize);//设置与可显示区域大小一样
layer->setPosition(Vec2::ZERO);
this->addChild(layer,3);
//层上添加一个返回按钮
auto button = MenuItemImage::create("fanhui.jpg", "fanhui.jpg", CC_CALLBACK_0(TestCeng::menuCloseCallback, this));
button->setScale(0.2);
auto menu = Menu::create(button, NULL);
menu->setPosition(Point(layer->getContentSize().width*0.95, layer->getContentSize().height*0.05));
layer->addChild(menu);
//对该层添加触摸事件 用于屏蔽背后的触摸事件
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);
//实现屏蔽
listener->onTouchBegan = [=](Touch *touch, Event *event) {
Point location = touch->getLocation();
if(layer->getBoundingBox().containsPoint(location))
return true;
};
//绑定layer的触摸事件
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, layer);
}
bool TestCeng::onTouchBegan(Touch *touch, Event *event)
{
Point location = touch->getLocation();
auto spr = (Sprite*)this->getChildByTag(1);
if (spr->getBoundingBox().containsPoint(location))
return true;
return false;
}
void TestCeng::onTouchMoved(Touch *touch, Event *event)
{
auto spr = (Sprite*)this->getChildByTag(1);
spr->setPosition(spr->getPosition() + touch->getDelta());
}
void TestCeng::menuCloseCallback()
{
this->removeChild(this->getChildByTag(2));
}
init方法里创建一个顶部标签,精灵,按钮,对精灵绑定监听事件,
点击按钮进入createLayer方法,创建一个与显示区域大小一样的层,层上添加了一个返回按钮,点击移除该层。
重点说的是listener监听事件 setSwallowTouches设置触摸不可穿透,用addEventListenerWithSceneGraphPriority方式绑定监听,分别对精灵和层绑定。不建议用优先级的方式绑定。把弹出层的Z轴设在最上面,然后它将显示在最外层,触摸事件发生在层上时,return true就返回了。
在弹出层的触摸事件Began里获取弹出层的大小并判断触摸点是否在层上,在层上就截断触摸,没有自然就不会响应。这样大家就可以任意设置弹出层的大小,实现某一区域的触摸截断了。