通过对导演、场景、层和节点的剖析,现在我们已经可以写出一个完整的游戏体系了,在实际应用中,场景一般都是作为游戏的关卡,层作为场景的组成元素(比如UI层,背景层), 导演根据游戏的胜负来激活不同的场景,作为关卡的切换。(以上是常规游戏的流程,仅作参考)
但是,单凭这些还不能作出一款高质量的游戏,优秀的游戏不仅要能玩,最关键的还要好玩,好玩的游戏自然少不了绚丽多彩的视觉效果。和其它主流2D引擎一样,cocos2d的图形显示也是靠精灵实现的,就是说,游戏中不论是UI还是人物、背景,只要是我们能看到的,都和精灵脱不了干系,因此它是我们在开发中打交道最多的类之一,本章咱们就重点研究一下cocos2d的精灵体系。
在研究代码之前,我们先要搞清楚精灵是什么,它的职能有哪些,这里我先给没有接触过游戏开发的童鞋普及一下,有过开发经验的可自行跳过~
说白一点,精灵就是将图形资源加载到内存中,并根据游戏需要将其显示到屏幕中的工具,游戏中大到背景、UI,小到NPC、道具,只要是用图片展示的,都是精灵或它的子类。从技术上讲,精灵是一个可以不断变化的图片,这些变化包括:位置移动、旋转、缩放、换帧(就是像动画片一样播放几张连续的图片,每帧换一张,形成一个动画效果)。其实我在前两章的例子中已经用到了精灵,当然只是使用了它的几个基本方法,现在我就较为系统的解释一下它的工作原理。
精灵相关类的关系图
CCSprite
这个类就是cocos2d中的精灵类,它可以说是CCNode在游戏中的主要表现形式(它继承自CCNode),在代码中我们可以这样理解,CCSprite除了节点的功能外,还封装了一张图片,在游戏中一个2D图形就是一个精灵类的对象。还有一点我们需要注意,就是cocos2d是用3d的方式绘制2d图形的,精灵绘制图像用的是openGL ES(移动平台用的3d引擎,android用的也是它),因此图片在内存中实际上是以Texture(贴图)的形式存在的。下面我们来看看它的方法有哪些:
+(id)spriteWithTexture:(CCTexture2D*)texture
根据一个CCTexture2D创建并返回一个精灵对象,就是封装了alloc、init和autorelease的静态初始化方法。前面说过,精灵是用Texture绘制的,CCTexture2D就是封装了Texture的类,精灵必须被赋予一个CCTexture2D的对象才能工作。
+(id)spriteWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
增加了读取范围的创建方法,该方法只会将范围内的图像加载到精灵中。
+(id)spriteWithFile:(NSString*)filename
根据图片资源创建并返回精灵对象,filename是文件(通常是png)的相对路径,即前面省略了bundle的路径。
+(id)spriteWithFile:(NSString*)filename rect:(CGRect)rect
也是根据图片资源创建并返回精灵对象,只是增加了读取范围,只会加载范围内的图像。
+(id)spriteWithSpriteFrame:(CCSpriteFrame*)spriteFrame
根据一个CCSPriteFrame对象创建并返回一个精灵对象,CCSPriteFrame是帧,它是组成动画的一部分,CCSPriteFrame的成员变量中有一个Texture,用帧创建精灵,其实就是用帧的Texture创建精灵,当精灵切换帧时,Texture也会随之更换,精灵就会呈现不同的形态,以次达到动画的效果。这里大家可以理解为,Texture和Frame都可以构成一个精灵对象。
+(id)spriteWithSpriteFrameName:(NSString*)spriteFrameName
根据帧的名字创建并返回一个精灵对象。这里要引入一个概念,就是cocos2d中的帧是由一个单例对象统一管理的,这个单例的作用就是充当一个内存池,它将需要使用的CCSpriteFrame对象保存到自己内部的一个字典中,当有精灵需要用到它时,就从池中取出来传递过去,如果有多个精灵需要一个Frame的话,可以共用一个,而不必拷贝N份,这样可以节约内存和创建对象的时间。这里的参数spriteFrameName其实就是字典的Key,内存池先通过它找到Frame对象,再用它去创建精灵。
+(id)spriteWithCGImage:(CGImageRef)image key:(NSString*)key
根据一个CGImageRef对象创建并返回精灵对象,这个过程比较纠结,大家看仔细,千万别造成误解……
首先CCTexture2D和CCSpriteFrame一样,都有内存池机制,参数key就是键值,该方法先在内存池中查找_和key匹配的Texture,如果有则直接用它创建精灵,如果没有就根据参数中的image,先生成一个UIImage对象,再用这个对象生成CCTexture2D的对象,用它来创建精灵并将其存入内存池(键值为key)。
+(id) spriteWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect
这里又引入了一个新的类–CCSpriteBatchNode,它是一个批量处理精灵绘制的类,具体什么意思呢?
精灵是用openGL的方法绘制图形的,这点大家都清楚了,当场景中有100个精灵的时候,程序的执行是这样的:for(int i= 0;i<100;i++){open-draw-close;},就是说执行了100次的open-draw-close;而用CCSpriteBatchNode绘制的话则是这样执行的:open-for(int i= 0;i<100;i++){draw;}- close,即省略了99次的open和close,从而优化了精灵的绘制效率。这里就是用CCSpriteBatchNode中的Texture创建精灵,并把精灵添加到batchNode中。
-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
最标准的精灵初始化方法,也是spriteWithTexture:rect:调用的方法,作用就是根据一个CCTexture对象对精灵进行初始化(只载入rect范围内的图形)。
-(id) initWithTexture:(CCTexture2D*)texture
缺省了rect参数的初始化,默认为将Texture的全部载入。
-(id) initWithFile:(NSString*)filename
spriteWithFile调用的方法,根据资源的文件名初始化精灵,就是先用文件名装载Texture,再用Texture初始化。
-(id) initWithFile:(NSString*)filename rect:(CGRect)rect
可自行设定图片范围的initWithFile方法。
– (id) initWithSpriteFrame:(CCSpriteFrame*)spriteFrame
-(id)initWithSpriteFrameName:(NSString*)spriteFrameName
– (id) initWithCGImage:(CGImageRef)image key:(NSString*)key
-(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect
这些都是上面介绍的静态方法调用的初始化,原理相同,就不一一介绍了。
-(CCSpriteBatchNode*) batchNode
获取精灵的批处理节点。
-(void) setBatchNode:(CCSpriteBatchNode *)batchNode
设置精灵的批处理节点。
-(void) setTextureRect:(CGRect)rect
设置Texture的区域,和初始化中rect的作用是一样的,这里是动态修改。
-(void) draw
绘制
-(void) addChild:(CCSprite*)child z:(NSInteger)z tag:(NSInteger) aTag
和CCNode的作用一样,但加了一些限制:当精灵被批处理节点管理时,只能添加精灵作为自己的子节点;精灵和它的子节点的Texture都必须和批处理节点相同。
-(void)removeChild: (CCSprite *)sprite cleanup:(BOOL)doCleanup
和CCNode的作用一样,另外加了一个功能,如果精灵被一个批处理节点管理,则将其从批处理节点中删除。
-(void)setPosition:(CGPoint)pos
-(void)setRotation:(float)rot
-(void)setSkewX:(float)sx
-(void)setSkewY:(float)sy
-(void)setScaleX:(float) sx
-(void)setScaleY:(float) sy
-(void)setScale:(float) s
-(void) setVertexZ:(float)z
-(void)setAnchorPoint:(CGPoint)anchor
-(void)setIsRelativeAnchorPoint:(BOOL)relative
-(void)setVisible:(BOOL)v
-(void)setFlipX:(BOOL)b
-(void) setFlipY:(BOOL)b
这些都和CCNode中的功能一样(其实到了这里这些方法才算真正有了用武之地),就不赘述了。
-(void) setOpacity:(GLubyte) anOpacity
这是精灵新扩展的方法,作用是设置透明度,0为全透明,255为不透明。
-(void) setColor:(ccColor3B)color3
设置精灵颜色,就是改变图片的色相。
-(void) setDisplayFrame:(CCSpriteFrame*)frame
切换精灵当前显示的图象,之前说过精灵是可以播放动画的,播放动画的实质就是切换Frame,都是靠该接口实现的。
-(void) setDisplayFrameWithAnimationName: (NSString*) animationName index:(int) frameIndex
将动画序列中的某一帧设置为精灵当前的显示帧。CCAnimation是封装动画功能的类,它可以看作是由若干个_CCSpriteFrame对象组成的序列,精灵按照顺序切换它们,就形成了动画。CCAnimation也有内存池,此处的animationName就是key,内存池通过它找到CCAnimation对象,再通过索引frameIndex找到动画序列中的某一帧,将该帧设为精灵的当前显示帧。
-(BOOL) isFrameDisplayed:(CCSpriteFrame*)frame
判断精灵当前显示的帧是否为参数frame,判断的依据是它们的成员texture的名字和rect是否相同。
-(CCSpriteFrame*) displayedFrame
获取精灵当前显示的帧。
-(void) setTexture:(CCTexture2D*)texture
设置精灵当前显示的贴图,作用其实和setDisplayFrame,都是改变精灵的形态,只不过这个方法是直接改Texture(setDisplayFrame内部也调用了该方法)。
-(CCTexture2D*) texture
获取精灵当前显示的贴图。
CCSpriteBatchNode
这个类在前面也介绍了一些,它的作用是优化精灵,提高精灵的绘制效率,精灵数量越多,效果越明显。它的工作原理是:将所有该对象的子节点(只能是精灵)用openGL的渲染方法一次性绘制,这样可以省去多次open-close的时间,_作为CCSpriteBatchNode对象子节点的精灵不能用自己的Texture绘制,而是用CCSpriteBatchNode对象的Texture统一绘制,精灵只是提供坐标、旋转角度等信息,这就是它只需要open-close一次的原因,但也正因为如此,导致批处理节点的所有子节点都必须和它用同一套Texture,即CCSpriteBatchNode对象绘制出的图形都是一样的,这点需要格外注意。CCSpriteBatchNode的用法和普通精灵没什么两样,都可以设置Texture,也都是用Texture来绘制,只要通过addChild方法成为其子节点的精灵,都会得到它的优化,但是CCSpriteBatchNode只能添加精灵为子节点。
+(id)batchNodeWithTexture:(CCTexture2D *)tex
和精灵的方法类似,根据一个CCTexture2D对象构建一个精灵批处理节点,封装了alloc、init和autorelease方法。
+(id)batchNodeWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity
根据文件名创建精灵批处理节点,fileImage是文件名,先通过它创建Texture,再将其添加进对象。
+(id)batchNodeWithFile:(NSString*) imageFile
根据文件名创建精灵批处理节点,缺省了capacity参数。
-(id)initWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity
根据Texture初始化。
-(id)initWithFile:(NSString *)fileImage capacity:(NSUInteger)capacity
根据文件名初始化。
-(void) addChild:(CCSprite*)child z:(NSInteger)z tag:(NSInteger) aTag
添加子节点,子节点必须是精灵,且精灵的Texture和批处理节点相同。
-(void) setTexture:(CCTexture2D*)texture
设置Texture。
-(CCTexture2D*) texture
获取Texture。
CCTexture2D
该类封装的就是一直提到的Texture(贴图),在3D渲染中,它是必不可少的,因此虽然大部分情况下由于封装的原因,被精灵盖在底层,但重要性还是相当的高。不过反过来说,该类的方法几乎都是被精灵或帧等类自动调用的,不太需要我们过问,因此咱们也没必要花太多时间去仔细研究它是怎么工作的,只要知道工作原理就可以了,除非你在学习openGL ES。我们可以把CCTextrue2D的对象看成图片资源在内存中的存在形式,它是openGL渲染图形的重要参数,通常情况下它是由UIImageRef到UIImage,再到CCTexture2D转化而来的。
– (id) initWithData:(const void*)data pixelFormat:(CCTexture2DPixelFormat)pixelFormat pixelsWide:(NSUInteger)width pixelsHigh:(NSUInteger)height contentSize:(CGSize)size
初始化CCTexture2D对象,方法中的那一长串参数都是引擎帮我们运算出来的,它通常被下面的方法调用,因此先别急着去钻研这些参数,即使你看到头大它也不过是生成一张贴图而已……
– (id) initWithImage:(UIImage *)uiImage resolutionType:(ccResolutionType)resolution
总的来说它就是将一个UIImage转化为一个CCTexture2D,这个方法被N多种方法封装过(比如我们前面说的精灵的initWithFile方法),基本上我们也不会主动去用它。
– (id) initWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size
根据一个字符串生成一个Texture,这个方法不需要图片资源。
CCTextureCache
该类的本质是一个内存池,用来缓存游戏中用到的CCTexture2D对象,是个单例。当精灵从池中获取对象时,如果存在,则将对象返回,如果不存在,则会生成一个,将其存入池中并返回,这样同样的Texture在池中最多只会有一份,达到节约空间的目的。
+ (CCTextureCache *)sharedTextureCache
获取单例,单例类的惯用模式。
+(void)purgeSharedTextureCache
释放内存池,当游戏结束时由导演调用。
CCSpriteFrame
这个类的最大作用就是作为Texture的载体,将图形数据传递给精灵,同时它还有offset_、rotated_等一系列参数,用来存储Plist的数据。(plist是一种数据存储格式,有专门的API可以将其读取并保存到内存中,但这里说的是一种动画编辑器生成的、基于plist格式保存的数据,就是说cocos2d支持这种动画编辑器,可以解析它的数据)使用Frame机制构建精灵,还可以支持精灵的动画效果,因为精灵除了做位移或旋转等操作,还可能实时地改变自身显示的图形。cocos2d用CCAnimation保存一个Frame序列,然后用CCAnimate这个行为类根据序列定时切换精灵的当前帧。
+(id) frameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
根据Texture构建一个CCSpriteFrame对象。
+(id) frameWithTextureFilename:(NSString*)filename rect:(CGRect)rect
根据字符串构建一个CCSpriteFrame对象,这个字符串其实是Texture内存池的key,通过它获取Texture。
+(id) frameWithTexture:(CCTexture2D*)texture rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize
可自行设定显示区域、偏移等参数的构建方法,参数一般是从plist中读取。
+(id) frameWithTextureFilename:(NSString*)filename rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize
和上一个类似,只是改用key构建。
CCSpriteFrameCache
管理帧的内存池,作用和CCTextureCache一样,就不赘述了。这里说几个比较特殊的接口。
-(void) addSpriteFramesWithDictionary:(NSDictionary*)dictionary textureFilename:(NSString*)textureFilename
根据一个字典和Texture的文件名构建CCSpriteFrame对象并添加到内存池中。一般来说这个字典都是从plist中读取出来的,通过这种方法来支持动画编辑器,当然我们也可以根据这个格式自己构建字典。
-(void) addSpriteFramesWithFile:(NSString*)plist textureFilename:(NSString*)textureFilename
根据一个plist文件名和Texture的文件名构建CCSpriteFrame对象并添加到内存池中。先通过文件名找到plist文件的路径,然后用字典的API把它加载到内存中,最后调用addSpriteFramesWithDictionary:textureFilename:方法。
-(void) addSpriteFramesWithFile:(NSString*)plist
缺省了Texture文件名的构建方法,缺省的文件名先在_plist中查找,如果没有则查找和plist同名的文件。
-(void) addSpriteFrame:(CCSpriteFrame*)frame name:(NSString*)frameName
将CCSpriteFrame对象添加到内存池中。
-(void) removeSpriteFrameByName:(NSString*)name
根据key从内存池中删除Frame。
– (void) removeSpriteFramesFromFile:(NSString*) plist
从内存池删除plist中列出的Frame,相当于addSpriteFramesWithFile的逆向操作。
– (void) removeSpriteFramesFromDictionary:(NSDictionary*) dictionary
从内存池删除字典中列出的Frame,相当于addSpriteFramesWithDictionary的逆向操作。
-(CCSpriteFrame*) spriteFrameByName:(NSString*)name
根据key在内存池中查找Frame并返回。
– (void) removeSpriteFramesFromTexture:(CCTexture2D*) texture
从内存池中删除和所给texture相同的Frame。
CCAnimation
其实这个类就是封装了一个Frame序列,作为精灵播放动画的参数,没有别的功能。
+(id) animation
构建一个空的动画(没有任何帧),就是封装了alloc、init和autorelease。
+(id) animationWithFrames:(NSArray*)frames
根据一个Frame数组构建CCAnimation对象,在CCAnimation中Frame序列就是用数组保存的,所以这里很好理解。
+(id) animationWithFrames:(NSArray*)frames delay:(float)delay
除了构建对象外,还设置了换帧的时间间隔。
-(void) addFrame:(CCSpriteFrame*)frame
向Frame序列中添加一个CCSpriteFrame对象。
-(void) addFrameWithFilename:(NSString*)filename
根据资源的文件名创建一个Frame并添加到序列中。
-(void) addFrameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
根据一个Texture创建一个Frame并添加到序列中。
CCAnimationCache
单例,CCAnimation的内存池,不多说了。
+ (CCAnimationCache *)sharedAnimationCache
获取单例。
-(void) addAnimation:(CCAnimation*)animation name:(NSString*)name
向池中添加一个CCAnimation对象。
-(void) removeAnimationByName:(NSString*)name
根据key从池中删除CCAnimation对象。
-(CCAnimation*) animationByName:(NSString*)name
根据key从池中获取CCAnimation对象。
-(void)addAnimationsWithDictionary:(NSDictionary *)dictionary
根据一个字典创建CCAnimation对象,并存入内存池。字典中保存的是每个动画的名字,动画中包含哪些帧,换帧间隔是多长。
-(void)addAnimationsWithFile:(NSString *)plist
根据一个plist文件创建CCAnimation对象,并存入内存池。先用文件中的信息生成一个字典,再调用addAnimationsWithDictionary方法来实现。
CCAnimate
这是一个持续型行为类,父类是CCActionInterval,它的作用就是根据CCAnimation中的序列及间隔时间,不断地切换精灵的帧,使其产生动画效果。
+(id) actionWithAnimation: (CCAnimation*)anim
根据CCSpriteFrame对象生成一个动画播放行为,持续的时间由帧数和间隔时间相乘算出。
+(id) actionWithAnimation: (CCAnimation*)anim restoreOriginalFrame:(BOOL)b
b为yes时,当动画播放完毕后会切换回播放前显示的帧。
+(id) actionWithDuration:(ccTime)duration animation: (CCAnimation*)anim restoreOriginalFrame:(BOOL)b
手动设置动画的播放时间,时间到动画才算结束。
OK,到这里我们就可以使用精灵来丰富我们的游戏世界了,这次我们再做一个练习,和之前的静态图片不同,这回我们做一个动态播放动画的工程。
(下载地址http://115.com/file/dpv26lft)
头文件内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#import "CCScene.h" #import "cocos2d.h" @interface MyGame : CCScene { CCSprite* m_background; CCSprite* m_kyo; } -(void) loadBG; -(void) loadActor; -(void) makeAnimation; @end |
实现
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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
#import "MyGame.h" @implementation MyGame -(id) init { if (self = [super init]) { //加载背景资源 [self loadBG]; //加载人物资源 [self loadActor]; CCLayer* layer = [CCLayer node]; //背景精灵以bg_0为初始帧 m_background = [CCSprite spriteWithSpriteFrameName:@"bg_0"]; [m_background setPosition:CGPointMake(240, 160)]; //人物精灵以stand为初始帧 m_kyo = [CCSprite spriteWithSpriteFrameName:@"stand"]; [m_kyo setPosition:CGPointMake(240, 120)]; [layer addChild:m_background z:0 tag:1]; [layer addChild:m_kyo z:1 tag:2]; [self addChild:layer]; //生成动画行为 [self makeAnimation]; } return self; } -(void) loadBG { //加载背景资源到内存中,共4帧 for (int i = 0; i < 4; i++) { //生成文件名bg_0.png ~ bg_3.png NSString* fileName = [NSString stringWithFormat:@"bg_%d.png", i]; CCSpriteFrame* frame = [CCSpriteFrame frameWithTextureFilename:fileName rect:CGRectMake(0, 0, 450, 330)]; //将frame加入内存池 [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFrame:frame name:[fileName stringByDeletingPathExtension]]; } } -(void) loadActor { //加载人物的站立资源到内存中,只有1帧 CCSpriteFrame* frame = [CCSpriteFrame frameWithTextureFilename:@"stand.png" rect:CGRectMake(0, 0, 292, 221)]; [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFrame:frame name:@"stand"]; //加载人物的出拳资源到内存中,共2帧 for (int i = 0; i < 2; i++) { //虽然是2帧,但这两个资源是存在一张图片中的,因此读取的文件名相同,通过不同的rect剪裁、区分 CCSpriteFrame* frame = [CCSpriteFrame frameWithTextureFilename:@"punch.png" rect:CGRectMake(i * 292, 0, 292, 221)]; [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFrame:frame name:[NSString stringWithFormat:@"punch_%d", i]]; } //加载人物的出拳资源到内存中,共4帧 for (int i = 0; i < 5; i++) { //4个资源在一张图片中 CCSpriteFrame* frame = [CCSpriteFrame frameWithTextureFilename:@"kick.png" rect:CGRectMake(i * 292, 0, 292, 221)]; [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFrame:frame name:[NSString stringWithFormat:@"kick_%d", i]]; } } -(void) makeAnimation { NSMutableArray* array = [NSMutableArray array]; //生成背景图动画,共4帧 for (int i = 0; i < 4; i++) { NSString* key = [NSString stringWithFormat:@"bg_%d", i]; //从内存池中取出Frame CCSpriteFrame* frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:key]; //添加到序列中 [array addObject:frame]; } //将数组转化为动画序列,换帧间隔0.1秒 CCAnimation* animBG = [CCAnimation animationWithFrames:array delay:0.1f]; //生成动画播放的行为对象 id actBG = [CCAnimate actionWithAnimation:animBG]; //清空缓存数组 [array removeAllObjects]; //生成出拳动画,共3帧 for (int i = 0; i < 2; i++) { NSString* key = [NSString stringWithFormat:@"punch_%d", i]; //从内存池中取出Frame CCSpriteFrame* frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:key]; [array addObject:frame]; } //添加完punch_0和puch_1后,再重复一次punch_0,因为准备动作和收招的图片是相同的,这里用一帧播放两遍的形式节省内存 [array addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"punch_0"]]; //将数组转化为动画序列,换帧间隔0.06秒 CCAnimation* animPunch = [CCAnimation animationWithFrames:array delay:0.06f]; //生成出拳动画的行为对象 id actPunch = [CCAnimate actionWithAnimation:animPunch restoreOriginalFrame:YES]; [array removeAllObjects]; //生成出拳动画,共6帧 for (int i = 0; i < 4; i++) { NSString* key = [NSString stringWithFormat:@"kick_%d", i]; //从内存池中取出Frame CCSpriteFrame* frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:key]; [array addObject:frame]; } //重复一次kick_1,原因同上 [array addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"kick_1"]]; //添加帧kick_4 [array addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"kick_4"]]; //这并不是kick动画的成员,而是作为动画结束后的还原帧而加入队列的,作用是使踢腿动作结束后恢复站立 [array addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"stand"]]; //将数组转化为动画序列,换帧间隔0.05秒 CCAnimation* animKick = [CCAnimation animationWithFrames:array delay:0.05f]; //生成踢腿动画的行为对象 id actKick = [CCAnimate actionWithAnimation:animKick]; [array removeAllObjects]; //精灵执行runAction方法激活行为对象 [m_background runAction:[CCRepeatForever actionWithAction:actBG]]; id actDelay = [CCDelayTime actionWithDuration:1]; //因为两次delay是完全无关的两个行为,因此这里用了copy,避免行为进度发生错乱 id attack = [CCSequence actions:actDelay, actPunch, [actDelay copy], actKick, nil]; [m_kyo runAction:[CCRepeatForever actionWithAction:attack]]; } @end |
效果图,主角草薙京会作出出拳、踢腿 动作,并反复循环,同时背景也是动态的,这些都是靠动画行为实现的。
- 本文固定链接: https://www.xuanyusong.com/archives/1370
- 转载请注明: 失落的宇宙 于 雨松MOMO程序研究院 发表
是不是 版本问题 还是我理解不对 我怎么觉得你写的 和官网的不一样
写的Very好 学习了~!
CCSpriteBatchNode的子节点有个条件,就是所有子节点必须必须和父节点用同一个texture(rect可以不一样),即源自同一张图片,你把两个图的精灵放到一个batchnod下肯定是不行的,这种情况只能用CCSprite,或者把资源整合到一张图中
两张图, 里面包含很多动画帧, 每个动画10帧, 图1里面比如动画1的1-8帧, 图2里面动画1的9-10帧, 这个动画该怎么顺序播放呀..CCSpriteBatchNode addChild的时候会出错..
凯哥的更新。。。。。
很好的文章,不过可不可以多写点点Cocos2d的文章呢?呵呵…….很期待
等待凯哥的更新
很好的文章,不过可不可以多写点点Cocos2d的文章呢?呵呵…….很期待
等凯哥的更新。。。。
宇宙爆发啦!^_^
嗯,第七感觉醒了
很不错
mengnan