转载自雨松MOMO程序研究院本文链接地址:Cocos2D研究院之精灵与动画(六)
通过对导演、场景、层和节点的剖析,现在我们已经可以写出一个完整的游戏体系了,在实际应用中,场景一般都是作为游戏的关卡,层作为场景的组成元素(比如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)
头文件内容
01
|
#import "CCScene.h"
|
02
|
#import "cocos2d.h"
|
03
|
04
|
@interface MyGame : CCScene
|
05
|
{
|
06
|
CCSprite* m_background;
|
07
|
CCSprite* m_kyo;
|
08
|
}
|
09
|
10
|
-( void ) loadBG;
|
11
|
-( void ) loadActor;
|
12
|
-( void ) makeAnimation;
|
13
|
14
|
@end
|
实现
001
|
#import "MyGame.h"
|
002
|
003
|
@implementation MyGame
|
004
|
005
|
-(id) init
|
006
|
{
|
007
|
if (self = [super init])
|
008
|
{
|
009
|
//加载背景资源
|
010
|
[self loadBG];
|
011
|
//加载人物资源
|
012
|
[self loadActor];
|
013
|
CCLayer* layer = [CCLayer node];
|
014
|
//背景精灵以bg_0为初始帧
|
015
|
m_background = [CCSprite spriteWithSpriteFrameName:@ "bg_0" ];
|
016
|
[m_background setPosition:CGPointMake(240, 160)];
|
017
|
//人物精灵以stand为初始帧
|
018
|
m_kyo = [CCSprite spriteWithSpriteFrameName:@ "stand" ];
|
019
|
[m_kyo setPosition:CGPointMake(240, 120)];
|
020
|
[layer addChild:m_background z:0 tag:1];
|
021
|
[layer addChild:m_kyo z:1 tag:2];
|
022
|
[self addChild:layer];
|
023
|
//生成动画行为
|
024
|
[self makeAnimation];
|
025
|
}
|
026
|
return self;
|
027
|
}
|
028
|
029
|
-( void ) loadBG
|
030
|
{
|
031
|
//加载背景资源到内存中,共4帧
|
032
|
for ( int i = 0; i < 4; i++)
|
033
|
{
|
034
|
//生成文件名bg_0.png ~ bg_3.png
|
035
|
NSString* fileName = [NSString stringWithFormat:@ "bg_%d.png" , i];
|
036
|
CCSpriteFrame* frame = [CCSpriteFrame frameWithTextureFilename:fileName rect:CGRectMake(0, 0, 450, 330)];
|
037
|
//将frame加入内存池
|
038
|
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFrame:frame name:[fileName stringByDeletingPathExtension]];
|
039
|
}
|
040
|
}
|
041
|
042
|
-( void ) loadActor
|
043
|
{
|
044
|
//加载人物的站立资源到内存中,只有1帧
|
045
|
CCSpriteFrame* frame = [CCSpriteFrame frameWithTextureFilename:@ "stand.png" rect:CGRectMake(0, 0, 292, 221)];
|
046
|
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFrame:frame name:@ "stand" ];
|
047
|
048
|
//加载人物的出拳资源到内存中,共2帧
|
049
|
for ( int i = 0; i < 2; i++)
|
050
|
{
|
051
|
//虽然是2帧,但这两个资源是存在一张图片中的,因此读取的文件名相同,通过不同的rect剪裁、区分
|
052
|
CCSpriteFrame* frame = [CCSpriteFrame frameWithTextureFilename:@ "punch.png" rect:CGRectMake(i * 292, 0, 292, 221)];
|
053
|
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFrame:frame name:[NSString stringWithFormat:@ "punch_%d" , i]];
|
054
|
}
|
055
|
056
|
//加载人物的出拳资源到内存中,共4帧
|
057
|
for ( int i = 0; i < 5; i++)
|
058
|
{
|
059
|
//4个资源在一张图片中
|
060
|
CCSpriteFrame* frame = [CCSpriteFrame frameWithTextureFilename:@ "kick.png" rect:CGRectMake(i * 292, 0, 292, 221)];
|
061
|
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFrame:frame name:[NSString stringWithFormat:@ "kick_%d" , i]];
|
062
|
}
|
063
|
}
|
064
|
065
|
-( void ) makeAnimation
|
066
|
{
|
067
|
NSMutableArray* array = [NSMutableArray array];
|
068
|
069
|
//生成背景图动画,共4帧
|
070
|
for ( int i = 0; i < 4; i++)
|
071
|
{
|
072
|
NSString* key = [NSString stringWithFormat:@ "bg_%d" , i];
|
073
|
//从内存池中取出Frame
|
074
|
CCSpriteFrame* frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:key];
|
075
|
//添加到序列中
|
076
|
[array addObject:frame];
|
077
|
}
|
078
|
//将数组转化为动画序列,换帧间隔0.1秒
|
079
|
CCAnimation* animBG = [CCAnimation animationWithFrames:array delay:0.1f];
|
080
|
//生成动画播放的行为对象
|
081
|
id actBG = [CCAnimate actionWithAnimation:animBG];
|
082
|
//清空缓存数组
|
083
|
[array removeAllObjects];
|
084
|
085
|
//生成出拳动画,共3帧
|
086
|
for ( int i = 0; i < 2; i++)
|
087
|
{
|
088
|
NSString* key = [NSString stringWithFormat:@ "punch_%d" , i];
|
089
|
//从内存池中取出Frame
|
090
|
CCSpriteFrame* frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:key];
|
091
|
[array addObject:frame];
|
092
|
}
|
093
|
//添加完punch_0和puch_1后,再重复一次punch_0,因为准备动作和收招的图片是相同的,这里用一帧播放两遍的形式节省内存
|
094
|
[array addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@ "punch_0" ]];
|
095
|
//将数组转化为动画序列,换帧间隔0.06秒
|
096
|
CCAnimation* animPunch = [CCAnimation animationWithFrames:array delay:0.06f];
|
097
|
//生成出拳动画的行为对象
|
098
|
id actPunch = [CCAnimate actionWithAnimation:animPunch restoreOriginalFrame:YES];
|
099
|
[array removeAllObjects];
|
100
|
101
|
//生成出拳动画,共6帧
|
102
|
for ( int i = 0; i < 4; i++)
|
103
|
{
|
104
|
NSString* key = [NSString stringWithFormat:@ "kick_%d" , i];
|
105
|
//从内存池中取出Frame
|
106
|
CCSpriteFrame* frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:key];
|
107
|
[array addObject:frame];
|
108
|
}
|
109
|
//重复一次kick_1,原因同上
|
110
|
[array addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@ "kick_1" ]];
|
111
|
//添加帧kick_4
|
112
|
[array addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@ "kick_4" ]];
|
113
|
//这并不是kick动画的成员,而是作为动画结束后的还原帧而加入队列的,作用是使踢腿动作结束后恢复站立
|
114
|
[array addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@ "stand" ]];
|
115
|
//将数组转化为动画序列,换帧间隔0.05秒
|
116
|
CCAnimation* animKick = [CCAnimation animationWithFrames:array delay:0.05f];
|
117
|
//生成踢腿动画的行为对象
|
118
|
id actKick = [CCAnimate actionWithAnimation:animKick];
|
119
|
[array removeAllObjects];
|
120
|
121
|
//精灵执行runAction方法激活行为对象
|
122
|
[m_background runAction:[CCRepeatForever actionWithAction:actBG]];
|
123
|
id actDelay = [CCDelayTime actionWithDuration:1];
|
124
|
//因为两次delay是完全无关的两个行为,因此这里用了copy,避免行为进度发生错乱
|
125
|
id attack = [CCSequence actions:actDelay, actPunch, [actDelay copy], actKick, nil];
|
126
|
[m_kyo runAction:[CCRepeatForever actionWithAction:attack]];
|
127
|
}
|
128
|
129
|
@end
|
效果图,主角草薙京会作出出拳、踢腿 动作,并反复循环,同时背景也是动态的,这些都是靠动画行为实现的。