Cocos-2d 關於多個CCSprite精靈播放同一個CCAction動畫有關問題

tags:    時間:2013-12-28 02:07:32
Cocos-2d 關於多個CCSprite精靈播放同一個CCAction動畫問題

問題描述:

在Cocos-2d場景動畫中,常常出現多個Sprite的同一行為動畫

假設場景中此時有兩個精靈sprite1,sprite2

他們其實點分別在場景左側,需要完成的動作CCMoteTo到場景最右側

初始狀態如下圖:

初始嘗試:

- (void)playAction {     //1.試圖兩個精靈播放同一個動畫     CGSize size = [[CCDirector sharedDirector] winSize];      CCMoveBy *move = [CCMoveBy actionWithDuration:3.0f position:ccp(size.width-[sprite1 textureRect].size.width,0)];     [sprite1 runAction:move];     [sprite2 runAction:move]; }

初始效果展示

點擊Play按鈕後效果圖:



我們發現儘管在代碼中sprite1和sprite2都是runAction:move,但是貌似只有下方的sprite2執行了此動作,而sprite1沒有執行。效果不盡人意!

原理解釋:

我們跟蹤一下

[sprite1 runAction:move];

-(CCAction*) runAction:(CCAction*) action { 	NSAssert( action != nil, @"Argument must be non-nil");  	[actionManager_ addAction:action target:self paused:!isRunning_]; 	return action; }

重點看一下[addAction:action target:self paused:!isRunning_];


-(void) addAction:(CCAction*)action target:(id)target paused:(BOOL)paused

{

 //有效性判斷。

NSAssert( action != nil, @"Argument action must be non-nil");

NSAssert( target != nil, @"Argument target must be non-nil");

 //定義一個哈希表項的指針變數並置空。

tHashElement *element = NULL;

//通過這個指針,找到對應的哈希表項返回給element; 

HASH_FIND_INT(targets, &target, element);

 //如果找不到。則代表新加入的哈希表項。則申請內存創建此哈希表項,並將其加入哈希表中。

if( ! element ) {

element = calloc( sizeof( *element ), 1 );

element->paused = paused;

element->target = [target retain];

HASH_ADD_INT(targets, target, element);

// CCLOG(@"cocos2d: ---- buckets: %d/%d - %@", targets->entries, targets->size, element->target);


}


//為哈希表項element申請內存,以存放動畫集 

[self actionAllocWithHashElement:element];

 //判斷action是否在element的動畫集中。確保只放入一次。(這也是為什麼一個動畫不能重複添加的原因!

NSAssert( !ccArrayContainsObject(element->actions, action), @"runAction: Action already running");

//將action放入element的動畫集中。 

ccArrayAppendObject(element->actions, action);

//設置是哪個CCNode要進行當前動畫 (重點原因在這裡!!!)

[action startWithTarget:target];

}


每一個CCAction對象都有且僅有一個target(id 類型)的成員,表示該動畫是有哪一個演員來執行的。

所以

[sprite1 runAction:move]; [sprite2 runAction:move];

對於同一個move(CCMoveTo對象)來說,第一次[sprite1 runAction:move];我們將move的target成員設置成了sprite1;

而第二次[sprite2 runAction:move];我們又將move的target成員設置成了sprite2;這樣第一次註冊sprite1的動畫move就會失效;

因此效果上只有sprite2在執行move了!

本段參考「紅孩兒Cocos2d-x 2.0 之 Actions 「三板斧」 之一」文章:http://www.2cto.com/kf/201211/169345.html 特此做出感謝!

解決方案:

- (void)playAction {     //1.試圖兩個精靈播放同一個動畫     CGSize size = [[CCDirector sharedDirector] winSize];      CCMoveBy *move = [CCMoveBy actionWithDuration:3.0f position:ccp(size.width-[sprite1 textureRect].size.width,0)];     [sprite1 runAction:move];     //[sprite2 runAction:move];          //2.改進     [sprite2 runAction:[move copy]]; }

很簡單:把move 複製一份,move的副本的target為sprite2,與sprite1無關,這樣兩個精靈就可以同時執行了。

效果圖:





順便提一句:

1.[CCNode stopAction:action]時

-(void) stopAction: (CCAction*) action { 	[actionManager_ removeAction:action]; }

action會被釋放


2.

-(void) removeFromParentAndCleanup:(BOOL)cleanup;

-(void) removeChild: (CCNode*)node cleanup:(BOOL)cleanup;

-(void) removeChildByTag:(NSInteger) tag cleanup:(BOOL)cleanup;

-(void) removeAllChildrenWithCleanup:(BOOL)cleanup;

這是CCNODE的刪除對象的方法,後面帶了一個cleanup參數,如果你將cleanup的值設為YES,系統在刪除對象的時候會對自動對當前對象進行stopAllActions的操作,也會釋放action。


3.無論上述哪種方式,一定注意不要內存泄露!

源碼分享:

HelloWorldLayer.h

#import <GameKit/GameKit.h>  // When you import this file, you import all the cocos2d classes #import "cocos2d.h"  // HelloWorldLayer @interface HelloWorldLayer : CCLayer  {     BOOL isPlay_;     CCSprite *sprite1;     CCSprite *sprite2; }  // returns a CCScene that contains the HelloWorldLayer as the only child +(CCScene *) scene;  @end

HelloWorldLayer.m 這裡只貼出有用部分

-(id) init { 	// always call "super" init 	// Apple recommends to re-assign "self" with the "super's" return value 	if( (self=[super init]) )      {		 		// create and initialize a Label 		CCLabelTTF *label = [CCLabelTTF labelWithString:@"TwoActionDemo" fontName:@"Marker Felt" fontSize:32]; 		CGSize size = [[CCDirector sharedDirector] winSize]; 		label.position =  ccp( size.width /2 , 4*size.height/5 ); 		[self addChild: label];                  //實例化兩個精靈         sprite1 = [CCSprite spriteWithFile:@"Icon.png"];         sprite2 = [CCSprite spriteWithFile:@"Icon.png"];         sprite1.position = ccp([sprite1 textureRect].size.width/2 , size.height/2);         sprite2.position = ccp([sprite1 textureRect].size.width/2 , size.height/4);         [self addChild:sprite1];         [self addChild:sprite2]; 		         //菜單按鈕         CCMenuItem *play = [CCMenuItemFont itemWithString:@"Play"];         CCMenuItem *stop = [CCMenuItemFont itemWithString:@"Stop"];         CCMenuItem *menuBtn = [CCMenuItemToggle itemWithTarget:self selector:@selector(btnPressed) items:play,stop, nil]; 		CCMenu *menu = [CCMenu menuWithItems:menuBtn, nil];         menu.position = ccp(9*size.width /10 , size.height/10);         [self addChild:menu]; 		         isPlay_ = NO;  	} 	return self; }

- (void)btnPressed {     isPlay_ = !isPlay_;     YES == isPlay_ ? [self playAction]:[self stopToOrignPostion]; }  - (void)playAction {     //1.試圖兩個精靈播放同一個動畫     CGSize size = [[CCDirector sharedDirector] winSize];      CCMoveBy *move = [CCMoveBy actionWithDuration:3.0f position:ccp(size.width-[sprite1 textureRect].size.width,0)];     [sprite1 runAction:move];     //[sprite2 runAction:move];          //2.改進     [sprite2 runAction:[move copy]]; }  - (void)stopToOrignPostion {     [self stopAllActions];          CGSize size = [[CCDirector sharedDirector] winSize];          CCPlace *place1 = [CCPlace actionWithPosition:ccp([sprite1 textureRect].size.width/2 , size.height/2)];     CCPlace *place2 = [CCPlace actionWithPosition:ccp([sprite1 textureRect].size.width/2 , size.height/4)];     [sprite1 runAction:place1];     [sprite2 runAction:place2]; }

// on "dealloc" you need to release all your retained objects - (void) dealloc { 	[sprite1 release];     	[sprite2 release]; 	[super dealloc]; }






推薦閱讀文章

Bookmark the permalink ,來源:互聯網