Search code examples
c++c++11lambdacocos2d-x

Lambda function capture by copy results in corruped data


std::vector<Spell*> playerSpells{ new Jump(1), new Arrow() };   

for (int i = 0; i < playerSpells.size(); i++)
{
    player->addSpell(playerSpells[i]);
}

for (int i = 0; i < player->getSpells().size(); i++)
{
    auto spell = player->getSpells()[i];

    player->getSpells()[i]->icon->onClickEndEH.add([=]() {
        auto a = player->getSpells()[i];
        auto b = player->getSpells()[i]->sprite;
    });
}

First, I checkedspell variable and it's fields are initialized correctly. When the lambda function is called, the variable b is corrupted. I can provide other parts of the code if that's not enough.

I simplified it as much as I can;

//GAMEOBJECT.H
class GameObject
{
public:
    cocos2d::Sprite* sprite = nullptr;

    GameObject(const std::string fileName = "", bool clickable = false);
    ~GameObject();
};


//GAMEOBJECT.CPP
GameObject::GameObject(const std::string fileName, bool addClickEvent)
{
    if (fileName != "")
    {
        this->sprite = Sprite::create(fileName);
    }
    else
    {
        this->sprite = Sprite::create();
    }

    if (addClickEvent)
    {
        this->addTouchListener();
        this->clickable = true;
    }
}

GameObject::~GameObject()
{
    Director::getInstance()->getEventDispatcher()->removeEventListener(this->listener);
}

//SPELL.H
class Spell : public GameObject
{
public:
    GameObject* icon = nullptr;

    Spell(const std::string fileName = "", bool clickable = false);
    ~Spell();
}

//SPELL.CPP
Spell::Spell(const std::string fileName, bool clickable) : GameObject(fileName, clickable)
{
}

Spell::~Spell()
{
    delete icon;
}

//ARROW.H
class Arrow : public Spell
{
public:
    Arrow();
    ~Arrow();
};

//ARROW.CPP
Arrow::Arrow() : Spell("arrow.png")
{
    this->icon = new GameObject("arrow.png", true);
}

Solution

  • Solved it with adding retain() and release() functions. I think cocos2dx checks its pool and removes the sprite since it's not being added to the scene in the scope.

    GameObject::GameObject(const std::string fileName, bool addClickEvent)
    {
        if (fileName != "")
        {
            this->sprite = Sprite::create(fileName);
        }
        else
        {
            this->sprite = Sprite::create();
        }
    
        this->sprite->retain();
    
        if (addClickEvent)
        {
            this->addTouchListener();
            this->clickable = true;
        }
    }
    
    GameObject::~GameObject()
    {
        this->sprite->release();
        Director::getInstance()->getEventDispatcher()->removeEventListener(this->listener);
    }