cocos2dx 3.2 中Sequence和Spawn无法执行RepeatForever动作的问题解决

   (博主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动作得到执行,但是实际运行效果并没有发生变化,令我百思不得其解,也许我了解的还不够深入有什么遗漏了,待我找个时间再研究研究,也希望各位能指点迷津

你可能感兴趣的:(cocos2d)