我們玩的游戲一般都可以看到精靈的運動,游戲的世界就是一個運動的世界,而所有的這些動作都可以分為一些基本的動作和動作的組合,今天就來學習一下動作類CCAction,首先看一下類之間的繼承關系。
CCAction類下派生了三個動作類,執行動作的類是CCNode以及它的子類,通過函數runAction()來執行動作,其中CCFiniteTimeAction之下是常用的瞬時動作和延時動作。動作從本質上來說就是改變節點的屬性,瞬時動作就是改變這些屬性不需要時間,瞬時就完成了,而延時動作改變這些屬性需要一些時間,可以通過參數來設置這個時間,下面是瞬時動作和延時動作的例子,解釋看源代碼。點擊下圖查看效果。
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
39
40
41
42
43
44
45
46
47
48
49
50
51
|
bool HelloWorld::init() { bool bRet = false ; do { CC_BREAK_IF(! CCLayer::init()); //創建一個精靈 CCSprite * sprite = CCSprite::create( "image.png" ); sprite->setPosition(ccp(240,160)); //最后一個參數是精靈的tag,以便在眾多的子節點中,通過getChildByTag()找到該節點 this ->addChild(sprite,0,0); //創建菜單 CCMenuItemFont * fontMenu1 = CCMenuItemFont::create( "start" ); CCMenuItemFont * fontMenu2 = CCMenuItemFont::create( "stop" ); //和move函數相互綁定 CCMenuItemToggle * toggleMenu = CCMenuItemToggle::createWithTarget( this ,menu_selector(HelloWorld::move), fontMenu1,fontMenu2,NULL); CCMenu * menu = CCMenu::create(toggleMenu,NULL); menu->setPosition(ccp(420,40)); this ->addChild(menu); bRet = true ; } while (0); return bRet; } void HelloWorld::move(CCObject* pSender) { //通過tag獲得添加的精靈 CCSprite * sprite = (CCSprite *) this ->getChildByTag(0); CCMenuItemToggle * toggleMenu = (CCMenuItemToggle *)pSender; //瞬時動作CCPlace,改變了精靈的坐標,可以用sprite->setPosition(ccp(60,160))代替,但寫成動作可以加入到動作序列中 CCPlace * action1 = CCPlace::create(ccp(60,160)); //瞬時動作,使精靈做X軸的翻轉 CCFlipX * action2 = CCFlipX::create( true ); if (toggleMenu->getSelectedIndex() == 1) { //通過runAction精靈執行動作 sprite->runAction(action2); } else if (toggleMenu->getSelectedIndex() == 0) { } } |
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
|
void HelloWorld::move(CCObject* pSender) { //通過tag獲得添加的精靈 CCSprite * sprite = (CCSprite *) this ->getChildByTag(0); CCMenuItemToggle * toggleMenu = (CCMenuItemToggle *)pSender; //延時動作,第一個參數是執行動作所需要的時間,moveTo是絕對的,就是移動到哪個坐標,而moveBy是相對的 //傳入的ccp(10,0)是一個向量,精靈沿著x軸移動了10個像素 CCMoveTo * moveTo = CCMoveTo::create(2.0,ccp(240,180)); CCMoveBy * moveBy = CCMoveBy::create(2.0,ccp(10,0)); //rotate按照一定的角度旋轉,to強調的是結果,現在是0度,會旋轉到90度,如果現在是90度,還是會在90度 CCRotateTo * rotateTo = CCRotateTo::create(2.0,90); //by強調旋轉的相對角度,不論現在多少度都會旋轉90度,以to和by結尾的動作類都是相同的道理 CCRotateBy * rotateBy = CCRotateBy::create(2.0,90); if (toggleMenu->getSelectedIndex() == 1) { //通過runAction精靈執行動作 //sprite->runAction(moveTo); //sprite->runAction(moveBy); sprite->runAction(rotateBy); } else if (toggleMenu->getSelectedIndex() == 0) { } } |
以by結尾的延時動作都可以通過reverse()獲得它的反動作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
void HelloWorld::move(CCObject* pSender) { //通過tag獲得添加的精靈 CCSprite * sprite = (CCSprite *) this ->getChildByTag(0); CCMenuItemToggle * toggleMenu = (CCMenuItemToggle *)pSender; //第二個參數是跳躍的向量,第三個參數是跳躍的高度,第四個參數是跳躍的次數 CCJumpBy * jumpBy = CCJumpBy::create(2.0,ccp(100,30),50,4); //以by結尾的延時動作都可以通過reverse()獲得它的反動作 CCActionInterval * jumpBack = jumpBy->reverse(); if (toggleMenu->getSelectedIndex() == 1) { //通過runAction精靈執行動作 sprite->runAction(jumpBy); } else if (toggleMenu->getSelectedIndex() == 0) { sprite->runAction(jumpBack); } } |
延時動作中還有倆個重復動作的函數,就是重復不斷的執行某一個動作,看下他們的繼承關系。
下面是實現的源代碼。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
void HelloWorld::move(CCObject* pSender) { //通過tag獲得添加的精靈 CCSprite * sprite = (CCSprite *) this ->getChildByTag(0); CCMenuItemToggle * toggleMenu = (CCMenuItemToggle *)pSender; //第一個參數傳入重復的動作,第二個參數傳入重復的次數 CCRepeat * repeat = CCRepeat::create(rotateBy,4); //永遠重復執行一個動作 CCRepeatForever * repeatForever = CCRepeatForever::create(rotateBy); if (toggleMenu->getSelectedIndex() == 1) { //通過runAction精靈執行動作 sprite->runAction(repeatForever); } else if (toggleMenu->getSelectedIndex() == 0) { } } |
下面再列舉一下基本常用的瞬時動作和延續動作。
瞬時動作:
放置Place:效果類似于 setPosition = ccp(x, y)。
隱藏Hide:效果類似于setVisible:false。
顯示Show:效果類似于setVisible:true。
可見切換:ToggleVisibility。
延時動作:
移動到CCMoveTo
移動CCMoveBy
跳躍到CCJumpTo設置終點位置和跳躍的高度和次數。
跳躍CCJumpBy設置終點位置和跳躍的高度和次數。
貝塞爾CCBezierBy支持 3 次貝塞爾曲線:P0-起點,P1-起點切線方向,P2-終點切線方向,P3-終點。
放大到CCScaleTo設置放大倍數,是浮點型。
放大CCScaleBy
旋轉到CCRotateTo
旋轉CCRotateBy
閃爍CCBlink設定閃爍次數
色調變化到CCTintTo
色調變換CCTintBy
變暗到CCFadeTo
由無變亮CCFadeIn
由亮變無CCFadeOut
每個函數的具體用法請大家自行試驗。
接下來說一下同步動作序列和順序動作序列。同步動作序列,就是幾個動作同時執行,順序動作序列,就是傳入的幾個動作按照順序順序執行。以下是他們的繼承關系圖。
接下來看一下實現的源代碼
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
|
void HelloWorld::move(CCObject* pSender) { //通過tag獲得添加的精靈 CCSprite * sprite = (CCSprite *) this ->getChildByTag(0); CCMenuItemToggle * toggleMenu = (CCMenuItemToggle *)pSender; //創建如下幾個動作 CCMoveBy * move = CCMoveBy::create(2.0,ccp(100,0)); CCRotateBy * rotate = CCRotateBy::create(2.0,720); CCFiniteTimeAction * moveBack = move->reverse(); CCFiniteTimeAction * rotateBack = rotate->reverse(); CCFlipY * flip = CCFlipY::create( true ); //同步動作序列,傳入的幾個動作同時執行,執行的整個時間是最長的一個動作的執行時間,參數類型是CCFiniteTimeAction //傳入的動作(動作的實質就是改變節點的屬性)所影響的屬性不要有沖突 CCSpawn * spawn = CCSpawn::create(move,rotate,NULL); CCSpawn * spawnBack = CCSpawn::create(moveBack,rotateBack,NULL); //順序動作序列,傳入的幾個動作按照傳入的順序順序執行,執行的整體時間是所有動作的時間之和 CCSequence * sequence = CCSequence::create(move,rotate,flip,NULL); CCSequence * sequenceBack = CCSequence::create(moveBack,rotateBack,flip->reverse(),NULL); if (toggleMenu->getSelectedIndex() == 1) { sprite->runAction(spawn); //sprite->runAction(sequence); } else if (toggleMenu->getSelectedIndex() == 0) { sprite->runAction(spawnBack); //sprite->runAction(sequenceBack); } } |
接下來介紹CCAction的另外來個子類,CCFollow實現鏡頭跟隨的效果,類似于我們在橫版過關游戲中看到的人物永遠在屏幕中間,而背景在移動,不過它們的實現具體是不是靠這個類就不知道了。CCSpeed可以實現快進和慢放的效果,就是改變了執行的速度。以下是源代碼。
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
39
40
41
|
void HelloWorld::move(CCObject* pSender) { //通過tag獲得添加的精靈 CCSprite * sprite = (CCSprite *) this ->getChildByTag(0); CCMenuItemToggle * toggleMenu = (CCMenuItemToggle *)pSender; //創建如下幾個動作 CCMoveBy * move = CCMoveBy::create(2.0,ccp(100,0)); CCRotateBy * rotate = CCRotateBy::create(2.0,720); CCFiniteTimeAction * moveBack = move->reverse(); CCFiniteTimeAction * rotateBack = rotate->reverse(); CCFlipY * flip = CCFlipY::create( true ); //同步動作序列,傳入的幾個動作同時執行,執行的整個時間是最長的一個動作的執行時間,參數類型是CCFiniteTimeAction //傳入的動作(動作的實質就是改變節點的屬性)所影響的屬性不要有沖突 CCSpawn * spawn = CCSpawn::create(move,rotate,NULL); CCSpawn * spawnBack = CCSpawn::create(moveBack,rotateBack,NULL); //順序動作序列,傳入的幾個動作按照傳入的順序順序執行,執行的整體時間是所有動作的時間之和 CCSequence * sequence = CCSequence::create(move,rotate,flip,NULL); CCSequence * sequenceBack = CCSequence::create(moveBack,rotateBack,flip->reverse(),NULL); //CCFollow實現一個節點跟隨一個節點運動,傳入的參數是要跟隨的節點 CCFollow * follow = CCFollow::create(sprite); //執行這個動作的是要跟隨的節點,一般是層,效果累世于橫版過關游戲中的場景 this ->runAction(follow); if (toggleMenu->getSelectedIndex() == 1) { sprite->runAction(spawn); //sprite->runAction(sequence); } else if (toggleMenu->getSelectedIndex() == 0) { sprite->runAction(spawnBack); //sprite->runAction(sequenceBack); } } |
以下是CCSpeed的實現,在上述代碼的基礎上做了一點修改。
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
39
40
41
42
43
44
45
|
void HelloWorld::move(CCObject* pSender) { //通過tag獲得添加的精靈 CCSprite * sprite = (CCSprite *) this ->getChildByTag(0); CCMenuItemToggle * toggleMenu = (CCMenuItemToggle *)pSender; //創建如下幾個動作 CCMoveBy * move = CCMoveBy::create(2.0,ccp(100,0)); CCRotateBy * rotate = CCRotateBy::create(2.0,720); CCFiniteTimeAction * moveBack = move->reverse(); CCFiniteTimeAction * rotateBack = rotate->reverse(); CCFlipY * flip = CCFlipY::create( true ); //同步動作序列,傳入的幾個動作同時執行,執行的整個時間是最長的一個動作的執行時間,參數類型是CCFiniteTimeAction //傳入的動作(動作的實質就是改變節點的屬性)所影響的屬性不要有沖突 CCSpawn * spawn = CCSpawn::create(move,rotate,NULL); CCSpawn * spawnBack = CCSpawn::create(moveBack,rotateBack,NULL); //順序動作序列,傳入的幾個動作按照傳入的順序順序執行,執行的整體時間是所有動作的時間之和 CCSequence * sequence = CCSequence::create(move,rotate,flip,NULL); CCSequence * sequenceBack = CCSequence::create(moveBack,rotateBack,flip->reverse(),NULL); //CCFollow實現一個節點跟隨一個節點運動,傳入的參數是要跟隨的節點 CCFollow * follow = CCFollow::create(sprite); //執行這個動作的是要跟隨的節點,一般是層,效果累世于橫版過關游戲中的場景 this ->runAction(follow); //CCSpeed分裝了一個動作類,第二個參數是要改變的速度的倍數 CCSpeed * speed1 = CCSpeed::create(spawn,2.0); CCSpeed * speed2 = CCSpeed::create(spawnBack,2.0); if (toggleMenu->getSelectedIndex() == 1) { sprite->runAction(speed1); //sprite->runAction(sequence); } else if (toggleMenu->getSelectedIndex() == 0) { sprite->runAction(speed2); //sprite->runAction(sequenceBack); } } |
以下再來介紹一下CCCallFunc家族類的使用方法,它們也是一個動作類,一般用在順序動作序列中執行的最后一個動作,目的是調用一個函數,來完成一些功能。以下是這些類的繼承關系。
接下來貼上源代碼,注釋是對各個類的使用的詳細講解。
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
bool HelloWorld::init() { bool bRet = false ; do { CC_BREAK_IF(! CCLayer::init()); CCSprite * sprite = CCSprite::create( "image.png" ); sprite->setPosition(ccp(240,160)); this ->addChild(sprite,0,0); //創建一個菜單,添加一個run事件 CCMenuItemFont * fontMenu = CCMenuItemFont::create( "begin" , this ,menu_selector(HelloWorld::run)); CCMenu * menu = CCMenu::create(fontMenu,NULL); menu->setPosition(ccp(400,40)); this ->addChild(menu); bRet = true ; } while (0); return bRet; } void HelloWorld::run(CCObject* pSender) { CCSprite * sprite = (CCSprite *) this ->getChildByTag(0); //創建延時動作 CCRotateBy * rotate = CCRotateBy::create(2.0,3*360); //CCCallFunc,為這個動作綁定一個函數,執行這個動作的時候會調用這個函數,創建以下四個動作的時候使用了不同的選擇器,但名字和各個動作有關 CCCallFunc * func = CCCallFunc::create( this ,callfunc_selector(HelloWorld::show)); //CCCallFuncN(N就是node的意思),與上邊不同的是,綁定的函數需要一個參數,這個傳入的參數就是執行這個動作的節點 CCCallFuncN * funcN = CCCallFuncN::create( this ,callfuncN_selector(HelloWorld:: remove )); int num = 10; //CCCallFuncND(D就是data的意思),這次綁定的函數,不僅需要綁定動作的節點作為參數傳遞,還帶了一個void *類型的參數,代表可以是任何類型 CCCallFuncND *funcND = CCCallFuncND::create( this ,callfuncND_selector(HelloWorld::showData),( void *)num); CCSprite * sprite2 = CCSprite::create( "image2.png" ); //CCCallFuncO(O就是object的意思)這次需要傳入的參數是CCObject *類型的 CCCallFuncO * funcO = CCCallFuncO::create( this ,callfuncO_selector(HelloWorld::showSprite),sprite2); //創建順序動作序列 //CCSequence * sequence = CCSequence::create(rotate,func,funcND,NULL); //CCSequence * sequence = CCSequence::create(rotate,func,funcN,NULL); CCSequence * sequence = CCSequence::create(rotate,func,funcN,funcO,NULL); sprite->runAction(sequence); } //以下函數不要忘記在頭文件中聲明,注意每個函數的參數 void HelloWorld::show() { CCLabelTTF * ttf = CCLabelTTF::create( "action end" , "Arial" ,32); ttf->setPosition(ccp(240,260)); this ->addChild(ttf); } void HelloWorld:: remove (CCNode * node) { //沒有通過getChildByTag()函數獲得執行動作的精靈,而是使用remove傳來的參數 CCSprite * sprite = (CCSprite *)node; //true表示sprite不僅會移除,而且這個節點上的所有操作和回調都將刪除 sprite->removeFromParentAndCleanup( true ); //通過以下的方法可以實現相同的效果,只是函數執行的對象不同 //this->removeChild(sprite,true); } void HelloWorld::showData(CCNode * node, void * data) { CCSprite * sprite = (CCSprite *)node; this ->removeChild(sprite, true ); CCLog( "num = %d" ,data); } void HelloWorld::showSprite(CCObject * sender) { CCSprite * sprite = (CCSprite *)sender; sprite->setPosition(ccp(240,160)); this ->addChild(sprite); } |