Selective touch enable problem

Hey all developers, I have a question for you.

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.

could anyone please help me getting it right…?? :smile:

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!!

Ya, tried that and it works @catch_up

but the problem is -

after the children are removed and when i touch the screen again.
it crashes.

this is the updated code.
try it and see if you could get anything from the logcat.
i couldn’t get anything.

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");

	Size visibleSize = Director::getInstance()->getVisibleSize();
	Vec2 origin = Director::getInstance()->getVisibleOrigin();

	auto touchX = touch->getLocation().x;
	auto touchY = touch->getLocation().y;

//	Vec2 xSize = visibleSize.width/2;
//	Vec2 ySize = visibleSize.height/2;
//	Vec2 xContentSize = _popUp->getContentSize().width/2;
//	Vec2 yContentSize = _popUp->getContentSize().height/2;

	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;
	}
}

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);
	CCLOG("removed child");

}

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();
}

@iQD could you help … !! :smile:

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?

onTouchBegan() returns a true value which tells cocos2d-x that yes a touch is encountered.

so, i have put the condition inside the method to return a true value for touch only when the touch points are within the HelloWorld image.

see the screenshot below -

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.

void HelloWorld::onTouchMoved(cocos2d::Touch *touch, cocos2d::Event *event)
{
	CCLOG("Touch Moved");

	Size visibleSize = Director::getInstance()->getVisibleSize();
	Vec2 origin = Director::getInstance()->getVisibleOrigin();

	if(this->getChildByTag(1) != nullptr)
	{
		this->removeChildByTag(1, true);
		CCLOG("removed child");
	}

}

but the crash still exists…

instead of this use a flag.
Let say there is a flag with default value 0 and
then your if statement would be

if(flag==0)
{
this->removeChildByTag(1, true);
flag=1;
CCLOG(“removed child”);
}
Now, check whether it works…

How can it be that it doesnot work!! :smiley:

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 :smiley:

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.

Thanks @catch_up

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.

And I guess, it means that it did not work fine!!

Can you show your final code?

Also AFAIK… if you remove an object then the event listeners on it are also removed automatically…

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();
}

Yeah, that is why I was asking you to use flags in first place :smile:

Great that it worked… From now on start using flags :smiley: :smiley:
I still wonder whether you realized or you already know that you have 2 layers in your scene :smiley:

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.

yup, flags seemed pretty helpful.