文章 [cocos2d] 利用texture atlases生成动画 中介绍了如何生成动画并绑定在CCSprite实例上。
使用该代码遇到了几个问题,值得mark下
问题1、多实例
问题描述:
新建一个PlayerSprite类继承CCSprite,若在PlayerSprite类中使用文中代码生成会出现问题。
//生成sprite(动画将绑定在sprite上) CCSprite *sprite = [CCSprite spriteWithSpriteFrame:[frameCache spriteFrameByName:@"1.png"]];
问题出现在于这句。
因为PlayerSprite已经是CCSprite类,当PlayerSprite实例化时,此行代码会再把CCSprite实例化,之后的动画会绑定在新的sprite上。
解决方法:
1、在PlayerSprite类中删除此句代码,并将之后的所有的sprite改成 self 即可。(注意最后还有一句addChild要删除)
即
1 @interface PlayerSprite : CCSprite 2 @end 3 @implementation PlayerSprite 4 //利用软件将5帧png贴图生成1张大的png贴图和plist 5 CCSpriteFrameCache *frameCache = [CCSpriteFrameCache sharedSpriteFrameCache]; 6 //读取plist并缓存plist包含的贴图 7 [frameCache addSpriteFramesWithFile:@"sprite.plist"]; 8 NSMutableArray *frames = [NSMutableArray arrayWithCapacity:5]; 9 int i; 10 for (i = 1; i <= 5; i++){ 11 //假设命名为1.png 2.png。。。。 12 NSString *file = [NSString stringWithFormat:@"%i.png",i]; 13 //从缓存中读取贴图生成动画帧 14 CCSpriteFrame *frame = [frameCache spriteFrameByName:file]; 15 //加入到动态数组中 16 [frames addObject:frame]; 17 } 18 //利用动画帧生成动画对象 19 CCAnimation *anim = [CCAnimation animationWithSpriteFrames:frames delay:0.05f]; 20 //生成sprite(动画将绑定在sprite上) 21 //此句删除 22 // CCSprite *sprite = [CCSprite spriteWithSpriteFrame:[frameCache spriteFrameByName:@"1.png"]]; 23 //生成在屏幕中间 24 self.position = CGPointMake(screenSize.width / 2, screenSize.height / 2); 25 //利用animate运行动画 26 CCAnimate *animate = [CCAnimate actionWithAnimation:anim]; 27 //重复播放动画 28 CCRepeatForever *repeat = [CCRepeatForever actionWithAction:animate]; 29 [self runAction:repeat]; 30 //加入到场景中 31 //此句也要删除,在适当的地方补回此句代码 32 // [self addChild:sprite z:0 tag:1]; 33 34 @end
2、然后在适当的地方实例化
PlayerSprite *sprite = [PlayerSprite spriteWithSpriteFrame:[frameCache spriteFrameByName:@"1.png"]]; [self addChild:sprite z:0 tag:1];
经验:
copy代码一定要明白每一行代码的用处~
问题2、contentSize为0
问题描述:开始出现问题1的时候我是直接把该代码删除
然后直接在适当的地方使用下面代码实例化。(如果不使用texture的相关属性不会有明显的问题)
PlayerSprite *sprite = [PlayerSprite node];
如果此时要获得sprite大小的话会发现返回值为0。
//sprite的contentSize大小
CGSize size1 = sprite.contentSize; //sprite所使用的贴图大小,使用纹理贴图集会发生问题,返回为纹理贴图的size
CGSize size2 = sprite.texture.contentSize; //返回的是sprite大小(包括旋转,缩放,变换) CGRect rect = sprite.boundingBox;
根据cocos2d库的源代码追溯下去寻找,发现如果使用spriteWithXXX实例化,最终会调用CCTexture2D获得贴图的大小;而直接使用[CCSprite node]初始化则少了该步,最后导致实例没有正确的size值;
1 @implementation CCSprite 2 3 //.... 4 5 -(id)initWithFile:(NSString*)filename{ 6 //.....省略 7 8 CCTexture2D *texture = [[CCTextureCache sharedCache] addImage:filename]; 9 10 if ( texture ){ 11 CGRect rect = CGRectZero; 12 rect.size = texture.contentSize; 13 return [self initWithTexture:texture rect:rect]; 14 } 15 16 [self release]; 17 return nil; 18 19 } 20 21 //..... 22 @end
解决方法:
同问题1,使用spriteWithXXX实例化即可
PlayerSprite *sprite = [PlayerSprite spriteWithSpriteFrame:[frameCache spriteFrameByName:@"1.png"]]; [self addChild:sprite z:0 tag:1];
经验:
由此可以看出CCSprite与CCLayer,CCScene的继承类实例过程有不同的地方,
CCSprite的继承类用spriteWithXXX实例化的话,不会调用继承类的init方法,而只会调用CCSprite的init方法。
而对于CCLlayer与CCScene的继承类,因为是用XXLayer *layer = [XXLayer node]此类方式实例化,而 node实则上是[[[self alloc] init] autorelease]的缩写,会调用继承类的init方法。
问题3、贴图复用/获得已缓存的贴图
PlayerSprite *sprite = [PlayerSprite spriteWithSpriteFrame:[frameCache spriteFrameByName:@"1.png"]];
可能有人会发现在问题1、2中sprite使用的贴图是frameCache中获得的,那么有没方法获得已经缓存好的贴图而不是每次都要重新加载呢?
Learn iPhone and iPad Cocos2D Game Development一书中作者在第七章中介绍到一个比较好的方法
( Scrolling With Joy -> Advanced Parallax Scrolling -> Re-creating the Background in Code)
待续