【问题标题】:Impossible to store cocos2d::Animation * in a std::vector of structure?不可能将 cocos2d::Animation * 存储在结构的 std::vector 中?
【发布时间】:2018-03-31 20:10:55
【问题描述】:

我从事一个使用 cocos2d-x 框架 (c++) 制作的项目。 在我的 Player 类中,我必须管理动画。 最初我的代码没有任何问题:

首先,动画对象是一个 cocos2d 类cocos2d::Animation。请记住,这个对象包含一个cocos2d::Vector<AnimationFrame*> _frames; 成员。 文档:http://www.cocos2d-x.org/reference/native-cpp/V3.5/d3/dc5/classcocos2d_1_1_animation.html#a0fdc0be158df7e09d04644be353db056

class Player : public cocos2d::Sprite {
private:
    cocos2d::Map<std::string, cocos2d::Animation*> animations;
    cocos2d::Vector<cocos2d::SpriteFrame*> getAnimation(const char *format, int frameStart, int count);
    void update(float delta) override;
    bool init() override;

public:
    static Player* create();
    bool init() override;
    //...
};

以及实现方面:

bool Player::init() {
    //...
    animations.insert("idleN", Animation::createWithSpriteFrames(getAnimation("%04d", 207, 9), 0.1));
    //...
}

Vector<SpriteFrame*> Player::getAnimation(const char *format, int frameStart, int count) {
    auto spriteCache = SpriteFrameCache::getInstance();
    Vector<SpriteFrame*> animFrames;
    char str[100] = {0};
    for (int i = 1; i <= count; i++)
    {
        sprintf(str, format, frameStart);
        log("%s", str);
        animFrames.pushBack(spriteCache->getSpriteFrameByName(str));
        frameStart++;
    }
    return animFrames;
}

//later in the code execution
void Player::manageIdle() {
        auto idleAnim = Animate::create(animations[0].anim);
        runAction(idleAnim);
}

您可以看到每个动画都包含在cocos2d::Map&lt;std::string, cocos2d::Animation*&gt; 中,正如我之前所说,这段代码运行良好,没有错误。


但除了名称和对象本身之外,我还需要更多信息,因此我决定使用一个结构来存储每个动画的所有信息。我将cocos2d::Map&lt;std::string, cocos2d::Animation*&gt; 替换为std::vector&lt;animData&gt;animData 作为结构。我像这样重构了代码:

class Player : public cocos2d::Sprite {
public:
    typedef struct animation {
        std::string name;
        cocos2d::Animation* anim;
        //all others info needed, not relevant here, (just several enum type variables)
    } animData;
private:
    std::vector<animData > animations;  //the new container
    //rest of code stay unchanged
};

实施方面的变化:

bool Player::init() {
    //...
    animations.push_back({"idleN", Animation::createWithSpriteFrames(getAnimation("%04d", 207, 9), 0.1)});
    //no problem here...
}

但是现在,当我尝试使用保存在我的容器(矢量)中的动画创建一个新动画时,我在这一行得到一个 SegV:

void Player::manageIdle() {
        auto idleAnim = Animate::create(animations[0].anim); //SegV here, in Animate::create() funct
        runAction(idleAnim);
}

经过搜索,发现cocos2d::Animation*类型的每个结构成员anim,现在都包含一个空的cocos2d::Vector&lt;AnimationFrame*&gt; _frames;,问题就出来了! 就好像他们失去了cocos2d::Vector&lt;AnimationFrame*&gt; ref 或类似的东西。

所以我的问题是,为什么 cocos2d::Vector&lt;AnimationFrame*&gt; 会在我重构的代码中变为空,而不是在之前的代码中?

我通过这样的测试发现了这个:

auto test = animList[0].anim->getFrames();
if (test.empty()) {
    log("empty"); //The test output empty
}

init() 函数末尾的调试器屏幕:

Player::manageIdle() 函数中的调试器屏幕:


编辑:当我在该行之后添加animations.back().anim-&gt;retain(); 以在向量中添加一个元素时,它解决了问题!

animations.push_back({"idleN", Animation::createWithSpriteFrames(getAnimation("%04d", 207, 9), 0.1)});
animations.back().anim->retain();

因为cocos2d::Animation*继承自cocos2d::Ref,所以它是一个自动释放对象。当在 cocos2d 容器内使用时,如 cocos2d::Map 或 cocos2d::Vector,它由容器本身自动管理。但是我使用 std::vector 所以我失去了我认为的参考。类似的东西。 现在我需要找到一种方法来摆脱这行额外的代码,因为这里的行数是我的两倍!

这里有一个新问题:我如何才能摆脱每次在向量中添加新元素时都必须调用 animations.back().anim-&gt;retain(); 的事实?

【问题讨论】:

  • 那么在init() 之后并且没有中间调用,调用manageIdle() 会产生这个错误吗?或者您是否在这两个调用之间进行其他函数调用,并且您没有披露此信息?你声称的对象是创造出来的,但不会因魔法而消失。
  • 如果我在push_back 函数的push_back 行之后调用manageIdle(),它运行良好。但是第二次调用,问题就出现了。因为代码执行离开了init() 函数,此时有些地方出错了。这两个调用之间没有其他函数调用。你有完整的代码逻辑在这里。
  • 没有足够的代码来查看流程,但Vector&lt;SpriteFrame*&gt; Player::getAnimation 正在返回一个向量的副本。如果Animation::createWithSpriteFrames 期望该向量保持有效,那么您就有问题了,因为返回的临时值立即超出该行末尾的范围。
  • @RetiredNinja 你是对的,我编辑了我的帖子。 Olaf Dietsche 对不起,你是什么意思?
  • 对不起,我的错!我错过了class Player的第二个定义。

标签: c++ pointers vector cocos2d-x std


【解决方案1】:

您可以围绕 Ref 创建一个包装器,它“保留”所有权,并存储此包装器,类似于 std::unique_ptr,例如

template<typename T> class RefOwner {
public:
    RefOwner(T *t) : ref(t) {
        ref->retain();
    }

    ~RefOwner() {
        ref->release();
    }

    T *operator->() { return ref; }
private:
    T *ref;
};

然后将其用作

struct animData {
    std::string name;
    RefOwner<cocos2d::Animation> anim;
    //all others info needed, not relevant here, (just several enum type variables)
};

免责声明:我对cocos2d-x没有经验,只是看了AnimationRef

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多