cocos2d-x遊戲引擎核心之四——動做調度機制

1、動做機制的用法node

在深刻學習動做機制在 Cocos2d-x 裏是如何實現的以前,咱們先來學習整套動做機制的用法,先知道怎麼用,再深刻學習它如何實現,是一個很好很重要的學習方法。編程

(1)基本概念數組

CCAction 是動做類的基類,全部的動做都派生自這個類,它建立的一個對象表明了一個動做。動做做用於 CCNode,所以,任何一個動做都須要由 CCNode 對象來執行。CCAction 做爲一個基類,其實質是一個接口(即抽象類),由它派生的實現類(如運動和轉動等)纔是咱們實際使用的動做。CCAction 的絕大多數實現類都派生自 CCFiniteTimeAction,這個類定義了在有限時間內能夠完成的動做。CCFiniteTimeAction 定義了 reverse 方法,經過這個方法能夠得到一個與原動做相反的動做(稱做逆動做),例如隱藏一個精靈後,用逆轉動做再顯示出來。固然,並不是全部的動做都有對應的逆動做,例如相似"放大到"等設置屬性爲常量的動做不存在逆動做,而設置屬性爲相對值的動做則每每存在相應的逆動做。由 CCFiniteTimeAction 派生出的兩個主要類分別是瞬時動做(CCActionInstant)和持續性動做(CCActionInterval),這兩類動做後面介紹的複合動做配合使用,能獲得複雜生動的動做效果。安全

(2)瞬時動做less

瞬時動做是指能馬上完成的動做,是 CCFiniteTimeAction 中動做持續時間爲 0 的特例。下面介紹一些經常使用的瞬時動做:ide

1. CCPlace:將節點放置到某個指定位置,其做用與修改節點的 Position 屬性相同。函數

2. CCFlipX 和 CCFlipY:這兩個動做分別用於將精靈沿 X 和 Y 軸反向顯示,其做用與設置精靈的 FlipX 和 FlipY 屬性相同。oop

3. CCShow 和 CCHide:這兩個動做分別用於顯示和隱藏節點,其做用與設置節點的 Visible 屬性的做用同樣。學習

4. CCCallFunc:動畫

  CCCallFunc 系列動做包括 CCCallFunc、CCCallFuncN、CCCallFuncND,以及 CCCall- FuncO 四個動做,用來在動做中進行方法的調用(之因此不是函數調用,是由於它們只能調用某個類中的實例方法,而不能調用普通的 C 函數)。當某個對象執行 CCCallFunc 系列動做時,就會調用一個先前被設置好的方法,以完成某些特別的功能。後面咱們將舉例說明它的用途。在 CCCallFunc 系列動做的 4 個類中,CCCallFunc 調用的方法不包含參數,CCCallFuncN 調用的方法包含一個 CCNode*類型的參數,表示執行動做的對象。CCCallFuncND 調用的方法包含兩個參數,不只有一個節點參數,還有一個自定義參數(CCNode*與 void*)。CCCallFuncO 調用的方法則只包含一個 CCObject*類型的參數。
  實際上,CCCallFunc 系列動做的後綴"N"表示 Node 參數,指的是執行動做的對象,"D"表示 Data 參數,指的是用戶自定義的數據,"O"表示對象,指的是一個用戶自定義的 CCObject 參數。在不一樣的狀況下,咱們能夠根據不一樣的需求來選擇不一樣的CCCallFunc 動做。考慮一種狀況,咱們建立了許多會在屏幕中移動的精靈,但願精靈在移動結束以後就從遊戲中刪除。
  爲了實現這個效果,咱們能夠建立一系列動做:首先讓精靈移動,而後調用一個 removeSelf(CCNode* nodeToRemove)方法來刪除 nodeToRemove 對象。在 removeSelf 方法中須要訪問執行此動做的精靈,所以咱們就採用 CCCallFuncN 來調用removeSelf 方法。

(3)持續性動做

持續性動做是在持續的一段時間裏逐漸完成的動做,每一種持續性動做一般都存在兩個不一樣的變種動做,分別具備 To 和 By 後綴:後綴爲 To 的動做描述了節點屬性值的絕對變化,例如 CCMoveTo 將對象移動到一個特定的位置;然後綴爲 By 的動做則描述了屬性值相對的變化,如 CCMoveBy 將對象移動一段相對位移。

根據做用效果不一樣,能夠將持續性動做劃分爲如下 4 大類:

  1. 位置變化動做:CCMoveTo 和 CCMoveBy,CCJumpTo 和 CCJumpBy,CCBezierTo 和 CCBezierBy。
  2. 屬性變化動做:CCScaleTo 和 CCScaleBy,CCRotateTo 和 CCRotateBy,CCFadeIn 和 CCFadeOut,CCFadeTo。
  3. 視覺特效動做:CCBlink,CCAnimation(播放幀動畫,用幀動畫的形式實現動畫效果)
  4. 控制動做: CCDelayTime、CCRepeat 和CCRepeatForever 等,控制動做與複合動做息息相關。

(4)複合動做

1 重複(CCRepeat/CCRepeatForever):

CCRepeat* CCRepeat::create(CCFiniteTimeAction *pAction, unsigned int times);
CCRepeatForever *CCRepeatForever::create(CCActionInterval *pAction);

2 並列(CCSpawn):

CCSpawn::create(CCFiniteTimeAction *pAction1,...);
CCSpawn::create(CCFiniteTimeAction *pAction1, CCFiniteTimeAction *pAction2);

3. 序列(CCSequence):

CCSequence::create(CCFiniteTimeAction *pAction1,...);
CCSequence::create(CCFiniteTimeAction *pAction1,CCFiniteTimeAction *pAction2);

在實現 CCSequence 和 CCSpawn 兩個組合動做類時,有一個很是有趣的細節:成員變量中並無定義一個可變長的容器來容納每個動做系列,而是定義了m_pOne和m_pTwo兩個動做成員變量。若是咱們建立了兩個動做的組合,那麼m_pOne與m_pTwo就分別是這兩個動做自己;當咱們建立更多動做的組合時,引擎會把動做分解爲兩部分來看待,其中後一部分只包含最後一個動做,而前一部分包含它以前的全部動做,引擎把 m_pTwo 設置爲後一部分的動做,把 m_pOne 設置爲其他全部動做的組合。例如,語句 sequence = CCSequence::create(action1, action2, action3, action4, NULL);就等價於:

CCSequence s1 = CCSequence::createWithTwoActions(action1, action2);
CCSequence s2 = CCSequence::createWithTwoActions(s1, action3);
sequence = CCSequence::createWithTwoActions(s2, action4);

CCSpawn 與 CCSequence 所採用的機制相似。下面是 CCSpawn 的一個初始化方法,就是利用遞歸的思想簡化了編程的複雜度:

CCFiniteTimeAction* CCSpawn::create(CCArray *arrayOfActions)
{
    CCFiniteTimeAction* prev = (CCFiniteTimeAction*)arrayOfActions->objectAtIndex(0);
    for (unsigned int i = 1; i < arrayOfActions->count(); ++i)
    {
        prev = create(prev, (CCFiniteTimeAction*)arrayOfActions->objectAtIndex(i));
    }
    return prev;
}

4. 延時(CCDelayTime):CCDelayTime::create(float d);

(5)變速動做

變速動做包括 CCSpeed 動做與 CCEase 系列動做:

1. CCSpeed:CCSpeed 用於線性地改變某個動做的速度,所以,能夠實現成倍地快放或慢放功能。爲了改變一個動做的速度,首先須要將目標動做包裝到 CCSpeed 動做中:

CCRepeatForever* repeat = CCRepeatForever::create(animation);
CCSpeed* speed = CCSpeed::create(repeat, 1.0f);
speed->setTag(action_speed_tag);
fish->runAction(speed);

接下來,在須要改變速度的地方,咱們經過修改變速動做的 speed 屬性來改變更做速度,下面的代碼將會把上面設置的動畫速度變爲原來的兩倍:

CCSpeed * speed = fish->getActionByTag(action_speed_tag);
speed->setSpeed(2.0f);

2. CCActionEase:

雖然使用 CCSpeed 可以改變更做的速度,然而它只能按比例改變目標動做的速度。若是咱們要實現動做由快到慢、速度隨時間改變的變速運動,須要不停地修改它的speed屬性才能實現,顯然這是一個很煩瑣的方法。下面將要介紹的CCActionEase系列動做經過使用內置的多種自動速度變化來解決這一問題。CCActionEase 系列包含 15 個動做,它們能夠被歸納爲 5 類動做:指數緩衝、Sine緩衝、彈性緩衝、跳躍緩衝和回震緩衝。每一類動做都有 3 個不一樣時期的變換:In、Out 和 InOut。

CCActionEase 的使用方法與 CCSpeed 相似。以 Sine 緩衝爲例,如下代碼實現了 InSine 變速運動:

CCEaseSineIn* sineIn = CCEaseSineIn::create(action);
sineIn->setTag(action_sine_in_tag);
fish->runAction(sineIn);

(6)建立自定義動做

CCAction 包含兩個重要的方法:step 與 update。step 方法會在每一幀動做更新時觸發,該方法接受一個表示調用時間間隔的參數 dt,dt 的積累即爲動做運行的總時間。引擎利用積累時間來計算動做運行的進度(一個從 0 到 1 的實數),並調用 update 方法更新動做。update 方法是 CCAction 的核心,它由 step 方法調用,接受一個表示動做進度的參數,每個動做都須要利用進度值改變目標節點的屬性或執行其餘指令。自定義動做只須要從這兩個方法入手便可,咱們一般只須要修改 update 方法就能夠實現簡單的動做。

  也許有的讀者已經有了疑問,step 方法與 update 方法均可以作到每一幀判斷一次方向,爲何選擇重載 step 方法而不是update 方法呢?這是由於引擎在 step 方法中對動做對象的內部成員進行了更新,更新後纔會由此方法調用 update 方法來更新目標節點。在方向追蹤的動做中,咱們除了在每一幀判斷方向,還必須同步執行被包裝的動做。這就須要咱們調用被包裝動做的 step 方法,以保證對象可以被完整地更新。

2、動做機制實現原理

(1)動做類的結構

class CC_DLL CCAction : public CCObject 
{
public:
    CCAction(void);
    virtual ~CCAction(void);

    const char* description();

    virtual CCObject* copyWithZone(CCZone *pZone);

    //! return true if the action has finished
    virtual bool isDone(void);

    //! called before the action start. It will also set the target.
    virtual void startWithTarget(CCNode *pTarget);

    /** 
    called after the action has finished. It will set the 'target' to nil.
    IMPORTANT: You should never call "[action stop]" manually. Instead, use: "target->stopAction(action);"
    */
    virtual void stop(void);

    //! called every frame with it's delta time. DON'T override unless you know what you are doing.
    virtual void step(float dt);

    /** 
    called once per frame. time a value between 0 and 1

    For example: 
    - 0 means that the action just started
    - 0.5 means that the action is in the middle
    - 1 means that the action is over
    */
    virtual void update(float time);
    
    inline CCNode* getTarget(void) { return m_pTarget; }
    /** The action will modify the target properties. */
    inline void setTarget(CCNode *pTarget) { m_pTarget = pTarget; }
    
    inline CCNode* getOriginalTarget(void) { return m_pOriginalTarget; } 
    /** Set the original target, since target can be nil.
    Is the target that were used to run the action. Unless you are doing something complex, like CCActionManager, you should NOT call this method.
    The target is 'assigned', it is not 'retained'.
    @since v0.8.2
    */
    inline void setOriginalTarget(CCNode *pOriginalTarget) { m_pOriginalTarget = pOriginalTarget; }

    inline int getTag(void) { return m_nTag; }
    inline void setTag(int nTag) { m_nTag = nTag; }

public:
    /** Allocates and initializes the action 
    @deprecated: Please use create() instead. This interface will be deprecated sooner or later.
    */
    CC_DEPRECATED_ATTRIBUTE static CCAction* action();

    /** Create an action */
    static CCAction* create();
protected:
    CCNode    *m_pOriginalTarget;
    /** The "target".
    The target will be set with the 'startWithTarget' method.
    When the 'stop' method is called, target will be set to nil.
    The target is 'assigned', it is not 'retained'.
    */
    CCNode    *m_pTarget;
    /** The action tag. An identifier of the action */
    int     m_nTag;
};

繼承自 CCAction 的 CCFiniteTimeAction 主要新增了一個用於保存該動做總的完成時間的成員變量:ccTime m_fDuration。對於 CCFiniteTimeAction 的兩個子類 CCActionInstant 和 CCActionInterval,前者沒有新增任何函數和變量,然後者增長了兩個成員變量--ccTime m_elapsed 和 bool m_bFirstTick,其中 m_elapsed 是從動做開始起逝去的時間,而 m_bFirstTick是一個控制變量,在後面的分析中,咱們將看到它的做用。

(2)動做的更新

1. 當咱們對 CCNode 調用 runAction(CCAction* action)方法時,動做管理類 CCActionManager(它是一個單例對象)會將新的 CCAction 和對應的目標節點添加到其管理的動做表。在 CCActionManager 的 addAction 方法中,咱們將動做添加到動做隊列以後,就會對該 CCAction 調用成員函數startWithTarget(CCNode* pTarget)來綁定該動做的執行者。而在 CCAction 的子類中(如 CCActionInterval),還初始化了一些參數:

void CCActionInterval::startWithTarget(CCNode *pTarget)
{
    CCFiniteTimeAction::startWithTarget(pTarget);
    m_elapsed = 0.0f;
    m_bFirstTick = true;
}

2. 當這些準備工做都完成後,每一幀刷新屏幕時,系統都會在 CCActionManager 中遍歷其動做表中的每個動做,並調用該動做的 step(ccTimedt)方法。step 方法主要負責計算 m_elapsed 的值,並調用 update(float time)方法,相關代碼以下:

void CCActionInterval::step(float dt)
{
    if (m_bFirstTick)
    {
        m_bFirstTick = false;
        m_elapsed = 0;
    }
    else
    {
        m_elapsed += dt;
    }
    this->update(MAX (0,
             MIN(
1, m_elapsed / MAX(m_fDuration, FLT_EPSILON))         )         ); }

傳入 update 方法的 time 參數表示逝去的時間與動做完成須要的時間的比值,是介於 0 和 1 之間的一個數,即動做完成的百分比

CCActionInterval並無進一步實現update方法。下面咱們繼續以繼承自CCActionInterval的CCRotateTo動做的update方法爲例,分析 update 函數是如何實現的,其實現代碼以下:

void CCRotateTo::update(float time)
{
    if (m_pTarget)
    {
        m_pTarget->setRotation(m_fStartAngle + m_fDiffAngle * time);
    }
}

看到這裏,咱們已經能看出 Cocos2d-x 的動做機制的整個工做流程了。在 CCRotateTo 中,最終完成的操做是修改目標節點的 Rotation 屬性值,更新該目標節點的旋轉屬性值。

3. 最後,在每一幀刷新結束後,在 CCActionManager 類的 update 方法中都會檢查動做隊列中每個動做的 isDone 函數是否返回 true。若是返回 true,則動做已完成,將其從隊列中刪除。isDone 函數的代碼以下:

bool CCActionInterval::isDone(void)
{
    return m_elapsed >= m_fDuration;
}

對於不一樣的動做類,雖然總體流程大體都是先調用 step 方法,而後按照各個動做的具體定義來更新目標節點的屬性,可是不一樣動做的具體實現會有所不一樣。例如,CCRepeatForever 動做的 isDone 函數始終返回 false,由於它是永遠在執行的動做;又如CCActionInstant 及其子類的 step 函數中,向 update 傳遞的參數值始終是 1,由於瞬時動做會在下一幀刷新後完成,不須要屢次執行 update。

(3)CCActionManager 的工做原理

學習了 CCAction 在每一幀中如何被更新以後,咱們不妨回頭看看動做管理類 CCActionManager 的工做原理。在對CCDirector 進行初始化時,也會對 CCActionManager 進行初始化。下面的代碼是 CCDirector::init()方法中的一部分:

    // action manager
    m_pActionManager = new CCActionManager();
    m_pScheduler->scheduleUpdateForTarget(m_pActionManager, kCCPrioritySystem, false);

能夠看到,在 CCActionManager 被初始化後,立刻就調用了定時調度器 CCScheduler 的 scheduleUpdateForTarget 方法。在 scheduleUpdateForTarget 函數中,咱們爲 CCActionManager 註冊了一個按期更新的服務,這意味着動做的調度與定時器的調度都統一受到 CCScheduler 的控制。具體地說,咱們能夠方便地同時暫停或恢復定時器與動做的運行,而沒必要考慮它們不一樣步的問題。

CCScheduler 在每一幀更新時,都會觸發 CCActionManager 註冊的 update 方法。從下面給出的 CCActionManager::update方法的代碼能夠看到,CCActionManager 在這時對每個動做都進行了更新。與調度器 CCScheduler 相似的一點是,爲了防止動做調度過程當中所遍歷的表被修改,Cocos2d-x 對動做的刪除進行了仔細地處理,保證任何狀況下均可以安全地刪除動做:

// main loop
void CCActionManager::update(float dt)
{
    for (tHashElement *elt = m_pTargets; elt != NULL; )
    {
        m_pCurrentTarget = elt;
        m_bCurrentTargetSalvaged = false;

        if (! m_pCurrentTarget->paused)
        {
            // The 'actions' CCMutableArray may change while inside this loop.
            for (m_pCurrentTarget->actionIndex = 0; m_pCurrentTarget->actionIndex < m_pCurrentTarget->actions->num;
                m_pCurrentTarget->actionIndex++)
            {
                m_pCurrentTarget->currentAction = (CCAction*)m_pCurrentTarget->actions->arr[m_pCurrentTarget->actionIndex];
                if (m_pCurrentTarget->currentAction == NULL)
                {
                    continue;
                }

                m_pCurrentTarget->currentActionSalvaged = false;

                m_pCurrentTarget->currentAction->step(dt);

                if (m_pCurrentTarget->currentActionSalvaged)
                {
                    // The currentAction told the node to remove it. To prevent the action from
                    // accidentally deallocating itself before finishing its step, we retained
                    // it. Now that step is done, it's safe to release it.
                    m_pCurrentTarget->currentAction->release();
                } else
                if (m_pCurrentTarget->currentAction->isDone())
                {
                    m_pCurrentTarget->currentAction->stop();

                    CCAction *pAction = m_pCurrentTarget->currentAction;
                    // Make currentAction nil to prevent removeAction from salvaging it.
                    m_pCurrentTarget->currentAction = NULL;
                    removeAction(pAction);
                }

                m_pCurrentTarget->currentAction = NULL;
            }
        }

        // elt, at this moment, is still valid
        // so it is safe to ask this here (issue #490)
        elt = (tHashElement*)(elt->hh.next);

        // only delete currentTarget if no actions were scheduled during the cycle (issue #481)
        if (m_bCurrentTargetSalvaged && m_pCurrentTarget->actions->num == 0)
        {
            deleteHashElement(m_pCurrentTarget);
        }
    }

    // issue #635
    m_pCurrentTarget = NULL;
}

 3、CCSequence動做案例分析——CCSequence不能執行CCRepeatForever

(1)示例代碼

CCBlink* blink=CCBlink::create(0.5f,10);//建立閃爍動畫,duration=0.5s  
CCAnimation* animation=CCAnimation::create();  
animation->addSpriteFrameWithFileName("CloseNormal.png");  
animation->addSpriteFrameWithFileName("CloseSelected.png");  
animation->setDelayPerUnit(1.0f);//幀間間隔1s  
CCAnimate* animate=CCAnimate::create(animation);//建立幀動畫  
CCRepeatForever* repeat=CCRepeatForever::create(animate);  
CCSequence* sequence=CCSequence::create(blink,repeat,NULL);//建立連續動畫  
CCSprite* close=CCSprite::create("CloseNormal.png");  
close->setPosition(ccp(240,160));  
this->addChild(close);  
close->runAction(sequence);//執行連續動畫 

結果精靈閃爍10次之後,幀動畫不執行了。

(2)緣由

//建立CCSequence  
CCSequence* CCSequence::create(CCFiniteTimeAction *pAction1, ...) 

內部調用了createWithVariableList,從實現能夠看出這是一個遞歸調用。

//獲取動做列表,建立CCSequence  
CCSequence* CCSequence::createWithVariableList(CCFiniteTimeAction *pAction1, va_list args)  
{  
    CCFiniteTimeAction *pNow;//當前動做  
    CCFiniteTimeAction *pPrev = pAction1;//第一個動做  
    bool bOneAction = true;//只有一個動做的標誌位  
  
    while (pAction1)  
    {  
        pNow = va_arg(args, CCFiniteTimeAction*);//獲取當前動做  
        if (pNow)//若是存在  
        {  
            pPrev = createWithTwoActions(pPrev, pNow);//用前兩個動做建立CCSequence並賦給第一個動做  
            bOneAction = false;//置false  
        }  
        else//若是不存在  
        {  
            // If only one action is added to CCSequence, make up a CCSequence by adding a simplest finite time action.  
            if (bOneAction)//若是隻有一個動做  
            {  
                pPrev = createWithTwoActions(pPrev, ExtraAction::create());  
            }  
            break;//跳出循環  
        }  
    }  
      
    return ((CCSequence*)pPrev);//返回第一個動做  
} 

假若有3個動做要被串聯,則先把第1個和第2個串聯一個CCSequence,再把這個CCSequence和第3個動做串聯成最終的CCSequence,而後返回。從CCSequence的成員變量能夠看到:

CCFiniteTimeAction *m_pActions[2];//代表只包含2個動做對象指針  

使用遞歸多少會下降程序的運行效率,可是卻能夠換來代碼的簡潔性,一樣的CCSpawn也是這麼實現的。在createWithTwoActions中,調用了initWithTwoActions函數,實現了把兩個動做串成一個CCSequence,關鍵代碼以下:

float d = pActionOne->getDuration() + pActionTwo->getDuration();//獲取兩個動做的duration  
CCActionInterval::initWithDuration(d);//賦給新的CCSequence  
  
m_pActions[0] = pActionOne;//同時把兩個動做賦給m_pActions指針數組  
pActionOne->retain();  
  
m_pActions[1] = pActionTwo;  
pActionTwo->retain();  

從示例能夠看出,閃爍動畫blink的duration是0.5s,那CCRepeatForever呢?1s?固然不是,1s只是幀動畫animate的幀間間隔,每一個幀動畫包含2幀,而CCRepeatForever的duration是0。所以,當示例中的閃爍動畫blink和重複動畫repeat串聯成CCSequence sequence的時候,sequence的duration就變成0.5+0=0.5s,這很重要。

CCSequence中有這麼一個成員變量

float m_split;//記錄了第一個動畫時長佔總時長的比例,也就是2個動畫的時長分界  

當執行runAction的時候,CCSequence會調用

void CCSequence::startWithTarget(CCNode *pTarget)  
{  
    CCActionInterval::startWithTarget(pTarget);  
    m_split = m_pActions[0]->getDuration() / m_fDuration;//獲取第一個動畫佔總時長的比例  
    m_last = -1;  
}  

而這裏因爲blink佔了0.5s,repeat佔了0s,總時長0.5s,因此m_split是0.5/0.5=1。blink佔滿了整個CCSequence。這時候再來看CCSequence::update(float dt)函數的執行,就會恍然大悟了。

int found = 0;//當前播放動做索引  
float new_t = 0.0f;//新播放進度  
  
if( t < m_split ) {//播放進度<分界進度  
    found = 0;//設置當前播放的是第一個動做  
    if( m_split != 0 )//若是第一個動做時長佔比!=0  
        new_t = t / m_split;//計算出第一個動做新的播放進度  
    else  
        new_t = 1;//設置第一個已播放完畢  
  
} else {//播放進度>=分界進度  
    found = 1;//設置當前播放的是第二個動做  
    if ( m_split == 1 )//若是第一個動做時長佔比==1  
        new_t = 1;//設置第二個動做已完成  
    else  
        new_t = (t-m_split) / (1 - m_split );//計算出第二個動做新的播放進度  
}  

  CCSpawn也會有這個問題,因此CCSpawn也沒法執行加入其中的CCRepeatForever動做。

  CCRepeatForever的反轉動做也是無效了,一個不會中止的動做從什麼地方開始反轉?固然你能夠先把動做反轉了再加入CCRepeatForever中,這是沒問題的。

(3)解決方案

對於同時動做,不使用CCSpawn,採用分別執行

close->runAction(blink);  
close->runAction(repeat);  

對於連續動做,不直接往CCSequence中加入CCRepeatForever,而是把CCRepeatForever放入瞬時動做CCCallFunc中,再把CCCallFunc加入CCSequence中執行。

close=CCSprite::create("CloseNormal.png");  
CCBlink* blink=CCBlink::create(0.5f,10);  
CCCallFunc* callFunc=CCCallFunc::create(this,callfunc_selector(TestScene::repeatFunc));//建立CCCallFunc對象  
CCSequence* sequence=CCSequence::create(blink,callFunc,NULL);//把CCCallFunc對象加入CCSequence中  
close->setPosition(ccp(240,160));  
this->addChild(close);  
close->runAction(sequence);  
  
  
void TestScene::repeatFunc()  
{  
    CCAnimation* animation=CCAnimation::create();  
    animation->addSpriteFrameWithFileName("CloseNormal.png");  
    animation->addSpriteFrameWithFileName("CloseSelected.png");  
    animation->setDelayPerUnit(1.0f);  
    CCAnimate* animate=CCAnimate::create(animation);  
    CCRepeatForever* repeat=CCRepeatForever::create(animate);  
    close->runAction(repeat);  

對於CCAnimation幀動畫,能夠設置循環屬性,而不使用CCRepeatForever。

animation->setLoops(-1);  

(4)雖然CCRepeatForever也一樣繼承於CCActionInterval,理論上是延時動做的子類,可是和通常的延時動做又有很大的不一樣,因此平時在使用的時候必須很當心,不能當成通常的CCActionInterval使用。

相關文章
相關標籤/搜索