[已解决]ui编辑器的动画释放失败

来自未来的注释begin:

问题已不完美解决,解决方案参考3楼和5楼。
来自未来的注释end。

编辑器版本:V1.3.0.1
Cocos2d-x版本:V3.0RC1

当时是这样的:
我很开心地使用UI编辑器生成的UI,并且播放它的动画:

/* 加载UI */ 
auto UI = cocostudio::GUIReader::getInstance()->widgetFromJsonFile("DNSMGameWinUI_1.ExportJson"); 
this->addChild(UI); 

//播放动画Animation 
cocostudio::ActionManagerEx::getInstance()->playActionByName("DNSMGameWinUI_1.ExportJson", "Animation"); 

播放动画是正常的(顺便一提,UI编辑器的这个功能很赞,很省事~)
如图(好吧,图片是不会动的,大家脑补一下):

然后,点击下一关,当然就切换场景了,切换场景之后,再次出现这个UI并播放动画的时候,就报错了,中断的地方是CCActionNode.cpp的getActionNode函数:

Node* ActionNode::getActionNode()
{
Node* cNode = dynamic_cast<Node*>(_object);
if (cNode != nullptr)
{
return cNode;
}
else
{
Widget* rootWidget = dynamic_cast<Widget*>(_object);
if (rootWidget != nullptr)
{
return rootWidget;
}
}
return nullptr;
}

此时函数中的_object已经是一个不能正常使用的对象了
发现是因为上一次的动画对象并没有释放掉(但是已经不能使用了),ActionManagerEx仍然保留了它的引用(存放在Map中)
于是我再次播放动画的时候,依旧是播放上一次的动画对象,所以就报错了。

我试着这样手动释放所有动画对象:cocostudio::ActionManagerEx::getInstance()->releaseActions();
但是我失败了(不然我也不会在这里了~)

于是,我的疑问是,UI的动画需要我们手动释放吗?或者说,UI本身就需要我们手动释放不?
还是说,这是一个bug?(希望是我的操作问题:10:

获得动画对像->保留->播放。

参考: http://k57box.blog.163.com/blog/static/142261374201302934218614/

你的意思是UI的动画要保留起来,每次都调用同一个对象来播放?

版主给的参考链接和我的问题没有关系,我的问题是,UI的动画没有释放成功,需不需要我们手动释放?

那, 我再用另一个方式描述一遍,看看能不能表达清楚~
我是这样的操作过程:进入一个场景->进去其他操作(就是玩游戏的过程)->游戏胜利->加载UI文件->播放UI动画->进入下一个场景->进去其他操作(就是玩游戏的过程)->游戏胜利->加载UI->播放UI动画->报错

也就是说,在切换场景之后,UI的动画对象没有释放成功,具体表现在:

void ActionManagerEx::initWithDictionary(const char* jsonName,const rapidjson::Value &dic, Ref* root)
{
std::string path = jsonName;
ssize_t pos = path.find_last_of("/");
std::string fileName = path.substr(pos+1,path.length());
CCLOG(“filename == %s”,fileName.c_str());
cocos2d::Vector<ActionObject*> actionList;
int actionCount = DICTOOL->getArrayCount_json(dic, “actionlist”);
for (int i=0; i<actionCount; i++) {
ActionObject* action = new ActionObject();
action->autorelease();
const rapidjson::Value &actionDic = DICTOOL->getDictionaryFromArray_json(dic, “actionlist”, i);
action->initWithDictionary(actionDic,root);
actionList.pushBack(action);
}

_actionDic.insert(std::pair<std::string, cocos2d::Vector<ActionObject*>>(fileName, actionList));

}
这个函数是加载UI文件的时候会调用的,生成动画对象,缓存起来。
在第二次场景加载UI的时候,我发现_actionDic已经缓存了上一次的UI动画对象,并没有删除掉,所以我第二次的动画对象没法正常缓存进去~

我需要怎么做才能释放UI的动画对象呢?我并没有找到相应的函数(除了releaseActions)

遇过一样的问题,其实解决方法很坑……QAQ

UI编辑器的动画在切换场景后要释放 cocostudio::ActionObject 的话,其实就诚如楼主在一楼最底下写的,真的是在切换场景时呼叫 cocostudio::ActionManagerEx::releaseActions() 无误。

但是!

正在播放中的 cocostudio::ActionObject 请别忘了让他停下来……:904:

如果是循环动画,那还好解决,就离开时把 playActionByName() 回传的 cocostudio::ActionObject 呼叫 stop() 就成了。

那如是非循环动画,那请记得加上 cocos2d::CallFunc 参数,在结束时请他停下来就成了。

BUT!由于目前项目都是在 lua 底下进行的,故仅试过在 lua 下执行以上行为,c++ 的就不确定了。

另外,ActionManagerEx::playActionByName(json, actionName, func) 这种带结束呼叫的在 lua 下呼叫真的是坑到极点……

因为 cocos2d::CallFunc 物件在 lua 环境下会产生 cocos2d::LuaCallFunc 物件来绑定 lua 函数,但是,产生出来的 cocos2d::LuaCallFunc 物件却因为没有加入任何节点下,所以在下一帧就会被移除。

所以,不能写成:

ccs.ActionManagerEx:getInstance():playActionByName("1.json", "start", cc.CallFunc:create(myCallback)) 

写成这样必定当机!

而必须屈就把 cocos2d::CallFunc 物件独立拉出来,自行保管:

callFuncObj = cc.CallFunc:create(myCallback) 
callFuncObj:retain() 
ccs.ActionManagerEx:getInstance():playActionByName("1.json", "someAnim", callFuncObj) 

最最惨的是,由于UI编辑器产生的 cocostudio::ActionObject 不会自行消灭,所以在动画结束后还是会一直被呼叫更新。循环动画也就算了,彷佛每次到了动画结尾被呼叫一次还貌似挺科学的。

那非循环动画!?

在 callback 函数被呼叫时没有 stop() 让动画停下来的话,那就会每一帧呼叫 callback 函数一次!

那要是把 callback 物件释放了(:release())的话呢?那就等着崩溃吧……

UI编辑器的动画实验性质很重,比不上 Armature 来的高大全,要使用的话请多留意些……T___T

原来如此…终于遇到同病相怜的人了:10:
谢谢,很详细的回答~马上去试试能不能解决~

再次感谢3楼~已经解决了,虽然和你说的稍微有点不一样,但也差不多了,我的解决方法如下:

  1. 播放UI动画的方式还是一样,调用playActionByName函数
  2. playActionByName函数会返回ActionObject对象
  3. 只要在离开(或关闭)这个UI的时候,调用ActionObject的stop函数就可以了(不需要使用CallFunc参数)。
  4. 在当前场景的onExit函数中,调用:
    ActionManagerEx::destroyInstance(); (当然还有其他Cocostudio单例类的destoryIntance函数,但是和这个问题无关~忽略)
  5. 然后就OK了,已经不会报错了,主要是一定要调用stop函数
  6. 我的是非循环动画,循环动画没有测试过~但就源码来看,是一样的规则~

PS:这个怎么说,应该也算是一个小坑,不知道有没有其他人反馈过~版主要不要记录一下~:7:

我也出现这种情况,每次切换场景都调用ActionManagerEx::destroyInstance(); 好麻烦,2dx不能智能一点去判断吗?我搞好久,还以为有bug,release 会修复这个问题吗?

我也不知道为什么,循环动画播放到一半就消失了。非循环的动画会不断的闪烁

— Begin quote from ____

引用第5楼笨木头于2014-04-12 08:44发表的 回 3楼(saintliao) 的帖子 :
再次感谢3楼~已经解决了,虽然和你说的稍微有点不一样,但也差不多了,我的解决方法如下:

  1. 播放UI动画的方式还是一样,调用playActionByName函数
  2. playActionByName函数会返回ActionObject对象
  3. 只要在离开(或关闭)这个UI的时候,调用ActionObject的stop函数就可以了(不需要使用CallFunc参数)。
    http://www.cocoachina.com/bbs/job.php?action=topost&tid=197713&pid=925085

— End quote

蛋疼的cocos2dx。代码质量堪忧啊。。
其实解决的办法不用那么麻烦,你们看看这一个函数
void ActionManagerEx::releaseActions()
{
std::unordered_map<std::string, cocos2d::Vector<ActionObject*>>::iterator iter;
for (iter = _actionDic.begin(); iter != _actionDic.end(); iter++)
{
cocos2d::Vector<ActionObject*> objList = iter->second;
– 下边几行是我加的
for (auto pObj : objList)
{
pObj->stop();
}
objList.clear();
}

_actionDic.clear();

}

PS.其实他倒也不用那么敝帚自珍。写个编辑器还得藏起来。做好平台,做渠道商坐地赚钱才是硬道理。

这个问题我出现很长时间了,一直没有解决,终于看到了这个帖子,算是解决了。
但有一点需要注意,如果UI动画是放在某按键的回调函数中,即只有按下按键才会播放UI动画时,当进入这个场景,但没有按按键,也就是没有激活UI动画,而是直接退出,这样的话程序会报错,原因是你在析构函数中stop了一个没有触发的UI动画。
如果这样的话,解决方案有两个,一个是把需要播放的UI动画在init() 中定义,比如_action_open = ActionManagerEx::getInstance()->getActionByName(“Ui_MainMenu_v1.1.1.json”, “Animation_open”);这样的话,即便进入场景但没有触发UI动画,程序依然不会报错;第二个方案是把如上的getActionByName的定义放在构造函数中。这两种方法实质相同,结果也相同,这样析构函数中的stop就不会引起程序报错了。

又发现一个问题,我的stop()和ActionManagerEx::destroyInstance()是放在析构函数中的,当相互切换的两个场景都有UI动画时,程序报错,即便已经在两个场景中都分别写了stop和ActionManagerEx::destroyInstance()…
尝试了很多种方法,最后,把stop()和ActionManagerEx::destroyInstance()放在负责场景切换的回调函数中的场景切换语句之前,问题就解决了,程序正常运行。
为了有同类问题的人能少走点弯路,特此分享。

我按照网上的方法,重构了一遍UI动画这块,才找到这个帖子,太坑了。囧~~~~:12::12::12::12:

您好,这个方法只适用于,清除所有的动画,如果我只是想停止当前层的某个动画能?其他层的不影响,当再重新增加这个个层时需要再一次play它,我也发生了上面的崩溃。。
我是在层的初始化中play一个动画,在层的析构函数中stop他,但是第二次再进初始化,play这个动画时就有崩溃了,找不到之前的动画

我js移植到iOS也遇到这个问题了,按照你方法改了下代码,加个jsb,onExit的时候调用下,果然解决了。

http://www.cocoachina.com/bbs/read.php?tid=213201&page=1#998529

另一个解决办法 2楼

好大的坑啊

不知版主,cocos2dx 3.2 这个问题解决了吗?

这软件做的好烂啊,可以用了一半了,悲剧
真心不如自己敲代码搞定,纯浪费时间:5::5::5:

引用网友的帖子,最简单的办法就是修改void ActionManagerEx::initWithDictionary(const char* jsonName,const rapidjson::Value &dic, Ref* root)里的最后一句代码_actionDic.insert(std::pair<std::string, cocos2d::Vector<ActionObject*>>(fileName, actionList));注释掉这句改为_actionDic = actionList,这样在切换场景时stop或者release动画再次加载场景时就正常了,修改后别忘了重新生成哦!

如果写的很坑 那就是设计的不好。