Question

Briefly, I have two sprites, one of which is a child of another. With each sprite related the event listener, as described there.

If both sprites are child nodes of the layer, then everything works great.

Now I need to second sprite is a child node of the first sprite. But in this case, the second sprite does not respond to events in general.

I'm in a panic and have no idea what's wrong and how to fix it. Please help.

Here's a simplified example:

.h

    #ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"

class HelloWorld : public cocos2d::Layer
{
public:
    // there's no 'id' in cpp, so we recommend returning the class instance pointer
    static cocos2d::Scene* createScene();

    // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
    virtual bool init();  

    // a selector callback
    void menuCloseCallback(cocos2d::Ref* pSender);

    // implement the "static create()" method manually
    CREATE_FUNC(HelloWorld);
};

class PlaySprite : public cocos2d::Sprite
{
public:
    virtual bool init();

    CREATE_FUNC(PlaySprite);

    void addEvent();

    bool touchBegan(cocos2d::Touch* touch, cocos2d::Event* event);
};

class InPlaySprite : public cocos2d::Sprite
{
public:
    virtual bool init();

    CREATE_FUNC(InPlaySprite);

    void addEvent();

    bool touchBegan(cocos2d::Touch* touch, cocos2d::Event* event);
};

#endif // __HELLOWORLD_SCENE_H__

.cpp

    #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()
{
    if ( !Layer::init() )
    {
        return false;
    }

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

    // add a "close" icon to exit the progress. it's an autorelease object
    auto closeItem = MenuItemImage::create(
                                           "CloseNormal.png",
                                           "CloseSelected.png",
                                           CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
    closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2 ,
                                origin.y + closeItem->getContentSize().height/2));
    auto menu = Menu::create(closeItem, NULL);
    menu->setPosition(Vec2::ZERO);
    this->addChild(menu, 1);

    /////////////////////////////
    // 3. add your codes below...


    auto sprite1 = PlaySprite::create();
    this->addChild(sprite1);

    auto sprite2 = InPlaySprite::create();

    // !!!
    sprite1->addChild(sprite2);

    return true;
}


void HelloWorld::menuCloseCallback(Ref* pSender)
{
    Director::getInstance()->end();
}


bool PlaySprite::init()
{
    if(!Sprite::init())
        return false;

    // to do

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

    this->setTexture("1.png");
    this->setPosition(visibleSize.width / 2, visibleSize.height / 2);

    this->addEvent();

    return true;
}

void PlaySprite::addEvent()
{
    auto listener = EventListenerTouchOneByOne::create();

    listener->setSwallowTouches(true);

    listener->onTouchBegan = [=](Touch* touch, Event* event)
        {
            return this->touchBegan(touch, event);
        };

    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
}

bool PlaySprite::touchBegan(Touch* touch, Event* event)
{
    auto target = (Sprite* ) event->getCurrentTarget();

    auto rect = this->getBoundingBox();

    if(rect.containsPoint(touch->getLocation()))
    {
        this->setTexture("1d.png");

        return true;
    }

    return false;
}


bool InPlaySprite::init()
{
    if(!Sprite::init())
        return false;

    // to do

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

    this->setTexture("2.png");
    this->setPosition(150.0f, 150.0f);

    this->addEvent();

    return true;
}

void InPlaySprite::addEvent()
{
    auto listener = EventListenerTouchOneByOne::create();

    listener->setSwallowTouches(true);

    listener->onTouchBegan = [=](Touch* touch, Event* event)
        {
            return this->touchBegan(touch, event);
        };

    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
}

bool InPlaySprite::touchBegan(Touch* touch, Event* event)
{
//  auto target = (Sprite* ) event->getCurrentTarget();

    auto rect = this->getBoundingBox();

    if(rect.containsPoint(touch->getLocation()))
    {
        this->setTexture("2d.png");

        return true;
    }

    return false;
}

Classes PlaySprite and InPlaySprite are are very similar because this is a very simplified example.

Was it helpful?

Solution

I think your problem is that you are swallowing touch events in the parent sprite when you call

listener->setSwallowTouches(true);

If you make that call inside of PlaySprite::addEvent() with false instead, I suspect things will work out for you.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top