Created many CCSprits but when triggering ccTouchBegan gives the last one allways

UPDATE more simple example :
Im using cocos2d-2.1rc0-x-2.1.3 , and i have something strange i created 10 CCSprites. i have class that extend CCSprite and CCTargetedTouchDelegate like this :

Ok , now i really puzzled i simplified the class as as i was reading on the net the suggestion was to extend CCNode better then CCSprite
and keep it as member of the CCNode
so here is the very simpleminded example based on the Hello cpp.
the problem remine the same , when touching any of the Gem instances i get printed the last Gem added , why ??
i expect that each Touchwill give me the correct instance that have bean touched ( i print id and name )
Gem.cpp

#include "Gem.h"
Gem::Gem()
{

   ;

}
Gem::~Gem()
{
    ;
}

void Gem::onEnter()
{

    CCDirector* pDirector = CCDirector::sharedDirector();
    pDirector->getTouchDispatcher()->addTargetedDelegate(this, 0, true);
    CCNode::onEnter();

}
void Gem::onExit()
{  
    CCDirector* pDirector = CCDirector::sharedDirector();
    pDirector->getTouchDispatcher()->removeDelegate(this);
    CCNode::onExit();    
} 
bool Gem::ccTouchBegan(CCTouch* touch, CCEvent* event)
{       
    CCPoint touchPoint = touch->getLocation();   
    CCLOG("Gem Touched! ImageName:%s |GemId:%s  x:%f ,y:%f myspriteX:%f, myspriteY:%f nodePosX:%f nodePosY:%f",this->getImageName().c_str(),this->getGemId().c_str(),touchPoint.x,touchPoint.y,getGemSprite()->getPositionX(),getGemSprite()->getPositionY(),this->getPositionX(),this->getPositionY());
    return true;
}
void Gem::ccTouchMoved(CCTouch* touch, CCEvent* event)
{
    CCPoint touchPoint = touch->getLocation();  
}
void Gem::ccTouchEnded(CCTouch* touch, CCEvent* event)
{

}

Gem.h

class Gem :public CCNode , public CCTargetedTouchDelegate 
{
public:
    Gem();

    virtual ~Gem();

    CC_SYNTHESIZE(std::string,imageName,ImageName)
    CC_SYNTHESIZE(std::string,gemId,GemId)
    CC_SYNTHESIZE(CCPoint,gemPos,GemPos)
    CC_SYNTHESIZE(CCSprite*,gemSprite,GemSprite)

    virtual void onEnter();
    virtual void onExit();
    virtual bool ccTouchBegan(CCTouch* touch, CCEvent* event);
    virtual void ccTouchMoved(CCTouch* touch, CCEvent* event);
    virtual void ccTouchEnded(CCTouch* touch, CCEvent* event);


};

The helloWorldScene.cpp init() method

bool HelloWorld::init()
{
       bool bRet = false;
        //////////////////////////////////////////////////////////////////////////
        // super init first
        //////////////////////////////////////////////////////////////////////////

       if(!CCLayer::init())
        return false;

        CCSize m_winSize;
        CCSize visibleSize;
        CCPoint origin;
        m_winSize = CCDirector::sharedDirector()->getWinSize();
        visibleSize = CCDirector::sharedDirector()->getVisibleSize();
        origin = CCDirector::sharedDirector()->getVisibleOrigin();

        CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("sprites.plist","sprites.png");
        CCSpriteBatchNode* gameBatchNode  = CCSpriteBatchNode::create("sprites.png"/*,200*/);
        CCSprite *bg= CCSprite::create("grideFinal.png");
        //bg->setAnchorPoint(ccp(0,0));
        bg->setPosition(ccp(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
        this->addChild(bg,1);

        Gem* gem1 = new Gem();
        gem1->retain();
        gem1->setGemId("gem1");
        gem1->setImageName("img_1");
        gem1->setGemSprite(CCSprite::createWithSpriteFrameName("gem1.png"));
        Gem* gem2 = new Gem();
        gem2->retain();
        gem2->setGemId("gem2");
        gem2->setImageName("img_2");
        gem2->setGemSprite(CCSprite::createWithSpriteFrameName("gem2.png"));

        gem1->setAnchorPoint(ccp(0,0));
        gem2->setAnchorPoint(ccp(0,0));

        gem1->setPosition(ccp(0,0));
        gem2->setPosition(ccp(gem1->getGemSprite()->getContentSize().width,40));

        gem1->getGemSprite()->setAnchorPoint(ccp(0,0));
        gem2->getGemSprite()->setAnchorPoint(ccp(0,0));
        gem1->getGemSprite()->setPosition(ccp(0,0));
        gem2->getGemSprite()->setPosition(ccp(gem1->getGemSprite()->getContentSize().width,40));
        //gameBatchNode->addChild(gem1->getGemSprite(),4,44);
        //gameBatchNode->addChild(gem2->getGemSprite(),4,45);
        this->addChild(gameBatchNode);

        bg->addChild(gem1->getGemSprite(),50);
        bg->addChild(gem2->getGemSprite(),50);
        bg->addChild(gem1,50);
        bg->addChild(gem2,50);

        bRet = true;


    return bRet;
}

Every thing is working fine except when i touch the screen and trigger Gem::ccTouchBegan method. its allways gives me the last CCSprite ( and i have like 50 on the screen why is that ? what im missing here ?

There’s

@ virtual void ccTouchesBegan(Set* touches, Event* event)@

from which you may use:

@ cocos2d::Touch* touch = (cocos2d::Touch*)( touches->anyObject() );@

or use iterator:
http://www.cocos2d-x.org/reference/native-cpp/d6/d8c/classcocos2d_1_1_set.html

Hey and thanks for the response ,
what you saying i need to use set of tuches ? (Set* touches)
why i can’t use the single touch ?
Gem::ccTouchBegan(CCTouch* touch, CCEvent* event)

someone please?

why i can’t use the single touch ?

You can, anyObject() would give you the single, first, touch.

mybe i wasn’t clear or understood but i do get the touches triggered and i to get into ccTouchBegan(CCTouch* touch, CCEvent* event);
the problem is that it returned me always the instance of the last CCSprite i added to the CCLayer

You could try checking first if the touchpoint is within the rect of the sprite…
Like,
@
if ( spriterect().containsPoint(convertTouchToNodeSpaceAR(touchpoint)) ){

CCLog(“Gem touched”);

}
@

Thanks Rick for the fast replay on this frustrating subject ,
befor im checking if its in the rect , when i click the Get sprite im getting it printing the last sprite i added no mater what .

the debug pointer when its get to this function :

bool Gem::ccTouchBegan(CCTouch* touch, CCEvent* event)
    {   
            CCPoint touchPoint = touch->getLocation();
            CCLOG("Gem Touched! %s %s  x:%f ,y:%f myspriteX:%f, myspriteY:%f",this->getImageName().c_str(),this->getGemId().c_str(),touchPoint.x,touchPoint.y,this->getPositionX(),this->getPositionY());

        return true;
    }

no matter how many different sprite’s from the Gem instances from the Gem class created , every time it prints the last Gem added ,
to i miss here something in the logic of the cocos2d -x framework ?

please see the updated question

If you move declaration and implementation of ccTouchBegan/Moved/Ended to HelloWorld, and add

@ this->setTouchEnabled(true);@

to HelloWorld::init(), then you would get actual position of the touch and you may call method according to Gem position.

Thanks Krunoslav for your replay .
i really want to understand many things are not clear to me regarding this .
in the CppTest directory in TouchesTest example , there is possibility to extend the CCSprite and make it touchable but this is only when i use Texture2D , and i want to use gameBatchNode as recommended any where on the net.
BUT if its not possible i want to know . its confusion
any way regarding your solution , how can i get the Gem instance only by X,Y?

Remove this from Gem, Put it in HelloWorld
virtual bool ccTouchBegan(CCTouch* touch, CCEvent* event); virtual void ccTouchMoved(CCTouch* touch, CCEvent* event); virtual void ccTouchEnded(CCTouch* touch, CCEvent* event);

Add this on Gem
Letter* Gem::objectWithTexture(CCTexture2D* aTexture){ Gem* mygem= new Gem(); mygem->initWithTexture( aTexture ); mygem->autorelease(); return mygem; } bool Gem::initWithTexture(CCTexture2D* aTexture){ CCSprite::initWithTexture(aTexture); return true; } bool Gem::containsTouchLocation(CCTouch* touch){ return rect().containsPoint(convertTouchToNodeSpaceAR(touch)); }

make your ccTouchBegan ‘void’ not ‘bool’

initializing gem in HelloWorld
CCTexture2D* texture = CCTextureCache::sharedTextureCache()->addImage("gem_sprite.png"); Gem* gemm= Gem::objectWithTexture(texture); gemm->setPosition( ccp(x,y) ); layer->addChild(gemm, 1, gemtag);

Checking gems in HelloWorld
void HelloWorld::ccTouchesBegan(CCSet* touches, CCEvent* event){ CCTouch* touch = (CCTouch*)(* touches->begin()); CCPoint pos = touch->getLocation(); CCObject * obj; CCARRAY_FOREACH(layer->getChildren(), obj) { CCNode * n = (CCNode *)obj; Gem *gemm = (Gem *)n; gemm->used = false; if (gemm->containsTouchLocation(touch)){ CCLog("Gem %i touched", gemm->getTag()); }}}

That’s how I do it.
Hope this helps.

Thanks allot for your help , i know this solution , is this the only thing that will work ?
few questions :

  1. why i can’t use CCSpriteBatchNode , its always gives problems , like when you try to add to the CCSpriteBatchNode object that extend CCSPrite its yeled for error that its supporting only CCSprits
  2. why i can’t i use the “class Gem :public CCSprite , public CCTargetedTouchDelegate” and have each class its own :

virtual bool ccTouchBegan(CCTouch* touch, CCEvent* event);
virtual void ccTouchMoved(CCTouch* touch, CCEvent* event);
virtual void ccTouchEnded(CCTouch* touch, CCEvent* event);

i don’t what to loop 20 - 30 - 40 objects each time i want to detect Touch .

  1. and what with this BUG or something else i didn’t figure that its the main question , why i can only get the last object that has bean added

i don’t what to loop 20 - 30 - 40 objects each time i want to detect Touch .

  1. and what with this BUG or something else i didn’t figure that its the main question , why i can only get the last object that has bean added

You got last after all of previous have been looped…

A) Correct that void/bool error
b) Try to do like @Rick suggested, when you’d have working example you may try to fix it for your need

I haven’t tried CCSpriteBatchNode. Maybe you could try changing the object to CCSpriteBatchNode here in

Letter* Gem::objectWithTexture(CCTexture2D* aTexture){
   Gem* mygem= new Gem();
   mygem->initWithTexture( aTexture );
   mygem->autorelease();
  return mygem;
}
bool Gem::initWithTexture(CCTexture2D* aTexture){
   CCSprite::initWithTexture(aTexture);
   return true;
}

well, you could just put a
break;
in the loop when a gem is touched so the loop stops.

If you are to add the objects its own ‘touches’ events
As soon as a touch event is triggered, all objects will run their own triggered touch event function
and still check if touch point is within the object rect.
It’s still like you looped through all objects.
That’s what I’ve tried before in the Touches Test.

Thank you very much on your answers i guess i will move touch methods to the controller class like you suggested , wow this cost me few days of work .

You are swallowing the touches.

pDirector->getTouchDispatcher()->addTargetedDelegate(this, 0, true); 

However each touch is returning true in the touch began method which will swallow it because you have signed up to swallow touches.

You want sprites to only respond to touches when they are touched and not swallow the touch since you want to be notified if multiple sprites are touched.

Thus

pDirector->getTouchDispatcher()->addTargetedDelegate(this, 0, false);

on your TouchBegan you should be returning false on any touch that is outside of the touchable area.

Thus you need something like

if (touchPoint is inside sprite)
{
   return true; //This touch will call moved/ended however we asked it not to swallow when we signed up so the touch will propagate down to the next guy.
}
else
{
   return false; //This touch is ignored and will not call moved/ended
}

also you will probably want to use convertToNodeSpace(touchPoint) the coordinates of your touches are in window space.

just saw it , thanks allot for taking the time to answer !
i will test it

Hi, I’m new in this forum and also to cocos2dx. I have the same problem, but for me, the touch does not trigger at all. I would want to understand more about touch triggers and event. I running cocos2dx on windows and convert it to C++. What could be the possible reason why my touch triggers does not work?

Pardon me, but I don’t see any calls to the CCTargetedTouchDelegate methods, is there anything else that needs to be done to start that? I have added the line:

this->setTouchEnabled(true);

then I create a Gem as above and it is displayed properly. I have set breakpoints on all the overridden CCTargetedTouchDelegate methods in Gem.cpp and none of them are being called when I click on my iPhone emulator. Is there something else that needs to be done to initiate the process?


To make sure, the pseudo code is:

Sprite is created
Touch Listener is attached to Sprite
Loop Begins
On Touch go through list of all Touch Listeners called
Upon first match (return true)
Stop
Loop Ends