Can we use CCRepeatForever action with CCSequence OR CCSpawn also?

I have three action each work individually fine…
CCAnimate animateAction = CCAnimate::actionWithAnimation;//Fine
CCRepeatForever
repeatAction = CCRepeatForever::actionWithAction(animateAction);//Fine
CCFiniteTimeAction *catMove1 = CCMoveTo::actionWithDuration((ccTime) actualT, ccp(windowSize.width/4, actualY));//Fine

testSprite~~>runAction); //Working fine
testSprite~~>runAction(CCSequence::actions(catMove1, repeatAction , NULL)); //It’s Not Working, only cat move done not animate

So, Is there any issue or we cannot use CCRepeatForever action in CCSequence??
If No, then how I fullfill my requirement??

1 Like

Hi, we met the same problem and use these code to solve it:

CCSequence *gSequenceAction(CCArray *actions)
{
  CC_ASSERT(actions->count()>1);
  CCSequence *seq = CCSequence::actionOneTwo((CCFiniteTimeAction*)actions->objectAtIndex(0),
    (CCFiniteTimeAction*)actions->objectAtIndex(1));
  for (unsigned int i = 2; i < actions->count(); ++i) {
    seq = CCSequence::actionOneTwo(seq, (CCFiniteTimeAction*)actions->objectAtIndex(i));
  }
  return seq;
}

CCSequence *gSequenceAction(CCFiniteTimeAction *pAction1, CCFiniteTimeAction *pAction2, ...)
{
  va_list params;
  va_start(params, pAction2);

  CCSequence *pPrev = CCSequence::actionOneTwo(pAction1, pAction2);
  CCFiniteTimeAction *pNow = NULL;  

  while( pPrev ) {
    pNow = va_arg(params, CCFiniteTimeAction*);
    if (pNow)
    {
      pPrev = CCSequence::actionOneTwo(pPrev, pNow);
    }
    else
    {
      break;
    }
  }  
  va_end(params);
  return pPrev;
}

Hope it’s useful for you.

The orginal API allows us to create a CCSequenceAction with only ONE CCFiniteTimeAction , so it have to return a CCFiniteAction on this situation.

But it does not worth that. I hope the previous functions can be added into next release.

The orginal API allows us to create a CCSequenceAction with only ONE CCFiniteTimeAction , so it have to return a CCFiniteAction in this situation.

But it does not worth that. I hope the previous functions can be added into next release.

CCSequence means run action one by another, if you add a CCRepeatForever, than it will block other actions.

Sorry, I didn’t read John’s code carefully.

In fact what I meat is another issue:
In the real world, we always want to repeat a sequnce of actions.
But we can’t add a CCSequenceAction into CCRepeatForever, since CCSequenceAction::actions() and CCSequenceAction::actionsWithArray() return CCFiniteTimeAction**, but CCRepeatForever needs CCActionInterval**。

Thanks Zhang for your efforts,

Hi Minggo,

I used CCRepeatForever action in last argument of CCSequence, then no any other coming action would block. But it also not working.
Can you suggest me, how I resolve my problem.
My requirement is that I have to move a sprite from one point to other with animate action, but my animate action stopped in the middle of movement…I want to animate it untill it not reached on destination point? CCSPawn is also not working?/

1 Like

Try to invoke runAction two times:

sprite->runAction(CCRepeatForever);
sprite->runAction(CCMoveTo);

I don’t know if it can work, you can have a try.

Ok it work, but it not fullfill the exact requirement…. For time being I use this …anyways Thanks.

seems weird that CCSequence::actions() returns CCFiniteTimeAction, while CCSequence::actionOneTwo() returns CCSequence.

however,
a work-around which seems to work is to convert the CCFiniteTimeAction into a CCSequence like this:

            CCFiniteTimeAction* seq       = CCSequence     ::actions(a, b, c, NULL);
            CCSequence        * seq2      = CCSequence     ::actionOneTwo(seq, seq);
            CCRepeatForever   * rep       = CCRepeatForever::actionWithAction(seq2);

These are the rules regarding repeatforever and sequences acctions:

  1. CCRepeatforever CAN contain CCSequence actions
  2. CCSequences CAN’T contain CCRepeatforever actions (well… they can, but they wouldn’t execute it!)

So the solution for this problem that I’ve found is to implement the repeat actions inside a block, and make the sequence actions to call (in the last place in order to be consistent with a repeat forever ongoing action…) the block with the actions:

  1. Create a CCCallBlock action (using C++11.0 lambdas):
  2. Implement your repeat forever actions inside a Block (Closure, Blocks, Lambdas… you name it)

CCCallLambda.h

#ifndef __CCCallLambda__
#define __CCCallLambda__
#include 
#include "cocos2d.h"
#define BLOCK_SELECTOR(b) CCCallBlock::createActionWithBlock(b)
typedef std::function CCBlock;
class CCCallBlock:public cocos2d::CCActionInstant{
public:
    static CCCallBlock * createActionWithBlock(CCBlock);
    bool initActionWithBlock(CCBlock);
    cocos2d::CCObject * copyWithZone(cocos2d::CCZone *);
    virtual void update(float time);
    CC_SYNTHESIZE(CCBlock, blocklambda, BlockLambda);
};
#endif /* defined(__CCCallLambda__) */
CCCallLambda.cpp
#include "CCCallLambda.h"

USING_NS_CC;
CCCallBlock * CCCallBlock::createActionWithBlock(CCBlock f) {

    CCCallBlock * pRet = new CCCallBlock;

    if (pRet && pRet->initActionWithBlock(f)){
        pRet->autorelease();
        return pRet;
    }
    CC_SAFE_DELETE(pRet);
    return pRet;

}

bool CCCallBlock::initActionWithBlock(CCBlock f){
    blocklambda=f;
    return true;
}

void CCCallBlock::update(float time) {
    CC_UNUSED_PARAM(time);
    if (blocklambda)
        blocklambda();
}
CCObject * CCCallBlock::copyWithZone(CCZone *pZone) {
    CCZone *pNewZone = NULL;
    CCCallBlock *pRet = NULL;

    if (pZone && pZone->m_pCopyObject) {
        pRet = (CCCallBlock*) (pZone->m_pCopyObject);
    } else {
        pRet = new CCCallBlock();
        pZone = pNewZone = new CCZone(pRet);
    }

    CCFiniteTimeAction::copyWithZone(pZone);
    CC_SAFE_DELETE(pNewZone);
    return pRet;
}

Implement your repeat actions:

    //Get Steeple
    CCSprite * steeple = (CCSprite*)getChildByTag(tag);

#define ang 135
    CCBlock dance_forever_block = [this] {
        CCRotateBy * rot1 = CCRotateBy::create(1.0, -2*ang);
        CCRotateBy * rot2 = CCRotateBy::create(1.0, 2*ang);
        CCSprite * steeple = (CCSprite*)getChildByTag(tag);
        CCSequence * dance_once = CCSequence::create(rot1,rot2,NULL);
        CCRepeatForever * dance_forever = CCRepeatForever::create(baile) ;
        steeple->runAction(dance_forever);
    };

    CCRotateBy * half_dance = CCRotateBy::create(1.0, ang);
    CCSequence * half_dance_and_then_dance_forever = CCSequence::create(half_dance,BLOCK_SELECTOR(dance_forever_block),NULL);
    steeple->runAction(half_dance_and_then_dance_forever);

Notice how inside the block the CCRepeatforever actions is made out of a CCSequence (Rule number 1 complaiant)
Notice how inside the CCSequence definition a Block is called, which is defined in our code.

Hope this helps!.

Regards.

auto MoveTo = MoveTo::create(2, Vec2(visibleSize.width - 150, 200));
auto MoveTo_2 = MoveTo::create(2, Vec2(150 , 200));
auto action = (ActionInterval*)Sequence::create(MoveTo, MoveTo_2, NULL);
ball->runAction(RepeatForever::create(action));

I used typecasting and it has worked for me but is it a viable solution? I mean is it safe to typecast CCFiniteTimeAction* to CCActionInterval*?

Depending on what you’re doing you might just get away with using Repeat, instead of RepeatForever. I’m only using this to repeat some arrow scaling as part of a tutorial, so I don’t care if it stops repeating after 9999999 repeats. Repeat doesn’t block anything and works exactly as you expect.

As for RepeatForever, not being able to repeat a sequence seems like its a bug, no matter what the explanation is as to why its not working. Cocos2d-x is still super awesome :smile:

1 Like

what is the equivalent of this code in cocos2d-x version?