Cocos-2d 关于多个CCSprite精灵播放同一个CCAction动画问题
2013-03-22 10:01
851 查看
问题描述:
在Cocos-2d场景动画中,常常出现多个Sprite的同一行为动画
假设场景中此时有两个精灵sprite1,sprite2
他们其实点分别在场景左侧,需要完成的动作CCMoteTo到场景最右侧
初始状态如下图:
初始尝试:
初始效果展示:
点击Play按钮后效果图:
我们发现尽管在代码中sprite1和sprite2都是runAction:move,但是貌似只有下方的sprite2执行了此动作,而sprite1没有执行。效果不尽人意!
原理解释:
我们跟踪一下
[sprite1
runAction:move];
重点看一下[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 类型)的成员,表示该动画是有哪一个演员来执行的。
所以
对于同一个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 特此做出感谢!)
解决方案:
很简单:把move 复制一份,move的副本的target为sprite2,与sprite1无关,这样两个精灵就可以同时执行了。
效果图:
顺便提一句:
1.[CCNode stopAction: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.m 这里只贴出有用部分
在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]; }
相关文章推荐
- Cocos-2d 关于多个CCSprite精灵播放同一个CCAction动画问题
- cocos 3.X 关于代码实现一段动画的播放 20180312 day4
- 关于Unity动画不能播放的问题
- 在Windows新建一个cocos-2d的项目遇到的问题
- 关于在一个页面中包含15个几以上flash mp3 players时,在非IE下播放不正常的问题和解决方案
- cocos2d-x tips(二)关于cocosbuilder中的动画和2dx的action同时正常运行的问题
- 2D GAME 关于视频播放的闪烁问题
- 关于Cocos2d-x中获取正在播放动画第几帧的问题
- [一位菜鸟的COCOS-2D编程之路]精灵表单的制作以及简易动画的生成
- 关于,VS2010 里面 一个解决方案包含多个win32程序,调试的问题~~~happy
- 一个关于在Eclipse中播放音频文件的问题
- 一个程序中关于多个osgGA::GUIEventHandler同时存在的问题
- 关于动画的一个小问题
- 关于cocosbuilder,引入ccbfile后,播放的问题。
- Cocos-2d js 多个精灵都调用同一个动作 执行不成功(加“.copy()“)
- 关于多个fragment复用同一个占位View产生的问题及解决
- 关于视频+音频APP,存在一个用户播放,其他用户就卡死的问题。视频分发?
- 关于Unity中如何判断一个动画播放结束
- 关于代码控制unity中动画播放的问题
- 关于DreamWaver制作的网页无法播放背景音乐和动画的问题