天天看点

《Cocos2D权威指南》——3.6 CCSprite精灵类

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。

该属性只会对精灵的纹理进行翻转,不会影响到精灵的子节点,同时也不会修改锚点位置。如果想要同时修改锚点并对子节点进行翻转,我们可以使用下面的方法:

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和矩形(以像素值表示)初始化一个精灵对象,示例代码如下:

(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,使用图片的大小作为矩形,示例代码如下:

(4)- (id) initwithspriteframe: (ccspriteframe *) spriteframe

该方法使用精灵帧初始化一个精灵。

(5)- (id) initwithspriteframename: (nsstring *) spriteframename

该方法使用精灵帧的名称初始化一个精灵。使用该方法时,将从ccspriteframecache中获取一个ccspriteframe;如果不存在,系统将抛出一个异常。

(6)- (id) initwithtexture: (cctexture2d *)texturerect: (cgrect) rect

该方法使用纹理和矩形初始化一个精灵,位置偏移是(0,0)。该方法还有一个变化,就是使用纹理的大小作为矩形。示例代码如下:

(7)- (ccspriteframe*) displayedframe

该方法返回当前所显示的精灵帧。示例代码如下:

(8)-(bool) isframedisplayed:(ccspriteframe*)frame

该方法判断某个ccspriteframe对象是否正在被显示。示例代码如下:

(9)-(void) setdisplayframe:(ccspriteframe*)frame

该方法为ccsprite精灵设置新的显示帧。示例代码如下:

(10)- (void) setdisplayframewithanimationname: (nsstring *) animationname

index: (int) frameindex

该方法根据动画名称和索引更改要显示的帧,动画名将从ccanimationcache中获取。示例代码如下:

(11)- (void) settexturerect: (cgrect) rect

该方法更新ccsprite精灵对象的纹理矩形大小。示例代码如下:

注意 使用了settexturerect再使用contentsize时要注意,不能再当成初始化时的contentsize大小来使用。

这个方法在实际开发中的作用何在呢?想象用cocos2d开发一款rpg小游戏,游戏中有很多角色,而每个角色都有显示自己生命值的血条,我们只需在预定消息的方法中使用settexturerect,就可以实时更改角色的血条长度。

(12)- (void) settexturerect: (cgrect) rect

该方法更新ccsprite精灵对象的纹理矩形,矩形旋转和未裁剪大小。示例代码如下:

(13)+(id)spritewithbatchnode:(ccspritebatchnode *) batchnode

该方法使用ccspritebatchnode和矩形(以点数表示)来创建一个精灵对象。首先看该方法的实现代码:

关于实例方法和类方法在实际使用中的区别前面已经提到,大家只需要知道大多数情况下直接使用类方法来创建对象就可以了。示例代码如下:

(14)+(id)spritewithcgimage:(cgimageref)image key:(nsstring *)key

该方法使用cgimageref和键值创建一个精灵对象。cctexturecache将使用该键值判断某个纹理是否已使用cgimage创建。例如,某个有效键值可能是“sprite_frame_01”,如果键值是nil,则会每次使用cctexturecache创建一个新纹理,示例代码如下:

(15)+(id)spritewithfile:(nsstring *)filename rect:(cgrect)rect

该方法使用图片名称和矩形创建一个精灵对象,位置偏移是(0,0)。该方法的使用频率相当高,大多数时候使用它创建精灵对象。示例代码如下:

该方法有一个变化,就是不指定rect,使用图片的大小作为矩形。大多数情况下无需特别指定rect的大小。示例代码如下:

(16)+(id) spritewithspriteframe: (ccspriteframe *) spriteframe

该方法使用精灵帧创建一个精灵。示例代码如下:

(17)+ (id) spritewithspriteframename: (nsstring *) spriteframename

该方法使用精灵帧的名称创建一个精灵。该方法在使用时,将从ccspriteframecache中获取该ccspriteframe,如果精灵帧缓存中不存在该精灵帧,系统将抛出一个异常。示例代码如下:

(18)+ (id) spritewithtexture: (cctexture2d *)texture rect: (cgrect) rect

该方法使用纹理和矩形创建一个精灵,位置偏移是(0,0)。示例代码如下:

该方法还有一个变化,就是使用纹理自身的大小作为矩形。示例代码如下:

(19)-(void)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加载。示例代码如下:

该方法还有一个变化,就是指定子节点的数量。示例代码如下:

(4)+(id)batchnodewithtexture:(cctexture2d *)tex

该方法使用cctexture2d纹理创建一个ccspritebatchnode精灵表单,可以容纳29个子节点。在实际运行时如果超出了限制,该数字还可以再增加约33%。示例代码如下:

(5)- (void) removechild: (ccsprite *) spritecleanup: (bool) docleanup

该方法从ccspritebatchnode精灵表单中删除子精灵,由于这种操作速度非常慢,通常不推荐使用。示例代码如下:

该方法还有一个变化,就是根据指定的索引删除子精灵。示例代码如下:

本书高级篇会详细说明如何使用纹理贴图和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

该方法使用纹理、矩形(以像素值表示)、是否旋转、位置偏移和原始大小等属性初始化一个ccspriteframe,此处的原始大小是未被裁剪前的精灵帧大小。

(3)+(id)framewithtexture:(cctexture2d *)texture

该方法使用纹理、矩形(以点值表示)两个属性创建一个ccspriteframe。使用此方法时,假定精灵帧未被裁剪过。示例代码如下:

ccspriteframe *frame1 = [ccspriteframe framewithtexture:spritesheet.texture rect:cgrectmake(228, 0, 75, 100)];

(4)+(id)framewithtexture:(cctexture2d *)texture

该方法使用纹理、矩形(以像素值表示)、是否旋转、位置偏移和原始大小等属性创建一个ccspriteframe,此处的原始大小是未被裁剪前的精灵帧大小。示例代码如下:

3.6.4 ccspriteframecache精灵帧缓存

ccspriteframecache(精灵帧缓存)主要用来存放ccspriteframe,因此它没有提供特别的属性,而是提供一系列用于管理ccspriteframe的方法,具体如下。

(1)- (void) addspriteframe: (ccspriteframe *) frame

该方法使用指定名称添加一个精灵帧。如果该名称在ccspriteframecache中已存在,则原有内容将被新精灵帧所替代。示例代码如下:

(2)- (void) addspriteframeswithdictionary: (nsdictionary *) dictionary

texture: (cctexture2d *) texture

该方法使用词典添加多个精灵帧,纹理将和所创建的精灵帧关联在一起。示例代码如下:

(3)- (void) addspriteframeswithdictionary: (nsdictionary *) dictionary

该方法使用词典添加多个精灵帧,纹理文件名称将和所创建的精灵帧关联在一起。示例代码如下:

(4)- (void) addspriteframeswithfile: (nsstring *) plist

该方法从plist属性文件中添加多个精灵帧。纹理将会被自动载入,纹理的扩展名则使用.png替代.plist。如果需要添加其他纹理,请使用addspriteframeswithfile:texture方法。示例代码如下:

(5)- (void) addspriteframeswithfile: (nsstring *) plist

该方法从plist文件中添加多个精灵帧,纹理文件名将和所创建的精灵帧关联在一起。示例代码如下:

(6)+ (void) purgesharedspriteframecache

该方法清除ccspriteframecache中的缓存,删除所有精灵帧,并释放所保留的变量。示例代码如下:

(7)- (void) removespriteframebyname: (nsstring *)name

该方法根据名称从精灵帧缓存中删除一个精灵帧。示例代码如下:

(8)- (void) removespriteframes

该方法清除已加载精灵帧的词典。当收到内存警报时会调用此方法,短时间内的作用是避免应用被ios强行关闭,中长期的作用是分配更多的资源。示例代码如下:

该方法还有一个变化,是从nsdictionary中删除多个精灵帧。示例代码如下:

(9)- (void) removespriteframesfromfile: (nsstring *) plist

该方法从plist属性文件中删除多个精灵帧。存储在该plist文件中的精灵帧都会被删除。示例代码如下:

(10)- (void) removespriteframesfromtexture: (cctexture2d *) texture

该方法删除所有与特定纹理相关联的精灵帧。示例代码如下:

(11)-(void)removeunusedspriteframes

该方法删除所有未使用的精灵帧,所有retain计数为1的精灵帧都会被删除。当游戏切换新场景时可以调用此方法。示例代码如下:

-(ccspriteframe )spriteframebyname:(nsstring )name;

返回之前添加的某个精灵帧,如果未找到对应名称的精灵帧,将返回nil。如果要使用该方法,需要retain这个精灵帧。示例代码如下:

(12)+(ccspriteframecache *)sharedspriteframecache

该方法获取精灵帧缓存的单例对象。示例代码如下:

(13)-(ccspriteframe)spriteframebyname:(nsstring)name

该方法使用文件名获取此前添加的精灵帧。示例代码如下:

继续阅读