Cocos-2d 关于多个CCSprite精灵播放同一个CCAction动画问题

来源:互联网 发布:淘宝购买服务在哪里 编辑:程序博客网 时间:2024/05/17 01:11

问题描述:

在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没有执行。效果不尽人意!

原理解释:

我们跟踪一下

[sprite1runAction: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 = [targetretain];

HASH_ADD_INT(targets,target, element);

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


}


//为哈希表项element申请内存,以存放动画集 

[selfactionAllocWithHashElement: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 valueif( (self=[super init]) )     {// create and initialize a LabelCCLabelTTF *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];}






原创粉丝点击