I am trying to achieve a lightbox type thing in cocos2d-x. (the image lightbox; when you touch outside the image then the image disappears)
So here’s my code -
#include "HelloWorldScene.h"
USING_NS_CC;
Scene* HelloWorld::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::create();
// 'layer' is an autorelease object
auto layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !Layer::init() )
{
return false;
}
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
/////////////////////////////
// 2. add your codes below...
_bgLayer = LayerColor::create(Color4B::WHITE, visibleSize.width, visibleSize.height);
_bgLayer->setTag(1);
_bgLayer->setTouchEnabled(true);
this->addChild(_bgLayer, 0);
_popUp = Sprite::create("HelloWorld.png");
_popUp->setPosition(Vec2(visibleSize.width/2, visibleSize.height/2));
_bgLayer->addChild(_popUp, 1);
_close = MenuItemImage::create("close.png", "close.png", CC_CALLBACK_1(HelloWorld::closeButton, this));
_close->setAnchorPoint(Vec2::ANCHOR_TOP_RIGHT);
_close->setPosition(Vec2(visibleSize.width/2 + _popUp->getContentSize().width/2,
visibleSize.height/2 + _popUp->getContentSize().height/2));
auto menu = Menu::create(_close, nullptr);
menu->setPosition(Vec2::ZERO);
_bgLayer->addChild(menu, 2);
auto testSprite = Sprite::create("CloseNormal.png");
testSprite->setPosition(Vec2(visibleSize.width/4, visibleSize.height/4));
this->addChild(testSprite, 4);
auto listener = EventListenerTouchOneByOne::create();
listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved, this);
listener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
this->setKeypadEnabled(true);
return true;
}
void HelloWorld::closeButton(cocos2d::Ref *pSender)
{
auto menuItem = (MenuItem *)pSender;
auto parent = menuItem->getParent();
auto grandParent = parent->getParent();
this->removeChild(grandParent, true);
}
bool HelloWorld::onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *event)
{
CCLOG("Touch Began");
return true;
}
void HelloWorld::onTouchMoved(cocos2d::Touch *touch, cocos2d::Event *event)
{
CCLOG("Touch Moved");
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
this->removeChildByTag(1, true);
}
void HelloWorld::onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *event)
{
CCLOG("Touch Ended");
}
void HelloWorld::onKeyReleased(cocos2d::EventKeyboard::KeyCode keycode, cocos2d::Event *event)
{
Director::getInstance()->end();
}
Here, i want to disable touch for the HelloWorld.png sprite and the menu close button.
as they are child of the layer and the touch is enabled for the layer
i am getting the touch all over
which ruins the idea.
When not simply putting a check whether the touch is inside the lightbox…
If it is inside then simply bunk the code block (using if else) where you’re hiding/vanishing the lightbox… otherwise if it is outside then hide it…
I hope I got the meaning of the question correctly!!
Can you please further elaborate on the problem… What you want to do and what is happening…
Actually, I am a bit confused with the explanation of what you want to do and the code you’ve written inside onTouchMoved.
Why have you written the code inside onTouchMoved and not in onTouchEnded and onTouchBegan.
Second thing, obviously when you have already removed the child with tag =1 then again on touch move that particular section of code inside onTouchMoved is executed and hence causing the crash because second time child/node with tag=1 doesnot exist. So for this try using flags.
And tell whether it works?
but when we touch on the white layer, then both the white layer, the helloworld sprite and the close menu button will be removed from the scene.
the white layer is a child to the scene
whereas the helloworld sprite and the close menu button are child of the white layer
and the touch is enabled for the layer, so when the layer is removed from the scene
the touch is also removed right ?
and all it’s associated properties… !!
everything works fine as expected, except for the crash. @catch_up i tried your suggestion by making this change.
And… the touch is enabled for the layer… Its fine… But are you seeing that your white layer is a child to a layer which you’ve added to the scene inside createScene().
So, layer is removed but which layer is removed !!
Are you noticing that the layer which is a child of layer is getting removed and not that layer which is parent of the layer which is removed… Kind of complex…
If I am not wrong, then I think you were trying to add a layer to the scene which is of type LayerColor but you actually end up doing adding a normal layer to the scene and then adding your layer created by LayerColor to this layer…
And I think this does not solves the problem…
I am not that strong in dealing with memory…
But still I would comment on this way…
See, as soon as you do this->getChildByTag(1) it will generated a crash…
Because there isn’t anything like this->getChildByTag(1), I mean it is not a variable pointer that you declared or it is not something that was allocated null after it was removed the layer/scene.
In the tree of the children of the main layer (not the one created by LayerColor but the layer which is created inside createScene()), that node with tag=1 is removed so there isn’t anything known as getChildByTag(1).
See, this is my interpretation and I think I am correct.
That is why I was suggesting you to use flags…
Anyways, I would still like you to do it using flags and come back
And I still wonder why are you writing code inside onTouchMoved and not onTouchEnded.
with a flag, i also need to check at the onTouchBegan()
because when the child is removed and when i touch again
it again calls the onTouchBegan() method
which leads to calculate on _popUp which doesn’t exists.
So, it crashed.
I wrapped the code with a flag, not it works fine.
No you don’t have to check it in onTouchBegan…
Have you read my last posts carefully.
I mentioned that you’re not attaching (may be by mistake) your event listener to the layer you are removing.
Are you seeing that you’ve 2 layers…
One created in createScene() function and other using LayerColor::create(…)
And the 2nd layer is _bgLayer which you’re actually removin but your event listener is attached to the first layer which is the parent of _bglayer…
Can you elaborate this?
Once the _bglayer is already removed and hence its child will be removed automatically. So, why would it calculate on _popUp.
This is the final code and it works.
the only reason of the crash was because the code was not layed out correctly
and also it was calculating results based on the touch.
whereas the touch was dependent on member variables, that are removed from the first touch.
So sort of referencing to something that doesn’t exist
but getting a flag to check the condition for once works.
#include "HelloWorldScene.h"
USING_NS_CC;
Scene* HelloWorld::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::create();
// 'layer' is an autorelease object
auto layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !Layer::init() )
{
return false;
}
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
/////////////////////////////
// 2. add your codes below...
_bgLayer = LayerColor::create(Color4B::WHITE, visibleSize.width, visibleSize.height);
_bgLayer->setTag(1);
_bgLayer->setTouchEnabled(true);
_bgLayer->setSwallowsTouches(true);
this->addChild(_bgLayer, 1);
_popUp = Sprite::create("HelloWorld.png");
_popUp->setPosition(Vec2(visibleSize.width/2, visibleSize.height/2));
_bgLayer->addChild(_popUp, 1);
_close = MenuItemImage::create("close.png", "close.png", CC_CALLBACK_1(HelloWorld::closeButton, this));
_close->setAnchorPoint(Vec2::ANCHOR_TOP_RIGHT);
_close->setPosition(Vec2(visibleSize.width/2 + _popUp->getContentSize().width/2,
visibleSize.height/2 + _popUp->getContentSize().height/2));
auto menu = Menu::create(_close, nullptr);
menu->setPosition(Vec2::ZERO);
_bgLayer->addChild(menu, 2);
auto testSprite = Sprite::create("CloseNormal.png");
testSprite->setPosition(Vec2(visibleSize.width/4, visibleSize.height/4));
this->addChild(testSprite, 4);
auto listener = EventListenerTouchOneByOne::create();
listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved, this);
listener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
this->setKeypadEnabled(true);
return true;
}
void HelloWorld::closeButton(cocos2d::Ref *pSender)
{
if(_flag == true) // for one time checking
{
auto menuItem = dynamic_cast <MenuItem *> (pSender);
auto parent = menuItem->getParent();
auto grandParent = parent->getParent();
this->removeChild(grandParent, true);
_flag = false;
}
}
bool HelloWorld::onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *event)
{
CCLOG("Touch Began");
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
if(_flag == true)
{
auto touchX = touch->getLocation().x;
auto touchY = touch->getLocation().y;
auto exp1 = visibleSize.width/2 - _popUp->getContentSize().width/2;
auto exp2 = visibleSize.width/2 + _popUp->getContentSize().width/2;
auto exp3 = visibleSize.height/2 - _popUp->getContentSize().height/2;
auto exp4 = visibleSize.height/2 + _popUp->getContentSize().height/2;
if( (touchX > exp1 && touchX < exp2) && (touchY > exp3 && touchY < exp4) )
{
CCLOG("INTERNALLY TOUCHED");
return false;
}
else
{
CCLOG("EXTERNALLY TOUCHED");
return true;
}
}
else
return true;
}
void HelloWorld::onTouchMoved(cocos2d::Touch *touch, cocos2d::Event *event)
{
CCLOG("Touch Moved");
}
void HelloWorld::onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *event)
{
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
if(_flag) // for one time checking
{
this->removeChildByTag(1, true);
CCLOG("removed child");
_flag = false;
}
CCLOG("Touch Ended");
}
void HelloWorld::onKeyReleased(cocos2d::EventKeyboard::KeyCode keycode, cocos2d::Event *event)
{
Director::getInstance()->end();
}
no… i knew that…
but as i was tagging a node, so i was pretty sure that
removing child from the scene with the particular ID will remove the one and it’s associated children only.
but the cause was due to the one stated in the previous post. @catch_up thanks for all your help.