CCSprite是Cocos2D游戏开发中最常用的类,用图片把精灵(sprite)显示在屏幕上。在游戏开发中,经常会遇到精灵(sprite)这个术语。精灵是一个图像,可以在屏幕上独立地移动。一个精灵可能是玩家角色、敌人,或者是大的背景图像。一般情况下,精灵来自于开发者所准备的PNG或PVRTC图像。一旦图像载入内存,就会将精灵转换成纹理图,从而被iPhone GPU用于在屏幕上渲染。3.6.1 CCSprite类的属性及方法生成精灵的最简单方法是把图片文件加载进CCTexture2D纹理,然后将它赋给精灵。精灵可以接受其他精灵作为子节点。CCSprite默认锚点位置是(0.5,0.5),也就是节点的几何中心。如果一个精灵的父类是标准CCNode节点,那么它和其他CCNode的属性和方法没有太大区别。如果一个精灵的父类或任何一个祖先类是CCSpriteBatchNode,那么它具备的特性包括:更快地纹理渲染、不支持Camera、不支持GridBase、不能单独设置Alias/Antialias属性、不能单独设置混合模式、不支持视差滚动。1 . CCSprite类的属性除了从CCNode类继承的属性外,CCSprite节点还有如下属性:atlasIndex:纹理集的索引,通常不建议修改该值。变量类型是NSUInteger。batchNode:对渲染CCSprite的CCSpriteBatchNode的弱引用(关于CCSpriteBatchNode将在3.6.2节介绍),变量类型是CCSpriteBatchNode。blendFunc:遵循CCTextureProtocol协议。变量类型是ccBlendFunc(纹理的混合方法)。color:RGB色彩,遵循CCRGBAProtocol协议。变量类型是ccColor3B(使用3字节组成的色彩信息,分别代表R、G、B)。dirty:判断精灵是否需要在纹理集中更新。变量类型是BOOL。flipX:判断精灵是否会沿水平方向翻转。变量类型是BOOL。该属性只会对精灵的纹理进行翻转,不会影响到精灵的子节点,同时也不会修改锚点位置。如果想要同时修改锚点并对子节点进行翻转,我们可以使用下面的方法:
sprite.scaleX *= -1;flipY:判断精灵是否会沿垂直方向翻转。变量类型是BOOL。该属性只会对精灵的纹理进行翻转,不会影响到精灵的子节点,同时也不会修改锚点位置。如果想要同时修改锚点并对子节点进行翻转,我们可以使用下面的方法:sprite.scaleY *= -1;offsetPosition:以点值计量的精灵位置的偏移,如果使用Zwoptex之类的编辑器,会自动计算该数值。变量类型是CGPoint。opacity:透明度,遵循CCRGBAProtocal协议。变量类型是Glubyte。quad:方形的信息(纹理坐标、顶点坐标、色彩等)。变量类型是ccV3F_C4B_T2F_Quad。textureAtlas:到CCTextureAtlas的弱引用,该属性仅在该精灵使用CCSpriteBatchNode渲染时可用。变量类型是CCTextureAtlas。textureRect:以点值返回精灵对象的矩形框。变量类型是CGRect。textureRectRotated:纹理矩形是否被旋转。变量类型是BOOL。2 . CCSprite类的方法除了从CCNode继承的方法,CCSprite还支持以下常用方法:(1)-(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect该方法使用CCSpriteBatchNode和矩形框(以点数表示)初始化一个精灵对象,具体实现代码可参考模板中的CCSprite.m文件。正如之前所提到的,对开发者来说,更多的是使用类方法来直接创建所需要的对象。该方法的一个变化是使用CCSpriteBatchNode和矩形(以像素值表示)初始化一个精灵对象,示例代码如下:
-(id)initWithBatchNode:(CCSpriteBatchNode *) batchNode rectInPixels:(CGRect) rect;(2)-(id)initWithCGImage:(CGImageRef)imagekey:(NSString *)key该方法使用CGImageRef和键值初始化一个精灵对象,CCTextureCache将使用该键值判断某个纹理是否已使用CGImage创建。例如某个有效键值可能是“sprite_frame_01”,如果键值是nil,则每次使用CCTextureCache创建一个新的纹理。(3)-(id)initWithFile:(NSString *)filenamerect:(CGRect)rect该方法使用图片名称和矩形初始化一个精灵对象,位置偏移是(0,0)。该方法有一个变化,就是不指定rect,使用图片的大小作为矩形,示例代码如下:
-(id)initWithFile:(NSString *)filename;(4)- (id) initWithSpriteFrame: (CCSpriteFrame *) spriteFrame该方法使用精灵帧初始化一个精灵。(5)- (id) initWithSpriteFrameName: (NSString *) spriteFrameName该方法使用精灵帧的名称初始化一个精灵。使用该方法时,将从CCSpriteFrameCache中获取一个CCSpriteFrame;如果不存在,系统将抛出一个异常。(6)- (id) initWithTexture: (CCTexture2D *)texturerect: (CGRect) rect该方法使用纹理和矩形初始化一个精灵,位置偏移是(0,0)。该方法还有一个变化,就是使用纹理的大小作为矩形。示例代码如下:
-(id) initWithTexture: (CCTexture2D *)texture;(7)- (CCSpriteFrame*) displayedFrame该方法返回当前所显示的精灵帧。示例代码如下:
[sprite setDisplayFrame:[self displayedFrame]];(8)-(BOOL) isFrameDisplayed:(CCSpriteFrame*)frame该方法判断某个CCSpriteFrame对象是否正在被显示。示例代码如下:
BOOL isBearDisplaed = [bear isFrameDisplayed:frame];(9)-(void) setDisplayFrame:(CCSpriteFrame*)frame该方法为CCSprite精灵设置新的显示帧。示例代码如下:
[self setDisplayFrame:spriteFrame];(10)- (void) setDisplayFrameWithAnimationName: (NSString *) animationNameindex: (int) frameIndex该方法根据动画名称和索引更改要显示的帧,动画名将从CCAnimationCache中获取。示例代码如下:
[bear setDisplayFrameWithAnimationName:animation index:indexNumber];(11)- (void) setTextureRect: (CGRect) rect该方法更新CCSprite精灵对象的纹理矩形大小。示例代码如下:
[self setTextureRect:rect];注意 使用了setTextureRect再使用contentSize时要注意,不能再当成初始化时的contentSize大小来使用。这个方法在实际开发中的作用何在呢?想象用Cocos2D开发一款RPG小游戏,游戏中有很多角色,而每个角色都有显示自己生命值的血条,我们只需在预定消息的方法中使用setTextureRect,就可以实时更改角色的血条长度。(12)- (void) setTextureRect: (CGRect) rect
rotated: (BOOL) rotated untrimmedSize: (CGSize) size该方法更新CCSprite精灵对象的纹理矩形,矩形旋转和未裁剪大小。示例代码如下:
[self setTextureRect:rect rotated:NO untrimmedSize:rect.size];(13)+(id)spriteWithBatchNode:(CCSpriteBatchNode *) batchNode
rect:(CGRect) rect该方法使用CCSpriteBatchNode和矩形(以点数表示)来创建一个精灵对象。首先看该方法的实现代码:
+(id) spriteWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect { return [[[self alloc] initWithBatchNode:batchNode rect:rect] autorelease]; }关于实例方法和类方法在实际使用中的区别前面已经提到,大家只需要知道大多数情况下直接使用类方法来创建对象就可以了。示例代码如下:
CCSpriteBatchNode *batch = [CCSpriteBatchNode batchNodeWithFile:@"blocks.png" capacity:150]; CCSprite *sprite = [CCSprite spriteWithBatchNode:batch rect:CGRectMake(100,100,32,32)];(14)+(id)spriteWithCGImage:(CGImageRef)image key:(NSString *)key该方法使用CGImageRef和键值创建一个精灵对象。CCTextureCache将使用该键值判断某个纹理是否已使用CGImage创建。例如,某个有效键值可能是“sprite_frame_01”,如果键值是nil,则会每次使用CCTextureCache创建一个新纹理,示例代码如下:
UIImage* img = [UIImage imageNamed:@"zebra.png"]; CCSprite* spr = [CCSprite spriteWithCGImage:img.CGImage key:@"img"];(15)+(id)spriteWithFile:(NSString *)filename rect:(CGRect)rect该方法使用图片名称和矩形创建一个精灵对象,位置偏移是(0,0)。该方法的使用频率相当高,大多数时候使用它创建精灵对象。示例代码如下:
CCSprite *bear = [CCSprite spriteWithFile:@"bear.png" rect:CGRectMake(100, 100, 32, 32)];该方法有一个变化,就是不指定rect,使用图片的大小作为矩形。大多数情况下无需特别指定rect的大小。示例代码如下:
CCSprite *bear = [CCSprite spriteWithFile:@"bear.png"];(16)+(id) spriteWithSpriteFrame: (CCSpriteFrame *) spriteFrame该方法使用精灵帧创建一个精灵。示例代码如下:
CCSprite *testPlayer = [CCSprite spriteWithSpriteFrame:startingFrame];(17)+ (id) spriteWithSpriteFrameName: (NSString *) spriteFrameName该方法使用精灵帧的名称创建一个精灵。该方法在使用时,将从CCSpriteFrameCache中获取该CCSpriteFrame,如果精灵帧缓存中不存在该精灵帧,系统将抛出一个异常。示例代码如下:
CCSprite *player = [CCSprite spriteWithSpriteFrameName:@"player.png"];(18)+ (id) spriteWithTexture: (CCTexture2D *)texture rect: (CGRect) rect该方法使用纹理和矩形创建一个精灵,位置偏移是(0,0)。示例代码如下:
CCTexture2D *playerTexture = [[CCTextureCache sharedTextureCache] addImage:@"player.png"]; CCSprite *player2 = [CCSprite spriteWithTexture:playerTexture rect:CGRectMake(100, 100, 200, 200)];该方法还有一个变化,就是使用纹理自身的大小作为矩形。示例代码如下:
CCTexture2D *playerTexture = [[CCTextureCache sharedTextureCache] addImage:@"player.png"]; CCSprite *player2 = [CCSprite spriteWithTexture:playerTexture];(19)-(void)updateTransform该方法根据旋转角度、位置和比例值更新方形。示例代码如下:
CCSprite *sprite = [CCSprite spriteWithBatchNode:batch rect:CGRectMake(100, 100, 200, 200)]; [sprite updateTransform];注意 只有当该精灵对象是使用CCSpriteBatchNode渲染生成时,才可使用该方法。CCSprite类在Cocos2D中起着极其重要的作用,游戏角色、敌人、场景、道具等几乎都会使用CCSprite来创建。对于CCSprite类的属性和方法,大家需要多去尝试,并在实践中逐渐体会它的强大!3.6.2 CCSpriteBatchNode精灵表单CCSpriteBatchNode前身是旧版本Cocos2D中的CCSpriteSheet,也就是精灵表单。在学习CCSpriteBatchNode之前,需要了解纹理图集的基本概念。生活中处处充满了限制,游戏开发也不例外。事实上,制约游戏开发的最大问题在于硬件设备的性能瓶颈。游戏开发者最容易遇到的问题就是,当有大量的精灵出现在屏幕上时,游戏的运行会变得奇慢无比。到目前为止,所有图像元素都是使用独立的CCSprite,看起来没什么不妥。但是,当屏幕上有15或20个元素时,会发现游戏帧速率急剧下降;添加的图像元素越多,帧速率下降越快。即便不提供任何游戏逻辑机制,只是简单地移动这些CCSprite精灵,随着精灵数量的增加,游戏还是会变得越来越慢。导致这一问题的原因有很多,最重要的就是在导入图像元素时,开发者使用了单独的CCSprite,而不是CCSpriteBatchNode。简而言之,调用过多的OpenGL ES命令、把每个图像作为单独的纹理来处理已经超过了GPU所能处理的上限。解决这一问题最简单的方法就是使用纹理图集。如果仔细看看CCSprite相关代码(CCSprite.h文件),会发现在游戏的每一帧都会调用-(void)draw方法。在draw方法中,每次在屏幕中绘制一个精灵时程序都会调用真实的OpenGL ES命令(gl为前缀的方法,详见gl.h);对于每个精灵,OpenGL ES都需要将纹理图绑定在这个CCSprite上,然后将其绘制在屏幕上(称为渲染)。在屏幕上真实显示图像的像素前,还有一件事要做:iOS提供的OpenGL ES驱动将把OpenGL ES的命令转换成GPU可以理解的硬件编码,从而让GPU显示图像。作为一个游戏开发者,无需了解更多驱动细节,只需明白一点,每次调用OpenGL ES的命令都将耗费OpenGL ES驱动的CPU时钟。如果尽可能地减少OpenGL ES的调用,游戏的运行将更为顺畅。要实现这一点,就需要让OpenGL ES一次性处理所有纹理图,而使用CCSpriteBatchNode和纹理图集就能实现这一目的。1 . CCSpriteBatchNode类的属性CCSpriteBatchNode是Cocos2D中一个特殊的类,其中可包含多个CCSprite。它直接继承自CCNode,其特有的属性如下:textureAtlas:所使用的纹理图集。变量类型是CCTextureAtlas。blendFunc:混合方法,遵循CCTextureProtocol协议。变量类型是ccBlendFunc。descendants:子孙节点。变量类型是CCArray。2 . CCSpriteBatchNode类的方法除了继承自CCNode的方法,特有的方法如下:(1)- (id) initWithFile: (NSString *) fileImagecapacity:(NSUInteger)capacity该方法使用特定格式的图片文件(PNG、JPEG、PVR等)初始化一个CCSpriteBatchNode精灵表单,可以容纳29个子节点。在实际运行时如果超出了限制,该数字还可以再增加约33%。图片文件将使用TextureMgr加载。(2)-(id)initWithTexture:(CCTexture2D *)texcapacity:(NSUInteger)capacity该方法使用CCTexture2D纹理和指定的子节点容量初始化一个CCSpriteBatchNode精灵表单。在实际运行时,如果超出了限制,该数字还可以再增加约33%。(3)+ (id) batchNodeWithFile: (NSString *) fileImage该方法使用特定格式的图片文件(PNG、JPEG、PVR等)创建一个CCSpriteBatchNode精灵表单,可以容纳29个子节点。在实际运行时如果超出了限制,该数字还可以再增加约33%。图片文件将使用TextureMgr加载。示例代码如下:
CCSpriteBatchNode* spriteSheet = [CCSpriteBatchNode batchNodeWithFile:@"bullet.png"];该方法还有一个变化,就是指定子节点的数量。示例代码如下:
CCSpriteBatchNode* spriteSheet = [CCSpriteBatchNode batchNodeWithFile:@"bullet.png" capacity:12];(4)+(id)batchNodeWithTexture:(CCTexture2D *)tex该方法使用CCTexture2D纹理创建一个CCSpriteBatchNode精灵表单,可以容纳29个子节点。在实际运行时如果超出了限制,该数字还可以再增加约33%。示例代码如下:
CCTexture2D* gameArtTexture = [[CCTextureCache sharedTextureCache] addImage:@"bgStage1.png"]; CCSpriteBatchNode *spriteBatch = [CCSpriteBatchNode batchNodeWithTexture:gameArtTexture];该方法还有一个变化,就是指定子节点的数量。示例代码如下:
CCSpriteBatchNode *spriteBatch = [CCSpriteBatchNode batchNodeWithTexture:gameArtTexture capacity:12];(5)- (void) removeChild: (CCSprite *) spritecleanup: (BOOL) doCleanup该方法从CCSpriteBatchNode精灵表单中删除子精灵,由于这种操作速度非常慢,通常不推荐使用。示例代码如下:
[spriteSheet removeChild:bear cleanup:YES];该方法还有一个变化,就是根据指定的索引删除子精灵。示例代码如下:
[spriteSheet removeChildAtIndex:2 cleanup:YES];本书高级篇会详细说明如何使用纹理贴图和CCSpriteBatchNode实际提升游戏的性能。注意 在使用精灵表单时,CCSpriteBatchNode、CCSpriteFrame和CCSpriteFrameCache常常配合使用。3.6.3 CCSpriteFrame精灵帧与我们之前了解的节点类不同,CCSpriteFrame和CCSpriteFrameCache都直接继承自Objective-C中的NSObject。CCSpriteFrame(精灵帧)中主要包括CCTexture2D纹理、矩形大小,用来表示一个精灵。1 . CCSpriteFrame精灵帧的属性CCSpriteFrame(精灵帧)的主要属性有如下几个:rect和rectInPixels:精灵帧的矩形大小,分别用点值和像素值来表示。这两个属性是彼此相连的,任一个属性的变化都会自动更新另一个属性。变量类型是CGRect。rotated:精灵帧的矩形框是否发生旋转。变量类型是BOOL。offset和offsetInPixels:精灵帧的位置偏移(以像素值计算)。变量类型是CGPoint。originalSize和originalSizeInPixels:修剪后图片的原始大小(以像素值计算)。变量类型是CGSize。texture:精灵帧的纹理。变量类型是CCTexture2D。textureFilename:精灵帧的纹理文件名称。变量类型是NSString。2 . CCSpriteFrame精灵帧的方法CCSpriteFrame有两个实例方法和两个静态方法(类方法),分别如下:(1)-(id)initWithTexture:(CCTexture2D *)texturerect: (CGRect) rect该方法使用纹理、矩形(以点值表示)两个属性初始化一个CCSpriteFrame。使用此方法时,假定精灵帧未被裁剪过。(2)-(id)initWithTexture:(CCTexture2D *)texture
rectInPixels:(CGRect) rect rotated:(BOOL) rotated offset:(CGPoint) offset originalSize:(CGSize) originalSize该方法使用纹理、矩形(以像素值表示)、是否旋转、位置偏移和原始大小等属性初始化一个CCSpriteFrame,此处的原始大小是未被裁剪前的精灵帧大小。(3)+(id)frameWithTexture:(CCTexture2D *)texture
rect: (CGRect) rect该方法使用纹理、矩形(以点值表示)两个属性创建一个CCSpriteFrame。使用此方法时,假定精灵帧未被裁剪过。示例代码如下:CCSpriteFrame *frame1 = [CCSpriteFrame frameWithTexture:spriteSheet.texture rect:CGRectMake(228, 0, 75, 100)];(4)+(id)frameWithTexture:(CCTexture2D *)texture
rectInPixels:(CGRect) rect rotated:(BOOL) rotated offset:(CGPoint) offset originalSize:(CGSize) originalSize该方法使用纹理、矩形(以像素值表示)、是否旋转、位置偏移和原始大小等属性创建一个CCSpriteFrame,此处的原始大小是未被裁剪前的精灵帧大小。示例代码如下:
CCSpriteFrame *frame1 = [CCSpriteFrame frameWithTexture:batch.texture rectInPixels:rect rotated:YES offset:ccp(0,0) originalSize:size1];3.6.4 CCSpriteFrameCache精灵帧缓存CCSpriteFrameCache(精灵帧缓存)主要用来存放CCSpriteFrame,因此它没有提供特别的属性,而是提供一系列用于管理CCSpriteFrame的方法,具体如下。(1)- (void) addSpriteFrame: (CCSpriteFrame *) frame
name: (NSString *) frameName该方法使用指定名称添加一个精灵帧。如果该名称在CCSpriteFrameCache中已存在,则原有内容将被新精灵帧所替代。示例代码如下:
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFrame:frame name:[NSString stringWithFormat:@"playerFrame1"]];(2)- (void) addSpriteFramesWithDictionary: (NSDictionary *) dictionarytexture: (CCTexture2D *) texture该方法使用词典添加多个精灵帧,纹理将和所创建的精灵帧关联在一起。示例代码如下:
[self addSpriteFramesWithDictionary:dict texture:texture];(3)- (void) addSpriteFramesWithDictionary: (NSDictionary *) dictionary
textureFilename: (NSString *) filename该方法使用词典添加多个精灵帧,纹理文件名称将和所创建的精灵帧关联在一起。示例代码如下:
[self addSpriteFramesWithDictionary:dict textureFilename:textureFile];(4)- (void) addSpriteFramesWithFile: (NSString *) plist该方法从plist属性文件中添加多个精灵帧。纹理将会被自动载入,纹理的扩展名则使用.png替代.plist。如果需要添加其他纹理,请使用addSpriteFramesWithFile:texture方法。示例代码如下:
//自动载入纹理,纹理扩展名使用.png替代.plist [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"myspritesheet.plist"]; //添加其他纹理 [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"myspritesheet.plist" texture:myTexture];(5)- (void) addSpriteFramesWithFile: (NSString *) plist
textureFilename: (NSString *) textureFileName该方法从plist文件中添加多个精灵帧,纹理文件名将和所创建的精灵帧关联在一起。示例代码如下:
CCSpriteFrameCache *cache = [CCSpriteFrameCache sharedSpriteFrameCache]; [cache addSpriteFramesWithFile:@"t1.plist" textureFile:@"t1.png"];(6)+ (void) purgeSharedSpriteFrameCache该方法清除CCSpriteFrameCache中的缓存,删除所有精灵帧,并释放所保留的变量。示例代码如下:
[CCSpriteFrameCache purgeSharedSpriteFrameCache];(7)- (void) removeSpriteFrameByName: (NSString *)Name该方法根据名称从精灵帧缓存中删除一个精灵帧。示例代码如下:
[[CCSpriteFrameCache sharedSpriteFrameCache] removeSpriteFrameByName:playerSprite];(8)- (void) removeSpriteFrames该方法清除已加载精灵帧的词典。当收到内存警报时会调用此方法,短时间内的作用是避免应用被iOS强行关闭,中长期的作用是分配更多的资源。示例代码如下:
[[CCSpriteFrameCache sharedSpriteFrameCache]removeSpriteFrames];该方法还有一个变化,是从NSDictionary中删除多个精灵帧。示例代码如下:
[[CCSpriteFrameCache sharedSpriteFrameCache]removeSpriteFramesFromDictionary:dict];(9)- (void) removeSpriteFramesFromFile: (NSString *) plist该方法从plist属性文件中删除多个精灵帧。存储在该plist文件中的精灵帧都会被删除。示例代码如下:
[[CCSpriteFrameCache sharedSpriteFrameCache]removeSpriteFramesFromFile:plistName];(10)- (void) removeSpriteFramesFromTexture: (CCTexture2D *) texture该方法删除所有与特定纹理相关联的精灵帧。示例代码如下:
[[CCSpriteFrameCache sharedSpriteFrameCache]removeSpriteFramesFromTexture:texture];(11)-(void)removeUnusedSpriteFrames该方法删除所有未使用的精灵帧,所有retain计数为1的精灵帧都会被删除。当游戏切换新场景时可以调用此方法。示例代码如下:
[[CCSpriteFrameCache sharedSpriteFrameCache]removeUnusedSpriteFrames];-(CCSpriteFrame )spriteFrameByName:(NSString )name;返回之前添加的某个精灵帧,如果未找到对应名称的精灵帧,将返回nil。如果要使用该方法,需要retain这个精灵帧。示例代码如下:
CCAnimation *myAnim = [[CCAnimation alloc] initWithName:@"select" delay:0.15f]; [myAnim addFrame:[cache spriteFrameByName:@"anim1.png"]];(12)+(CCSpriteFrameCache *)sharedSpriteFrameCache该方法获取精灵帧缓存的单例对象。示例代码如下:
CCSpriteFrameCache *frameCache1 = [CCSpriteFrameCache sharedSpriteFrameCache];(13)-(CCSpriteFrame)spriteFrameByName:(NSString)name该方法使用文件名获取此前添加的精灵帧。示例代码如下:
CCSpriteFrame *frame1 = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:@"frame1.png"]; 相关资源:Cocos2D-X游戏开发技术精解