Cocos2d-x爲我們提供了一套動作的複合機制,允許我們組合各種基本動作,產生更爲複雜和生動的動作效果。複合動作是一類特殊的動作,因此它也需要使用CCNode
的runAction
方法執行。而它的特殊之處在於,作爲動作容器,複合動作可以把許多動作組合成一個複雜的動作。因此,我們通常會使用一個或多個動作來創建複合動作,再把動作交給節點執行。
複合動作十分靈活,這是由於複合動作本身也是動作,因此也可以作爲一個普通的動作嵌套在其他複合動作中。
1. 重複(CCRepeat
/CCRepeatForever
)
有的情況下,動作只需要執行一次即可,但我們還常常遇到一個動作反覆執行的情況。對於一些重複的動作,如魚的擺動、能量槽的轉動,我們可以通過CCRepeat
與CCRepeatForever
這兩個方式重複執行:
CCRepeat* CCRepeat::create(CCFiniteTimeAction *pAction, unsigned int times);
CCRepeatForever *CCRepeatForever::create(CCActionInterval *pAction);
在上述代碼中,pAction
參數表示需要重複的動作,第一個方法允許指定動作的重複次數,第二個方法使節點一直重複該動作直到動作被停止。
2. 並列(CCSpawn
)
指的是使一批動作同時執行。在《捕魚達人》遊戲中,魚一邊沿曲線遊動一邊擺尾巴,炮彈一邊發射一邊噴射氣體,金幣一邊旋轉一邊移動等動作,都可以通過CCSpawn
來實現。CCSpawn
從CCActionInterval
派生而來的,它提供了兩個工廠方法:
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
)並不被支持。
在實現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;
}
衆所周知,遞歸往往會犧牲一些效率,但能換來代碼的簡潔。在這兩個複合動作中,細節處理得十分優雅,所有的操作都只需要針對兩個動作實施,多個動作的組合會被自動變換爲遞歸實現:
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種位置變化動作類型,下面將簡要介紹這幾種動作。
-
CCMoveTo
和CCMoveBy
:用於使節點做直線運動。設置了動作時間和終點位置後,節點就會在規定時間內,從當前位置直線移動到設置的終點位置。它們的初始化方法分別爲:CCMoveTo::create(ccTime duration, CCPoint& pos); CCMoveBy::create(ccTime duration, CCPoint& pos);
其中,
duration
參數表示動作持續的時間,pos
參數表示移動的終點或距離。對於CCMoveTo
,節點會被移動到pos
對應的位置;對於CCMoveBy
,節點會相對之前的位置移動pos
的距離。 -
CCJumpTo
和CCJumpBy
:使節點以一定的軌跡跳躍到指定位置。它們的初始化方法如下:CCJumpTo::create(ccTime duration, CCPoint pos, float height, int jumps); CCJumpBy::create(ccTime duration, CCPoint pos, float height, int jumps);
其中
pos
表示跳躍的終點或距離,height
表示最大高度,jumps
表示跳躍次數。 -
CCBezierTo
和CCBezierBy
:使節點進行曲線運動,運動的軌跡由貝塞爾曲線描述。貝塞爾曲線是描述任意曲線的有力工具,在許多軟件(如Adobe Photoshop)中,鋼筆工具就是貝塞爾曲線的應用。實際上,在《捕魚達人》遊戲中,爲了控制魚的遊動,我們就用到了貝塞爾曲線。
每一條貝塞爾曲線都包含一個起點和一個終點。在一條曲線中,起點和終點都各自包含一個控制點,而控制點到端點的連線稱作控制線。控制線決定了從端點發出的曲線的形狀,包含角度和長度兩個參數:角度決定了它所控制的曲線的方向,即這段曲線在這一控制點的切線方向;長度控制曲線的曲率。控制線越長,它所控制的曲線離控制線越近。示例圖如圖4-1所示。
圖4-1 三段貝塞爾曲線
任意一段曲線都可以由一段或幾段相連的貝塞爾曲線組成,因此我們只需考慮一段貝塞爾曲線應該如何描述即可。一段獨立的貝塞爾曲線如圖4-2所示。
圖4-2 貝塞爾曲線及其控制點
使用時我們要先創建ccBezierConfig
結構體,設置好終點endPosition
以及兩個控制點controlPoint_1
和controlPoint_2
後,再把結構體傳入CCBezierTo
或CCBezierBy
的初始化方法中:
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
屬性,使屬性值平滑地變化到一個新值,從而使遊戲元素產生縮放的動畫效果。
-
CCScaleTo
和CCScaleBy
:產生縮放效果,使節點的縮放係數隨時間線性變化,對應的初始化方法爲:CCScaleTo::create(ccTime duration, float s); CCScaleBy::create(ccTime duration, float s);
其中,
s
爲縮放係數的最終值或變化量。 -
CCRotateTo
和CCRotateBy
:產生旋轉效果,對應的初始化方法爲:CCRotateTo::create(ccTime duration, float fDeltaAngle); CCRotateBy::create(ccTime duration, float fDeltaAngle);
其中
fDeltaAngle
的單位是角度,正方向爲順時針方向。 -
CCFadeIn
和CCFadeOut
:產生淡入淡出效果,其中前者實現了淡入效果,後者實現了淡出效果,對應的初始化方法爲:CCFadeIn::create(ccTime duration); CCFadeOut::create(ccTime duration);
這裏需要說明的是,只有實現了
CCRGBAProtocol
接口的節點纔可以執行這類動作,這是因爲與透明度或顏色相關的屬性都繼承自CCRGBAProtocol
接口。不過許多常見的節點,例如CCSprite
與CCLayerColor
等,都實現了CCRGBAProtocol
接口,因此通常我們不必擔心這個問題。以下介紹的幾個動作也有類似的問題。
-
CCFadeTo
:用於設置一段時間內透明度的變化效果,其初始化方法爲:CCFadeTo::create(ccTime duration, Glubyte opacity);
參數中的
Glubyte
是8位無符號整數,因此,opacity
可取0至255中的任意整數。與透明度相關的動作只能應用在精靈(CCSprite
)上,且子節點不會受到父節點的影響。 -
CCTintTo
和CCTintBy
:設置色調變化。這個動作較爲少用,其初始化方法爲:CCTintTo::create(ccTime duration,GLubyte r,Glubyte g,Glubyte b); CCTintBy::create(float duration, GLshort deltaRed, GLshort deltaGreen, GLshort deltaBlue);
與
CCFadeTo
類似,r
、g
和b
的取值範圍也爲0至255。
這一類動作用於實現一些特殊的視覺效果,下面將簡要介紹其中的兩個動作。
-
CCBlink
:使目標節點閃爍,其初始化方法爲:CCBlink::create(ccTime duration, unsigned int uBlicks);
其中,
uBlicks
是閃爍次數。 -
CCAnimation
:播放幀動畫,用幀動畫的形式實現動畫效果,例如魚的遊動。