【发布时间】: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<std::string, cocos2d::Animation*> 中,正如我之前所说,这段代码运行良好,没有错误。
但除了名称和对象本身之外,我还需要更多信息,因此我决定使用一个结构来存储每个动画的所有信息。我将cocos2d::Map<std::string, cocos2d::Animation*> 替换为std::vector<animData> 和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<AnimationFrame*> _frames;,问题就出来了!
就好像他们失去了cocos2d::Vector<AnimationFrame*> ref 或类似的东西。
所以我的问题是,为什么 cocos2d::Vector<AnimationFrame*> 会在我重构的代码中变为空,而不是在之前的代码中?
我通过这样的测试发现了这个:
auto test = animList[0].anim->getFrames();
if (test.empty()) {
log("empty"); //The test output empty
}
init() 函数末尾的调试器屏幕:
Player::manageIdle() 函数中的调试器屏幕:
编辑:当我在该行之后添加animations.back().anim->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->retain(); 的事实?
【问题讨论】:
-
那么在
init()之后并且没有中间调用,调用manageIdle()会产生这个错误吗?或者您是否在这两个调用之间进行其他函数调用,并且您没有披露此信息?你声称的对象是创造出来的,但不会因魔法而消失。 -
如果我在
push_back函数的push_back行之后调用manageIdle(),它运行良好。但是第二次调用,问题就出现了。因为代码执行离开了init()函数,此时有些地方出错了。这两个调用之间没有其他函数调用。你有完整的代码逻辑在这里。 -
没有足够的代码来查看流程,但
Vector<SpriteFrame*> Player::getAnimation正在返回一个向量的副本。如果Animation::createWithSpriteFrames期望该向量保持有效,那么您就有问题了,因为返回的临时值立即超出该行末尾的范围。 -
@RetiredNinja 你是对的,我编辑了我的帖子。 Olaf Dietsche 对不起,你是什么意思?
-
对不起,我的错!我错过了
class Player的第二个定义。
标签: c++ pointers vector cocos2d-x std