Cocos2d-x 之複合動作

Cocos2d-x爲我們提供了一套動作的複合機制,允許我們組合各種基本動作,產生更爲複雜和生動的動作效果。複合動作是一類特殊的動作,因此它也需要使用CCNoderunAction方法執行。而它的特殊之處在於,作爲動作容器,複合動作可以把許多動作組合成一個複雜的動作。因此,我們通常會使用一個或多個動作來創建複合動作,再把動作交給節點執行。

複合動作十分靈活,這是由於複合動作本身也是動作,因此也可以作爲一個普通的動作嵌套在其他複合動作中。

1. 重複(CCRepeat/CCRepeatForever

有的情況下,動作只需要執行一次即可,但我們還常常遇到一個動作反覆執行的情況。對於一些重複的動作,如魚的擺動、能量槽的轉動,我們可以通過CCRepeatCCRepeatForever這兩個方式重複執行:

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

在上述代碼中,pAction參數表示需要重複的動作,第一個方法允許指定動作的重複次數,第二個方法使節點一直重複該動作直到動作被停止。

2. 並列(CCSpawn

指的是使一批動作同時執行。在《捕魚達人》遊戲中,魚一邊沿曲線遊動一邊擺尾巴,炮彈一邊發射一邊噴射氣體,金幣一邊旋轉一邊移動等動作,都可以通過CCSpawn來實現。CCSpawnCCActionInterval派生而來的,它提供了兩個工廠方法:

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

其中第一個靜態方法可以將多個動作同時並列執行,參數表中最後一個動作後需要緊跟NULL表示結束。第二個則只能指定兩個動作複合,不需要在最後一個動作後緊跟NULL。此外,執行的動作必須是能夠同時執行的、繼承自CCFiniteTimeAction的動作。組合後,CCSpawn動作的最終完成時間由其成員中最大執行時間的動作來決定。

3. 序列(CCSequence

除了讓動作同時並列執行,我們更常遇到的情況是順序執行一系列動作。CCSequence提供了一個動作隊列,它會順序執行一系列動作,例如魚游出屏幕外後需要調用回調函數,捕到魚後顯示金幣數量,經過一段時間再讓金幣數量消失,等等。

CCSequence同樣派生自CCActionInterval。與CCSpawn一樣,CCSquence也提供了兩個工廠方法:

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

它們的作用分別是建立多個和兩個動作的順序執行的動作序列。同樣要注意複合動作的使用條件,部分的非延時動作(如CCRepeatForever)並不被支持。

在實現CCSequenceCCSpawn兩個組合動作類時,有一個非常有趣的細節:成員變量中並沒有定義一個可變長的容器來容納每一個動作系列,而是定義了m_pOnem_pTwo兩個動作成員變量。如果我們創建了兩個動作的組合,那麼m_pOnem_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);

CCSpawnCCSequence所採用的機制類似,在此就不再贅述了。採用這種遞歸的方式,而不是直接使用容器來定義組合動作,實際上爲編程帶來了極大的便利。維護多個動作的組合是一個複雜的問題,現在我們只需要考慮兩個動作組合的情況就可以了。下面是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;
}

衆所周知,遞歸往往會犧牲一些效率,但能換來代碼的簡潔。在這兩個複合動作中,細節處理得十分優雅,所有的操作都只需要針對兩個動作實施,多個動作的組合會被自動變換爲遞歸實現:

void CCSpawn::update(float time)
{
    if (m_pOne)
    {
        m_pOne->update(time);
    }
    if (m_pTwo)
    {
        m_pTwo->update(time);
    }
}

CCActionInterval* CCSpawn::reverse(void)
{
    return CCSpawn::create(m_pOne->reverse(), m_pTwo->reverse());
}

4. 延時(CCDelayTime

CCDelayTime是一個“什麼都不做”的動作,類似於音樂中的休止符,用來表示動作序列裏一段空白期,通過佔位的方式將不同的動作段串接在一起。實際上,這與一個定時期實現的延遲沒有區別,但相比之下,使用CCDelayTime動作來延時就可以方便地利用動作序列把一套動作連接在一起。CCDelayTime只提供了一個工程方法,如下所示:

CCDelayTime::create(float d);

其中僅包含一個實型參數,表示動作佔用的時間。

針對位置(position)這一屬性,引擎爲我們提供了3種位置變化動作類型,下面將簡要介紹這幾種動作。

  • CCMoveToCCMoveBy:用於使節點做直線運動。設置了動作時間和終點位置後,節點就會在規定時間內,從當前位置直線移動到設置的終點位置。它們的初始化方法分別爲:

    CCMoveTo::create(ccTime duration, CCPoint& pos);
    CCMoveBy::create(ccTime duration, CCPoint& pos);

    其中,duration參數表示動作持續的時間,pos參數表示移動的終點或距離。對於CCMoveTo,節點會被移動到pos對應的位置;對於CCMoveBy,節點會相對之前的位置移動pos的距離。

  • CCJumpToCCJumpBy:使節點以一定的軌跡跳躍到指定位置。它們的初始化方法如下:

    CCJumpTo::create(ccTime duration, CCPoint pos, float height, int jumps);
    CCJumpBy::create(ccTime duration, CCPoint pos, float height, int jumps);

    其中pos表示跳躍的終點或距離,height表示最大高度,jumps表示跳躍次數。

  • CCBezierToCCBezierBy:使節點進行曲線運動,運動的軌跡由貝塞爾曲線描述。貝塞爾曲線是描述任意曲線的有力工具,在許多軟件(如Adobe Photoshop)中,鋼筆工具就是貝塞爾曲線的應用。實際上,在《捕魚達人》遊戲中,爲了控制魚的遊動,我們就用到了貝塞爾曲線。

每一條貝塞爾曲線都包含一個起點和一個終點。在一條曲線中,起點和終點都各自包含一個控制點,而控制點到端點的連線稱作控制線。控制線決定了從端點發出的曲線的形狀,包含角度和長度兩個參數:角度決定了它所控制的曲線的方向,即這段曲線在這一控制點的切線方向;長度控制曲線的曲率。控制線越長,它所控制的曲線離控制線越近。示例圖如圖4-1所示。

enter image description here[+]查看原圖

圖4-1 三段貝塞爾曲線

任意一段曲線都可以由一段或幾段相連的貝塞爾曲線組成,因此我們只需考慮一段貝塞爾曲線應該如何描述即可。一段獨立的貝塞爾曲線如圖4-2所示。

enter image description here

圖4-2 貝塞爾曲線及其控制點

使用時我們要先創建ccBezierConfig結構體,設置好終點endPosition以及兩個控制點controlPoint_1controlPoint_2後,再把結構體傳入CCBezierToCCBezierBy的初始化方法中:

ccBezierConfig bezier;
bezier.controlPoint_1 = ccp(20, 150);
bezier.controlPoint_2 = ccp(200, 30);
bezier.endPosition = ccp(160, 30);
CFiniteTimeAction * beizerAction = CCBezierTo::create(actualDuration / 4, bezier);

另一類動作是屬性變化動作,它的特點是通過屬性值的逐漸變化來實現動畫效果。例如,下面要介紹的第一個動作CCScaleTo,它會在一段時間內不斷地改變遊戲元素的scale屬性,使屬性值平滑地變化到一個新值,從而使遊戲元素產生縮放的動畫效果。

  • CCScaleToCCScaleBy:產生縮放效果,使節點的縮放係數隨時間線性變化,對應的初始化方法爲:

    CCScaleTo::create(ccTime duration, float s);
    CCScaleBy::create(ccTime duration, float s);

    其中,s爲縮放係數的最終值或變化量。

  • CCRotateToCCRotateBy:產生旋轉效果,對應的初始化方法爲:

    CCRotateTo::create(ccTime duration, float fDeltaAngle);
    CCRotateBy::create(ccTime duration, float fDeltaAngle);

    其中fDeltaAngle的單位是角度,正方向爲順時針方向。

  • CCFadeInCCFadeOut:產生淡入淡出效果,其中前者實現了淡入效果,後者實現了淡出效果,對應的初始化方法爲:

    CCFadeIn::create(ccTime duration);
    CCFadeOut::create(ccTime duration);

    這裏需要說明的是,只有實現了CCRGBAProtocol接口的節點纔可以執行這類動作,這是因爲與透明度或顏色相關的屬性都繼承自CCRGBAProtocol接口。不過許多常見的節點,例如CCSpriteCCLayerColor等,都實現了CCRGBAProtocol接口,因此通常我們不必擔心這個問題。

    以下介紹的幾個動作也有類似的問題。

  • CCFadeTo:用於設置一段時間內透明度的變化效果,其初始化方法爲:

    CCFadeTo::create(ccTime duration, Glubyte opacity);

    參數中的Glubyte是8位無符號整數,因此,opacity可取0至255中的任意整數。與透明度相關的動作只能應用在精靈(CCSprite)上,且子節點不會受到父節點的影響。

  • CCTintToCCTintBy:設置色調變化。這個動作較爲少用,其初始化方法爲:

    CCTintTo::create(ccTime duration,GLubyte r,Glubyte g,Glubyte b);
    CCTintBy::create(float duration, GLshort deltaRed, GLshort deltaGreen, GLshort deltaBlue);

    CCFadeTo類似,rgb的取值範圍也爲0至255。

這一類動作用於實現一些特殊的視覺效果,下面將簡要介紹其中的兩個動作。

  • CCBlink:使目標節點閃爍,其初始化方法爲:

    CCBlink::create(ccTime duration, unsigned int uBlicks);

    其中,uBlicks是閃爍次數。

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


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