Different problems with a cocos::Vector<Sprite *>

Hello community, I always have had problems with this kind of object, the thing is that I am following a tutorial http://seryusjj.github.io and this guy manages vectors to pointers to subclassed Sprites as a cocos::Vector, the problem is that when you try to pass the Vector as a reference to a member function and try to copy elements from it gives a lot of kind of errors in compilation.

For example, this is the code of the function
void Player::setTargets(cocos2d::Vector<BasicEnemy *> &targets){

    for(int i; i < targets.size(); ++i)
        _targets.pushBack(targets.at(i));

    for (int i = 0; i < _numBullets; i++){
        _bulletPool.at(i)->setPlayerTargets(_targets);
        }
}

and this is the errors I get…

error: static_assert failed due to requirement 'std::is_convertible<BasicEnemy *, cocos2d::Ref *>::value' "Invalid Type for cocos2d::Vector<T>!"
        static_assert(std::is_convertible<T, Ref*>::value, "Invalid Type for cocos2d::Vector<T>!");

error: member access into incomplete type 'BasicEnemy'

Does your BasicEnemy class inherit from a cocos2d::Node or more specifically cocos2d::Ref?
Cocos Vector can only contain Ref objects or Ref subclass objects.

The assertion is implying that your BasicEnemy class is not implicitly convertible to a cocos Ref which is required for the cocos Vector.

Can you show us your BasicEnemy .h/.hpp file? There is also a potential circular dependency seen from the second error on incomplete type.

Yes, sure this is the BasicEnemy.h

#ifndef SKYHUNTERTUTORIAL_BASICENEMY_H
#define SKYHUNTERTUTORIAL_BASICENEMY_H

#include "cocos2d.h"

class Bullet;
class Player;

class BasicEnemy : public cocos2d::Sprite {

public:
    enum Animations {IDLE = 0, EXPLOSION = 1};

private:
    int SHOOT_TAG = 3;
    bool _initialized;
    cocos2d::Vector<Bullet *> _bulletPool;
    int _numBullets;
    int _bulletIndex;
    void shoot();
    void scheduleShoot();

    Player *_target;

    cocos2d::Action *_idleAnimation;
    cocos2d::Action *_explosionAnimation;
    cocos2d::Action *_shoot;

    Animations _currentAnimation;

    void createIdleAnimation();
    void createExplosionAnimation();

public:

    virtual void setParent(Node *parent);
    void setTarget(Player *target);
    void setCurrentAnimation(Animations anim);
    Animations getCurrentAnimation() { return _currentAnimation;}

    CC_SYNTHESIZE(float, _speed, Speed);
    CREATE_FUNC(BasicEnemy);
    virtual bool init();
    BasicEnemy();
    void update(float dt);
    virtual void setVisible(bool bVisible);
};

#endif //SKYHUNTERTUTORIAL_BASICENEMY_H

and this is the BasicEnemy.cpp

#include "BasicEnemy.h"
#include "Player.h"
#include "Bullet.h"

USING_NS_CC;

BasicEnemy::BasicEnemy() : _speed(100), _numBullets(10),
_bulletIndex(0), _initialized(false), _currentAnimation(IDLE),
_target(nullptr), _idleAnimation(nullptr), _explosionAnimation(nullptr),
_shoot(nullptr) {}

bool BasicEnemy::init() {

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

    for (int i = 0; i < _numBullets; i++)
        _bulletPool.pushBack(Bullet::createEnemyBullet());

    createIdleAnimation();
    createExplosionAnimation();
    runAction(_idleAnimation);

    scheduleShoot();
    scheduleUpdate();
    return true;
}

void BasicEnemy::scheduleShoot(){

    DelayTime *delayAction = DelayTime::create(1.5f);

    CallFunc *callSelectorAction = CallFunc::create(CC_CALLBACK_0(BasicEnemy::shoot, this));
    auto shootSequence = Sequence::create(delayAction, callSelectorAction, NULL);

    _shoot = RepeatForever::create(shootSequence);
    _shoot->setTag(SHOOT_TAG);
    _shoot->retain();

    runAction(_shoot);
}

void BasicEnemy::setTarget(Player* target) {

    _target = target;

    for (int i = 0; i < _numBullets; i++)
        _bulletPool.at(i)->setEnemyTarget(_target);
}

void BasicEnemy::shoot(){

    _bulletIndex = _bulletIndex % _numBullets;
    auto bullet = _bulletPool.at(_bulletIndex);
    bullet->setAnchorPoint(Point(0.5, 1));

    if (!bullet->isVisible()){
        bullet->setPosition(getPositionX(), getPositionY() - getBoundingBox().size.height * 0.5);
        bullet->setVisible(true);
    }
    _bulletIndex++;
}

void BasicEnemy::setCurrentAnimation(Animations anim){

    if (_currentAnimation == anim) return;
    _currentAnimation = anim;
    if (_currentAnimation == IDLE){
        stopActionByTag(EXPLOSION);
        runAction(_idleAnimation);
        }
    if (_currentAnimation == EXPLOSION){
        stopActionByTag(IDLE);
        runAction(_explosionAnimation);
        }
}

void BasicEnemy::setParent(Node* parent){
    Sprite::setParent(parent);
    if (!_initialized){
        for (int i = 0; i < _numBullets; i++){
            getParent()->addChild(_bulletPool.at(i));
            }
        _initialized = true;
        }

}

void BasicEnemy::createIdleAnimation() {

    Vector<SpriteFrame *> animFrames;
    auto acc = 0;

    for (int i = 0; i < 4; i++)
        {
        auto frame = SpriteFrame::create("animacion_enemigo.png", Rect(acc, 0, 50, 63));
        acc += 50;
        animFrames.pushBack(frame);
        }
    this->setSpriteFrame(animFrames.at(0));

    auto animation = Animation::createWithSpriteFrames(animFrames, 0.25f);

    auto animate = Animate::create(animation);

    _idleAnimation = RepeatForever::create(animate);

    _idleAnimation->setTag(BasicEnemy::Animations::IDLE);
}

void BasicEnemy::createExplosionAnimation() {
    Vector<SpriteFrame *> animFrames;
    auto acc = 0;
    for (int i = 0; i < 4; i++)
        {
        auto frame = SpriteFrame::create("animacion_enemigo_explotar.png", Rect(acc, 0, 50, 63));
        acc += 50;
        animFrames.pushBack(frame);
        }

    auto animation = Animation::createWithSpriteFrames(animFrames, 0.15f);

    //creamos la accion de animar el objeto animation, en este caso no necesitamos
    //un RepeatForever porque solo debe explotar una vez y parar.
    _explosionAnimation = Animate::create(animation);

    _explosionAnimation->setTag(BasicEnemy::Animations::EXPLOSION);
}

void BasicEnemy::setVisible(bool visible){
    Sprite::setVisible(visible);
    if (visible){
        runAction(_shoot);
        }
    else{
        stopActionByTag(SHOOT_TAG);
        }
}



void BasicEnemy::update(float dt){

    if (!isVisible()) return;

    if (_currentAnimation == EXPLOSION){
        stopActionByTag(SHOOT_TAG);
        if (_explosionAnimation->isDone() && isVisible()){
            setVisible(false);
            }
        return;
    }
    //para abajo
    setAnchorPoint(Point(0.5, 1));
    setPosition(getPositionX(), getPositionY() - _speed*dt);
    if (getPositionY() < 0){
        setVisible(false);
    }
}

I think you might have a circular dependency among the Player and BasicEnemy classes. I do not see your Player.h and cpp files, but I am pretty sure that is the issue. Read up a bit about circular dependency and see if this is your scenario and try fix it. That is my next guess as it looks like your BasicEnemy class does inherit from Ref which was my first guess to look at.

Hi. Just use std::vector<BasicEnemy*> and all your problem solved.

make sure you have #include "BasicEnemy.h" in Player.cpp

I checked on the classes files this guy from the tutorial http://seryusjj.github.io worked on, and compared on mines and yes, it was a circular problem. I was missing an #include in the Player.h file, after that the project have compiled perfect. THANK YOU!

So then, there is no trouble using pointers as members of a cocos2d::Vector?
Because in the past with personal projects I had this kind of troubles also, but then probably, I also had circular dependencies, and tought that cocos2d-x is a hard framework to use.

1 Like

cocos2d::Vector is a specialized container that expects pointers to objects of type cocos2d::Ref, which are reference counted objects. If you try to add any other type of object, it won’t work. It was made so it would hold Ref objects and ensure that they are not freed/destroyed until you are ready to destroy them.

When an object of type Ref is added to the vector, it calls retain() on it, which increments the reference count, and when an object is removed from the vector, it calls release(), which decrements the reference count.

2 Likes

So, then cocos2d::Vector just holds pointers to subclased Ref objects?

That’s pretty much it. If you want to store any other kind of object, then just use a std::vector. Also, nothing is stopping you from using a std::vector for cocos2d::Ref* either, but then you’ll be responsible for controlling their lifetime (through retain/release, if required).

1 Like

Thank you very much

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.