How to get absolute positions of touches and layer objects?

Hi there,

I’m having a weird issue when trying to detect which character (currently a CCLayer-extending object) I’m touching. The problem is that the location of the sprite I’m clicking on never matches the touch location that is registered.

I’ve tried different conversion functions but neither of them seem to work.

Any idea about how can I detect in ccTouchesBegan where a CCLayer is being touched? How can I get the absolute position in the map of the touch poisition as I receive it (I will move the character to the clicked position)?

I know that they may be very basic questions, but I’ve been looking for the answer for some hours and I can’t find the solution.

Thanks in advance!

This is my current code and still not working.

bool VMap::ccTouchBegan(cocos2d::CCTouch *touch, cocos2d::CCEvent *pEvent) {

    // Get the touch point
    CCPoint touchLocation = this->convertTouchToNodeSpace(touch);
    CCLOG("Touch: %f %f", touchLocation.x, touchLocation.y);

    // Selecting a unit
    vector::iterator unitIterator;
    NUnit *unit;
    for(unitIterator = units.begin(); unitIterator != units.end(); ++unitIterator) {
        unit = *unitIterator;
        CCPoint unitLocation = this->convertToNodeSpace(unit->getPosition());
        CCLOG("Unit: %f %f", unitLocation.x, unitLocation.y);

        CCRect rect = CCRectMake(0,0, unit->getContentSize().width, unit->getContentSize().height);     
        if (rect.containsPoint(unitLocation)) {
            //CCLOG("Unit selected : %f %f", unitLocation.x, unitLocation.y);
        }
    }
}

touchLocation gives me the touch position relative to the lower left screen corner. I need to get the position relative to the map origin.
unitLocation should be the unit position also relative to the map origin, so I can compare and perform some calculations in order to select and move.

I’ve played around with node parents and different space conversions without success… Any idea about how to get it working?

You have to convert your touchLocation into map space. Then it should work.

Yep, but how can I do that? Which is the function to perform such conversion?

Map->convertToNodeSpace

I’m already doing it (first line in the method):

@
CCPoint touchLocation = this->convertTouchToNodeSpace(touch);
@

I do:

 location = touch->getLocationInView();

        location = cocos2d::CCDirector::sharedDirector()->convertToGL(location);

I get the same result: the relative position to the lower left corner of the screen ó_Ô

Could it be some issue related to node parenting?

Well, that is where CCLayers start, an anchor point of (0,0)

You can change that to some other quadrant or middle if that is where you want to start.

Do I need to change the anchor point?

Currently I have a Map (extends CCLayer). This has a CCLayer child that manages pan and zoom (anchor point set to 0,0), as it only shows a part of the whole map. And this ‘pan and zoom’ layer has many CCLayer children (actual characters).

What role plays anchor points if I just want to get where the characters are positioned and if they are being clicked?

oh, I misunderstood that. Anchor point would not play a role in this.

So what I do is get the touch, like I showed above, and I check the bounding box of my sprites to see if the touch point falls within the bounding box of any of them. If it does, it was clicked/touched. I then act upon it.

Yes, and that’s what (I think) I do:

bool VMap::ccTouchBegan(cocos2d::CCTouch *touch, cocos2d::CCEvent *pEvent) {

    CCPoint touchLocation = touch->getLocationInView();
    touchLocation = CCDirector::sharedDirector()->convertToGL(touchLocation);

    vector::iterator unitIterator;
    NUnit *unit;
    for(unitIterator = units.begin(); unitIterator != units.end(); ++unitIterator) {
        unit = *unitIterator;

        if (unit->boundingBox().containsPoint(touchLocation)) {
            // Altough I'm clicking inside the bounding box 
            // of a character (rendered through debug mode)
            // I never get into this code block     
        }
    }

    return true;
}

But while the characters (units) calculate their bounding box according to the map layer position, the touch point is in screen coordinates. And I’ve tried to get characters and touch locations all in the same space but I can’t get it working U_U

Did you resolve this?

simply use CCNode::convertToNodeSpace (or CCNode::convertToWorldSpace), it’s pretty straightforward

Can you show us an example? Because doing this doesn’t work.

CCPoint touchLocation = touch->getLocationInView();
touchLocation = CCDirector::sharedDirector()->convertToGL( touchLocation );
touchLocation=convertToNodeSpace(touchLocation);
CCLOG(" TouchLocation X=%f           TouchLocation Y=%f",touchLocation.x,touchLocation.y);
        
if(rect.containsPoint(touchLocation))
{
     // do something
}

I know this is an old post but I ran into a similar issue. If you use Node::getBoundingBox() the documentation indicates it returns the coordinates in terms of the parent’s coordinate system, so when converting to node space must do it on the parent. Also Touch::getLocation returns the point in GL coordinates already so can skip the GL conversion step (perhaps this function was not available back at time of the original post. I am using cocos2dx 3.13 currently)

example:

bool PlayerNode::hitTest(const std::vector<cocos2d::Touch*>& touches)
{
    if(!this->getParent())
    {
        return false;
    }

    const auto& boundingBox = this->getBoundingBox();
    
    for(const auto& touch : touches)
    {
        const auto& touchLocation = this->getParent()->convertToNodeSpace(touch->getLocation());
        
        if(boundingBox.containsPoint(touchLocation))
        {
            return true;
        }
    }
    return false;
}