Cocos2Dx之動作Action


Cocos2Dx之動作Action

不錯的文章轉載了,原文地址:http://my.oschina.net/sulliy/blog/290914

Cocos2Dx提供了調度器。結合調度器,我們可以不斷地修改節點的屬性,從而實現豐富的動態效果。但是這樣做過於麻煩。舉個例子,我們需要把一個精靈從一個位置移動到另外一個位置。從我們前面提到的內容來思考一個可行的方案:在父節點的update中,將它的位置移動鎮間隔時間移動的距離。假定目標位置到初始距離爲d,移動時間指定爲t秒,幀間隔時間1/60秒,那麼每個update我們需要往目標位置移動d/60t。這還是勻速地移動,如果要求加速移動的效果,或者減速移動的效果,情況就變得更加複雜一些了。

爲了簡化節點的動作執行,一般是CCSprite,Cocos2Dx提供了CAction來支持動作。動作可以簡單地分爲兩類,一是瞬時動作,即立即完成的動作;二是持續性動作,即動作的執行需要持續一段時間。另外,我們還需要有能夠組合各種動作的能力,稱之爲複合動作。下面我們,先看看動作的分類。

瞬時動作。瞬時動作就是下一幀就要完成的動作,比如定位、縮放。其實這樣的動作不需要定時機制,修改節點的屬性即可,但還是封裝爲一個動作,目的是方便進行做動作的組合。瞬時動作包括:

  • CCPlace:將節點放置到某個指定位置,其作用與修改節點的Position屬性相同。

  • CCFlipX和CCFlipY:用於將精靈沿X和Y軸反向顯示,其作用與設置精靈的FlipX和FlipY屬性相同。

  • CCShow和CCHide:用於顯示和隱藏節點,其作用與設置節點的Visible屬性的作用一樣。

  • CCCallFunc:CCCallFunc系列動作包括CCCallFunc、CCCallFuncN、CCCallFuncND,以及CCCall- FuncO四個動作,用來在動作中進行類的實例方法的調用。

持續性動作。持續性動作,以爲着動作的執行需要持續一段時間。所以,這些都做都需要指定動作執行的時間:duration。持續性動作還可以根據產生的效果不同進一步細分。

  • 位置變化動作:針對位置(position)屬性。

    • CCMoveTo和CCMoveBy:用於使節點做直線運動。

    • CCJumpTo和CCJumpBy:使節點以一定的軌跡跳躍到指定位置。

    • CCBezierTo和CCBezierBy:使節點進行曲線運動,運動的軌跡由貝塞爾曲線描述。

by是相對於節點的位置;to是絕對位置。

  • 屬性變化動作:通過屬性值的逐漸變化來實現動畫效果。

    • CCScaleTo和CCScaleBy:產生縮放效果,使節點的縮放係數隨時間線性變化。

    • CCRotateTo和CCRotateBy:產生旋轉效果。

    • CCFadeIn和CCFadeOut:產生淡入淡出效果,其中前者實現了淡入效果,後者實現了淡出效果。

    • CCFadeTo:用於設置一段時間內透明度的變化效果。

    • CCTintTo和CCTintBy:設置色調變化。可以理解爲RGB顏色中的某一種顏色值變化了。

  • 視覺特效動作:一些特殊的視覺效果。

    • CCBlink:使目標節點閃爍。

    • CCAnimation:播放幀動畫,用幀動畫的形式實現動畫效果。

  • 控制動作:對一些列動作進行精細控制。

    • CCDelayTime:將動作延時一定的時間才執行。

    • CCRepeat:把現有的動作重複一定次數。

    • CCRepeatForever:現有動作一直執行下去。

複合動作。複合動作是由一些基本動作一起組合成的新動作。可以滿足遊戲需要的複雜動作需求。

  • CCSpawn:允許一批動作同時執行。

  • CCSequence:順序執行一系列動作。

  • CCSpeed:改變動作執行的速度。

  • CCActionEase:是一類動作,不同於CCSpeed的勻速執行,CCActionEase允許多樣化地控制速度。

    • CCEaseRateAction

      • CCEaseIn

      • CCEaseOut

      • CCEaseInOut

    • CCEaseExponentialIn

    • CCEaseExponentialIn

    • CCEaseExponentialInOut

    • CCEaseSineIn

    • CCEaseSineOut

    • CCEaseSineInOut

    • CCEaseElastic

      • CCEaseElasticIn

      • CCEaseElasticOut

      • CCEaseElasticInOut

    • CCEaseBounce

      • CCEaseBounceIn

      • CCEaseBounceOut

      • CCEaseBounceInOut

    • CCEaseBackIn

    • CCEaseBackOut

    • CCEaseBackInOut

JQuery有個比較詳盡的Easing效果示例。可以訪問http://jqueryui.com/easing/查看。並不是所有的Easing效果Cocos2Dx都支持。

知道大概有哪些Action後,我們看看Action是怎麼得到調度執行的。

我們執行一個Action的方法,一般是是調用CCSripte::runAction,因爲動作一般都是作用於某個精靈的。CCSripte繼承自CCNodeRGBA,後者繼承自CCNode。CCNode提供了runAction來執行動作。CCLayer也繼承自CCNode,因此它也是可以執行動作的。後面都假定執行動作的對象是一個CCSprite。

CCNode有一個成員m_pActionManager,它默認初始化爲CCDirector內部的CCActionManager。CCDirector在它的init初始化函數中會創建一個CCActionManager對象。這類似於調度器CCScheduler。CCNode擁有指向CCActionManager對象的指針,因此在CCNode::runAction裏面,通過CCActionManager的addAction將自己添加到CCActionManager當中。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void CCActionManager::addAction(CCAction *pAction, CCNode *pTarget, bool paused)
{
    tHashElement *pElement = NULL;
    CCObject *tmp = pTarget;
    HASH_FIND_INT(m_pTargets, &tmp, pElement);
    if (! pElement)
    {
        pElement = (tHashElement*)calloc(sizeof(*pElement), 1);
        pElement->paused = paused;
        pTarget->retain();
        pElement->target = pTarget;
        HASH_ADD_INT(m_pTargets, target, pElement);
    }
     actionAllocWithHashElement(pElement);
     ccArrayAppendObject(pElement->actions, pAction);
     pAction->startWithTarget(pTarget);
}

CCActionManager同樣用一個HASH表來存儲動作CCAction,HASH表的鍵還是CCObject類型的指針。每個CCSprite可能同時在執行多個動作。CCActionManager將這些動作放在一個數組當中(tHashElement->actions)。CCActionManager::addAction先根據傳入的pTarget作爲鍵來查看HASH表中是否已經存在對應的Bucket。如果沒有找到,就現在HASH表中創建一個。然後,將現在的CCAction添加到數組的末尾。

到目前爲止,我們只是將需要執行的動作放在一個HASH表中。什麼時候執行呢?這就要看CCDirector::init。CCDirector初始化的時候,先創建一個CCScheduler,然後創建一個CCActionManager,隨後馬上調用CCScheduler::scheduleUpdateForTarget將自己註冊爲一個update調度器。並且update調度器的優先級設爲了最高優先級kCCPrioritySystem(INT_MIN)。

CCScheduler::scheduleUpdateForTarget的實現我們前面已經討論過了。它將註冊的update調度器根據優先級放到不同的列表當中。update調度器在每個幀間隔時間到期之後就會被調用。CCActionManager把自己註冊爲一個update調度器,那麼在每個幀間隔時間到期之後,就會調用CCActionManager的update函數。由於CCActionManager管理所有的動作,因此CCActionManager::update相當於管理動作的一個主循環。

注:到這裏,我們已經討論過不少Cocos2Dx的主循環:應用程序主循環、CCDirector的主循環、CCScheduler的主循環、CCScene的主循環、負責內存維護的棧循環。現在我們討論到了負責管理動作的CCActionManager主循環。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
void CCActionManager::update(float dt)
{
    for (tHashElement *elt = m_pTargets; elt != NULL; )
    {
        m_pCurrentTarget = elt;
        m_bCurrentTargetSalvaged = false;
        if (! m_pCurrentTarget->paused)
        {
            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)
                {
                    m_pCurrentTarget->currentAction->release();
                else if (m_pCurrentTarget->currentAction->isDone())
                {
                    m_pCurrentTarget->currentAction->stop();
                    CCAction *pAction = m_pCurrentTarget->currentAction;
                    m_pCurrentTarget->currentAction = NULL;
                    removeAction(pAction);
                }
                m_pCurrentTarget->currentAction = NULL;
            }
        }
        elt = (tHashElement*)(elt->hh.next);
        if (m_bCurrentTargetSalvaged && m_pCurrentTarget->actions->num == 0)
        {
            deleteHashElement(m_pCurrentTarget);
        }
    }
    m_pCurrentTarget = NULL;
}

CCActionManager::update的外循環遍歷所有的HASH表的Bucket,內循環遍歷CCSprite上的所有動作。執行動作是通過調用CCAction的step函數(多態調用)。不同類型的動作,可能實現了不同的step。我們後面再看不同的step實現。currentActionSalvaged用來標記動作是否被要求刪除。CCAction的isDone虛函數用來幫助CCActionManager判斷當前的動作是否已經完成。CCAction的stop虛函數是在清理動作之前執行的回調函數。如果動作執行完step之後,已經完成了,那麼動作就會從HASH表中Bucket的數組上刪除。最後,如果某個CCSprite上已經沒有附加任何動作,就刪除HASH表中這個Bucket。

我們可以看出,實現一個動作的關鍵是在繼承CCAction的基礎上實現這些虛函數:

  • void step(float dt)

  • void stop(void)

  • bool isDone(void)

  • void update(float time) 真正實現動作的地方,step相當於一個包裝器,它內部會調用update。

動作相關的類很多。它們放在\cocos2dx\actions下面。我們不討論所有的動作,挑選一兩個看下怎麼在CCAction的基礎上實現動作即可。點到爲止。

CCActionInterval和CCJumpBy

CCActionInterval是持續性動作。意味着動作的完成可能會持續大於一幀的時間。CCActionInterval和CCActionInstant都是繼承自CCFiniteTimeAction。對計算機來說,時間還是一個有限量。CCFiniteTimeAction本身只是提供了對動作持續時間duration的封裝。CCActionInstant的duration爲0而已。CCActionInterval的duration是構造函數傳入的。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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))));
}

CCActionInterval的step累積已經流逝的時間,存放在m_elapsed當中。如果流逝的時間m_elapsed大於我們指定的動作執行時間duration,那麼動作就該完成了,這就是CCActionInterval::isDone做的事情。

需要注意的是,傳給動作update的時間是一個0-1的浮點數。它表示整個動作完成的程度。由於每幀都會調用step,那麼動作的update函數的執行次數就是按duration和幀間隔時間除的結果。step是用來控制動作執行過程的。

因此CCJumpBy需要做的就是在每幀調用update時,將CCSprite移動到合適的位置。

?
1
2
3
4
5
6
7
8
9
10
11
void CCJumpBy::update(float t)
{
    if (m_pTarget)
    {
        float frac = fmodf( t * m_nJumps, 1.0f );
        float y = m_height * 4 * frac * (1 - frac);
        y += m_delta.y * t;
        float x = m_delta.x * t;
        m_pTarget->setPosition(ccpAdd( m_startPosition, ccp(x,y)));
    }
}

CCSpeed

CCSpeed本身並不是一個獨立的動作。它作用於其他的繼續性動作(CCActionInterval),可以加快或者降低其他動作的執行速度。具體實現的辦法是在step的流逝時間參數上乘以了一個速度因子,實現非常簡單。

他的isDone和stop都是調用作用動作的對應實現。

CCActionInstant和CCPlace

CCActionInstant是在下一幀就會完成的動作。CCActionInstant::isDone的直接返回ture,因此CCActionInstant::step只會執行一次,對應的動作的update函數也就只會執行一次。

CCPlace繼承自CCActionInstant,它的update函數也就非常簡單了,直接將自己的位置設置爲目標位置即可。

由於動作的執行只是改變了CCSprite的屬性,但是還需要在當前幀上反應出來,因此調度器的調用應該在渲染場景之前,詳見CCDirector::drawScene。

還有一個特殊動作是CCAnimate,它和CCAnimation一起完成動畫效果。我們後面再討論。

CCCallFunc、CCCallFuncN、CCCallFuncND、CCCallFuncO

這類動作比較特殊,它們用來幫助執行自定義的回調函數。後綴代表的是不同的參數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章