Possible BUG: tilemap GID always returns 0 on Android wherever Sprite moves

I am trying to create a simple tilemap with collision detection following this link:
https://www.raywenderlich.com/2683-cocos2d-x-tile-map-tutorial-part-2 with some slight modifications.

I can see there are no collisions happening as the tileGID always returns 0 even when i cross the red meta tiles, where collidable=true. Can anyone help?

I am using Android Studio with Nexus 4 emulator. Seems like a bug.

#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"
#include "../cocos2d/cocos/deprecated/CCDeprecated.h"

USING_NS_CC;

Scene* HelloWorld::createScene()
{

    return HelloWorld::create();
}

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Scene::init() )
    {
        return false;
    }

    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
    CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadEffect("collect-coin.ogg");

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

    // add a label shows "Hello World"
    // create and initialize a label

    _tileMap = new CCTMXTiledMap();
   // _tileMap->initWithTMXFile("TileMap.tmx");
    _tileMap->initWithTMXFile("TileMap1.tmx");

    _background = _tileMap->layerNamed("Background");
    _foreground = _tileMap->layerNamed("Foreground");

    _meta = _tileMap->layerNamed("Meta");
    _meta->setVisible(true);
    this->addChild(_tileMap);

    CCTMXObjectGroup *objectGroup = _tileMap->objectGroupNamed("Objects");

    if(objectGroup == NULL){
        CCLOG("tile map has no objects object layer");
        return false;
    }
    //https://stackoverflow.com/questions/32981246/cocos2d-error-no-suitable-conversion-function-from-cocos2dvaluemap-to-cocos2d
    auto spawnPoints = objectGroup->objectNamed("SpawnPoint");

    int x = spawnPoints.at("x").asInt();
    int y = spawnPoints.at("y").asInt();

    _player = Sprite::create("Player.png");
    _player->setPosition(ccp(x,y));

    this->addChild(_player);
    this->setViewPointCenter(_player->getPosition());

    // Touch init, add boulders and event listeners
    initTouch();
    //schedule(schedule_selector(GameScene::addBoulder), 5.0f);

    //auto contactListener = EventListenerPhysicsContact::create();
    //contactListener->onContactBegin = CC_CALLBACK_1(GameScene::onContactBegan, this);
    //this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(contactListener, this);
  
    return true;
}

void HelloWorld::setViewPointCenter(CCPoint position) {

    CCSize winSize = CCDirector::sharedDirector()->getWinSize();

    int x = MAX(position.x, winSize.width/2);
    int y = MAX(position.y, winSize.height/2);
    x = MIN(x, (_tileMap->getMapSize().width * this->_tileMap->getTileSize().width) - winSize.width / 2);
    y = MIN(y, (_tileMap->getMapSize().height * _tileMap->getTileSize().height) - winSize.height/2);
    CCPoint actualPosition = ccp(x, y);

    CCPoint centerOfView = ccp(winSize.width/2, winSize.height/2);
    CCPoint viewPoint = ccpSub(centerOfView, actualPosition);
    this->setPosition(viewPoint);
}

void HelloWorld::initTouch()
{
    auto listener = EventListenerTouchOneByOne::create();
    listener -> onTouchBegan = [] (Touch* touch, Event* event) { return true;};
    listener -> onTouchEnded = CC_CALLBACK_2(HelloWorld::moveSoldier, this);
    //listener -> onTouchEnded = [=] (Touch* touch, Event* event) {};
    _eventDispatcher -> addEventListenerWithSceneGraphPriority(listener, this);
}


void HelloWorld::setPlayerPosition(CCPoint position) {
    Point tileCoord = this->tileCoordForPosition(position);
    int tileGid = _meta->tileGIDAt(tileCoord);
    int tileGid1 = _meta->tileGIDAt(CCPoint(21,10));  // Here is where collision shld happen
    CCLOG("*******************  tileGid = %d *****************",tileGid);
    CCLOG("*******************  tileGid = %d *****************",tileGid1); // Correctly returns 49
    //CCLOG("GID:, Properties:%s", _meta->getPropertiesForGID(tileGid).asValueMap()["name"].asString().c_str());
    if (tileGid)
    {
        auto properties = _tileMap->getPropertiesForGID(tileGid).asValueMap();
        if (!properties.empty()) {
            //CCString *collision = new CCString();
            auto collision = properties["Collidable"].asString();
            if ("True" == collision) {
                CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect("collect-coin.ogg");
                return;
            }
            //auto *collectible = new CCString();
            auto collectible = properties["Collectable"].asString();
            if ("True" == collectible) {
                _meta->removeTileAt(tileCoord);
                _foreground->removeTileAt(tileCoord);
                //CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect("collect-coin.ogg");
            }
        }
    }
    _player->setPosition(position);
}

void HelloWorld::moveSoldier(cocos2d::Touch *touch, cocos2d::Event *event)
{

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

    CCPoint playerPos = _player->getPosition();
    CCPoint diff = ccpSub(touchLocation, playerPos);

    if ( abs(diff.x) > abs(diff.y) ) {
        if (diff.x > 0) {
            playerPos.x += _tileMap->getTileSize().width;
         //   _player->runAction(actionTo2);
        } else {
            playerPos.x -= _tileMap->getTileSize().width;
         //   _player->runAction(actionTo1);
        }
    } else {
        if (diff.y > 0) {
            playerPos.y += _tileMap->getTileSize().height;
        } else {
            playerPos.y -= _tileMap->getTileSize().height;
        }
    }


    // safety check on the bounds of the map
    if (playerPos.x <= (_tileMap->getMapSize().width * _tileMap->getTileSize().width) &&
        playerPos.y <= (_tileMap->getMapSize().height * _tileMap->getTileSize().height) &&
        playerPos.y >= 0 &&
        playerPos.x >= 0 )
    {
        this->setPlayerPosition(playerPos);
    }
    
    this->setPlayerPosition(playerPos);
    this->setViewPointCenter(_player->getPosition());
}

CCPoint HelloWorld::tileCoordForPosition(CCPoint position)
{
    int x = position.x / _tileMap->getTileSize().width;
    int y = ((_tileMap->getMapSize().height * _tileMap->getTileSize().height) - position.y) / _tileMap->getTileSize().height;
    return ccp(x, y);
}

I could not post the tilemap here, you can check it in this post of mine: https://stackoverflow.com/questions/62284074/cocos2dx-tilemap-gid-always-returns-0-on-android-emulator-nexus-4

@slackmoehrle I am not getting any response on this post. Can you please look into it, or ask some cocos developer to suggest if it is a bug with the cocos2d-x library, and what is the corrective action I can take? Thanks.

I have solved it in an alternate manner but @slackmoehrle i am still looking for an answer as why the original approach didnt work, or if it is a bug then what is the workaround.

void HelloWorld::setPlayerPosition(CCPoint position) {

auto layer = _tileMap->layerNamed("Obstacles");
CCTMXObjectGroup *objectGroup = _tileMap->objectGroupNamed("Obstacles");
cocos2d::ValueVector array = objectGroup->getObjects();
for (cocos2d::Value &object : array)
{
    cocos2d::ValueMap myobj = object.asValueMap();
    int width = myobj["width"].asInt();
    int height = myobj["height"].asInt();

    int x = myobj["x"].asInt();
    int y = myobj["y"].asInt();

    // Below detects collision but the next commented section is more precise solution
    CCRect myRect = CCRectMake(x, y, width, height);
    if (_player->getBoundingBox().intersectsRect(myRect))
    {
        CCLOG("********* Collison ************");
        return;
    }
  /*
  // Alternately you may also have which is more precise and allows Sprite to move
  if (myRect.containsPoint(position))
    {
        CCLOG("********* Touchpoint Collison ************");
        return;
    }
   */
}

 _player->setPosition(position);
}

Are you moving over a v2 project to v3?

@slackmoehrle Thanks for the response, no it was v3 from the beginning.

C:\>cocos -v
cocos2d-x-3.17.2
Cocos Console 2.3

Thanks, I ask because you are using the old CC prefix. We haven’t used that in many many years. It still works obviously.

I asked a member of the engineering team to review this thread. If they don’t, if you send me a project I can just drop in and test I will. I have other priorities right now or I would just do it already.

1 Like

That was really helpful, thanks much @slackmoehrle. I have uploaded the code in github and shared the link with you in PM.

As per your convenience and you get time, you might git clone and use my code to test.