(博主qq,1204802552,欢迎交流)
有时候,我们想在Sequence或者Spawn中加入RepeatForever的动作,但在实际运行中,该循环动作并未得到执行,我们先来说下解决的方法。
对于Spawn,很简单,我们只需要把RepeatForever动作拿出来单独执行即可
Animation *animation = Animation::createWithSpriteFrames(aniframe,0.1f);
Animate *animate = Animate::create(animation);
aniSprite->runAction(RepeatForever::create((ActionInterval *)animate));
aniSprite->runAction(MoveTo::create(5,PointMake(visibleSize.width-40, visibleSize.height/2)));
对于Sequence,则要使用回调函数来执行RepeatForever动作
CallFunc *callfunc = CallFunc::create(this,callfunc_selector(HelloWorld::animateCallback));
Action * seq = Sequence::create(MoveTo::create(5,PointMake(visibleSize.width-40, visibleSize.height/2)),callfunc,NULL);aniSprite->runAction(seq);
下面分析下具体原因:Spawn和Sequence都采用递归的创建方式
我们先来看看Spawn的创建源码:Spawn有不同形式的Create函数,但最终它们都会调用CreateWithTwoActions,所以我们直接看该函数
Spawn* Spawn::createWithTwoActions(FiniteTimeAction *action1, FiniteTimeAction *action2)
{
Spawn *spawn = new Spawn();
spawn->initWithTwoActions(action1, action2);
spawn->autorelease();
return spawn;
}
bool Spawn::initWithTwoActions(FiniteTimeAction *action1, FiniteTimeAction *action2)
{
CCASSERT(action1 != nullptr, "");
CCASSERT(action2 != nullptr, "");
bool ret = false;
float d1 = action1->getDuration();
float d2 = action2->getDuration();
if (ActionInterval::initWithDuration(MAX(d1, d2)))
{
_one = action1;
_two = action2;
if (d1 > d2)
{
_two = Sequence::createWithTwoActions(action2, DelayTime::create(d1 - d2));
}
else if (d1 < d2)
{
_one = Sequence::createWithTwoActions(action1, DelayTime::create(d2 - d1));
}
_one->retain();
_two->retain();
ret = true;
}
return ret;
}
从上面的代码中可以看到,Spawn为了同步不同的动作,会调用Sequence,即它最终执行的其实是由Sequence创建的动作,那么下面我们就来看看Sequence的创建和执行
bool Sequence::initWithTwoActions(FiniteTimeAction *actionOne, FiniteTimeAction *actionTwo)
{
CCASSERT(actionOne != nullptr, "");
CCASSERT(actionTwo != nullptr, "");
float d = actionOne->getDuration() + actionTwo->getDuration();
ActionInterval::initWithDuration(d);
_actions[0] = actionOne;
actionOne->retain();
_actions[1] = actionTwo;
actionTwo->retain();
return true;
}
从上面这段代码可以看出,一个Sequence动作包含了两个子动作,它的duration为两个动作的总和。
void Sequence::startWithTarget(Node *target)
{
ActionInterval::startWithTarget(target);
_split = _actions[0]->getDuration() / _duration;
_last = -1;
}
void Sequence::update(float t)
{
int found = 0;
float new_t = 0.0f;
if( t < _split ) {
// action[0]
found = 0;
if( _split != 0 )
new_t = t / _split;
else
new_t = 1;
} else {
// action[1]
found = 1;
if ( _split == 1 )
new_t = 1;
else
new_t = (t-_split) / (1 - _split );
}
if ( found==1 ) {
if( _last == -1 ) {
// action[0] was skipped, execute it.
_actions[0]->startWithTarget(_target);
_actions[0]->update(1.0f);
_actions[0]->stop();
}
else if( _last == 0 )
{
// switching to action 1. stop action 0.
_actions[0]->update(1.0f);
_actions[0]->stop();
}
}
else if(found==0 && _last==1 )
{
// Reverse mode ?
// XXX: Bug. this case doesn't contemplate when _last==-1, found=0 and in "reverse mode"
// since it will require a hack to know if an action is on reverse mode or not.
// "step" should be overriden, and the "reverseMode" value propagated to inner Sequences.
_actions[1]->update(0);
_actions[1]->stop();
}
// Last action found and it is done.
if( found == _last && _actions[found]->isDone() )
{
return;
}
// Last action found and it is done
if( found != _last )
{
_actions[found]->startWithTarget(_target);
}
_actions[found]->update(new_t);
_last = found;
}
从上面两段代码可以理解到:Sequence根据子动作的duration所占比例以及运行时间来判断哪个动作需要得到执行。对于普通的两个动作,他们都可以得到执行。。。。。。。。但是,一个RepeatForever动作的duration为0,也就是说其所占运行时间的比例为0,即是不会运行,这个可以从Update的前段代码可以看出,这个分析结果也符合我们看到的实际运行效果。
但是,我们来看看红色的那部分代码。假如我们把RepeatForever动作当做参数1传入,当运行的时候,该动作被跳过了,于是红色部分的代码得到执行,经过调试也确实如此。
我们看下注释:// action[0] was skipped, execute it. ----------excute可以翻译为执行,也可以翻译为处死!!从代码看,action [ 0 ]在启动之后马上被结束,所以注释中的excute极有可能是处死的意思。于是我尝试改造代码,不调用stop,并向update传入0.0f,我想要看到的效果是:RepeatForever动作得到执行,但是实际运行效果并没有发生变化,令我百思不得其解,也许我了解的还不够深入有什么遗漏了,待我找个时间再研究研究,也希望各位能指点迷津