智慧 + 毅力 = 无所不能

正确性、健壮性、可靠性、效率、易用性、可读性、可复用性、兼容性、可移植性...

导航

转自:http://www.cnblogs.com/andyque/archive/2011/03/18/1988097.html

cocos2d和像素格式

  在cocos2d里面,理解像素格式非常重要。因为,像素格式会影响在你的游戏中加载一张图片到底需要多少内存。因为游戏通常要加载大量的图片资源,所以你要尽可能充分利用移动设备上面非常少的可用物理内存。

  默认情况下面,当你在cocos2d里面加载一张图片的时候,对于每一个像素点使用4个byte来表示--1个byte(8位)代表red,另外3个byte分别代表green、blue和alpha透明通道。这个就简称RGBA8888。

  因此,如果你使用默认的像素格式来加载图片的话,你可以通过下面的公式来计算出将要消耗多少内存来加载:

  图像宽度(width)×图像高度(height)×每一个像素的位数(bytes per pixel) = 内存大小

  此时,如果你有一张512×512的图片,那么当你使用默认的像素格式去加载它的话,那么将耗费

  512×512×4=1MB(好多啊!)

  这里,我们以Iphone3G为例。它总共只有128兆内存,但是系统就要占掉一大半,还有其它一些程序也要使用一些内存,实际可用的内存更少。对于单独一张spritesheet来说那确实足够了。可是想像一下你有许许多多的spritesheet,而且游戏里面经常需要大量的spritesheet!

  这里就需要让像素格式来帮忙了。你可以为图片的每个像素点指定更小的字节来保存图片。(比如每个像素点2个字节,即每个像素点16位),这种方式就能够在图片质量和内存消耗之间取得一个很好的平衡点。

  通常,你是在你的游戏看起来还ok的提前下,尽可能少地使用内存。背景图片就非常适用用8位或者16位来存储,而精灵则一般要用16位或者32位。对于更多可选的像素格式和适用的场合,你可以参考Riq(cocos2d的作者)的一篇文章: understanding pixel format guide.(理解像素格式向导)

  顺便说一下,如果你注意看窗口的右下角,你会看到Texture Packer会基于你当前选择的像素格式计算出这张spritesheet所消耗的内存大小,因此你不必手动计算了。:)

像素格式和抖动

  很多时候,当你使用较小的像素格式来加载图片的时候,你会发现图片的质量也在相应的降低。这时你会看到图像存在许多颜色的梯度变化。这里有一个例子,展示了当你使用像素格式RBGA4444去显示一张图片的时候会是什么样子:

  看到没有,图像上面有许许多多的“条条”和颜色梯度变化,特别是熊和绿色的框框那里。

  这时,你可能想重新设计你的图片来确保使用更少的梯度,或者使用大一点的像素格式。但是,在这里,TexturePacker实现了另外一个杀手锏功能--图像抖动。

  当你使用TexturePacker来保存spritesheet的时候,你可以指定目标像素格式为RGBA4444,然后选择“dithering method”。这个默认选项会修改一些你的图像的颜色,但是当有梯度变化或者其它一些会带来问题的颜色以后,图像看起来就会非常糟糕。

  继续,我们为spritesheet选择RBGA4444格式,然后改变抖动选项为“FloydSteinberg+Alpha”。Texture Packer将会在动态修改你的图片,而且马上显示出效果来。和上面的图片相比,是不是好看多了?

  现在让我们保存这个spritesheet。点击Texture file旁边的“...”,在弹出的对话框中选择TextureFun\Resouces目录,然后命名为“sprites-hd.pvr.ccz”。然后,TexturePack会自动为我们在Data file那里生成相应的plist文件路径。并且会命名为“sprites-hd.plist”,这个名字是根据前面你提要的名字来命名的。

  “但是,等一下!”,你可能会说,“为什么是pvr.ccz?!”。好吧,我很高兴你会这样问。。。

PVRs和压缩

  PVR图像是专门为ios设备上面的PowerVR图形芯片指定的图像容器。它们在ios设备上非常好用,因为可以直接加载到显卡上面,而不需要经过中间的转化。

  PVR图像也可以包含许多种不同像素格式的图像数据。之前,cocos2d仅仅支持一些用  sdk指定的 texturetool app来创建的图片格式,不过后来cocos2d已经扩展了许多格式了。

  而且,最近cocos2d更新到了可以支持压缩了的pvr图像格式pvr.ccz。使用这种图片格式的好处有两点:一、可以使你的应用程序更小,因为图片是压缩过了的。二、你的游戏能够启动地更快。

  总而言之,对于spritesheet来说,你可能通过指定16位的像素格式来减少内存消耗,同时保存为pvr.ccz格式来使程序加载速度更快。最后,点击“Publish”按钮,你的spritesheet和属性列表文件就生成好了。Teture Packer会提示你,一些精灵将会创建成红色(因为你使用的是免费版本)。

优化背景图片

  现在,让我们也来加载并优化一下我们的背景图片。点击new创建一个新的Texture Packer窗口,然后点击“Add Folder”,并且选择“TextureFun\Art\flower”文件夹。

  把图片格式改成RBG565(对于大的图片来说,你可能需要更好的质量),然后改变抖动方法为“FloydSteinberg”(为什么不是FloydSteinberg+Alpha呢?因为像素格式是RBG565,没有了Alpha通道)。然后指定保存texture file的路径为“TextureFun\Resouces\flower-hd.pvr.ccz”。最后,点击“Publish”,关闭警告信息,这时你的屏幕看起来会是下面这样:

在cocos2d里面使用spritesheet

  现在回到我们的项目,右键点击Resources,然后选择“Add\Existing Files...”。选择flower-hd.plist, lower-hd.pvr.ccz, sprites-hd.plist, and sprites-hd.pvr.ccz。同时,确保没有选中“ Copy items into destination group’s folder (if needed)”,然后单击完成。

  下一步,打开HelloWorldScene.m,并且用下面的代码替换掉你的init方法里的内容:

-(id) init
{
if( (self=[super init] )) {
CGSize winSize = [CCDirector sharedDirector].winSize;

[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGB565]; 
CCSprite * background = [CCSprite spriteWithFile:@"flower-hd.pvr.ccz"];
background.anchorPoint = ccp(0,0);
[self addChild:background];

// More coming here soon...
}
return self;
}

  你需要做的第一件事情就是加载背景图片。首先,你告诉cocos2d使用RBG565的像素格式(你正在为你的背景图片的每个像素使用8位),然后调用spriteWithFile从磁盘上加载pvr.ccz格式的图片。注意,这里你并不需要把它当作一个spritesheet(比如,加载plist文件),因为这里“spritesheet”就只有一张图片。

  注意,其实你在加载pvr.ccz格式的文件的时候并不需要指定像素格式,因为这个文件格式本身就包含了这样一些信息。但是,我们还是显示地在这里指 定了像素格式,因为如果你加载png格式的图片的话,(png格式图片总是保存为每个像素32位,尽管你可能会使用不同的像素格式把它加载到内存里)。

  下面,让我们在“more coming here soon”注释的地方添加下面的代码:

[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA4444];
CCSpriteBatchNode *spritesBgNode;
spritesBgNode = [CCSpriteBatchNode batchNodeWithFile:@"sprites-hd.pvr.ccz"];
[self addChild:spritesBgNode]; 
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"sprites-hd.plist"];

  这里把像素格式设置为RBGA4444(你为主精灵所使用的每个像素点16位的像素格式),然后为这个spritesheet创建一个batch node。你也需要加载plist文件,把每一个精灵对应的帧(frame)加载到精灵帧缓冲区(sprite frame cache)中。

  最后,紧接着上面加入下面的代码:

NSArray *images = [NSArray arrayWithObjects:@"bear_2x2.png", @"bird.png", @"cat.png", @"dog.png", @"turtle.png", @"ooze_2x2.png", nil]; 
for(int i =0; i < images.count; ++i) {
NSString *image = [images objectAtIndex:i];
float offsetFraction = ((float)(i+1))/(images.count+1); 
CGPoint spriteOffset = ccp(winSize.width*offsetFraction, winSize.height/2);
CCSprite *sprite = [CCSprite spriteWithSpriteFrameName:image];
sprite.position = spriteOffset;
[spritesBgNode addChild:sprite];
}

[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_Default];

      这个循环遍历spritesheet中的所有的图片,并把他们合理地旋转在屏幕上面。

  如果你使用iPad来编译并运行你的代码的话,你会得到下面的运行结果:

  这个不就是你想实现的效果吗--记住,这里的红色仅仅因为你使用的是免费版本。(ps:正式版本也不贵,100多块人民币可以搞定)

  但是真正让人着迷的是那些你看不到的东西。

  在背后,你的应用程序加载速度会明显比以前快很多。而且使用更少的内存,更让人心动的是,它看起来还是那么棒!而这些功能使用Texture Packer都可以很容易的完成。

不相信我?

  当我写这篇文章的时候,我做了一系列的简单的测试,从最好的情形到最坏的情形,来测试到底我的程序是如何运转的。下面是我得出的一些结论:

  • 做最原始的事情。使用默认像素格式单个单个精灵地加载,不使用任何spritesheet。大约花费了0.73秒钟加载,消耗大约26兆内存。而且当你添加更多的精灵进去的时候,游戏就开始卡了。
  • 使用默认的像素格式,并且使用spritesheet:前进了一大步。这样会使游戏性能更好,同时也会减少内存消耗(因为你会把所有的精灵加载到一张大小的spritesheet的,而opengl使用纹理的大小都是2的指数幂,如果你一张精灵的大小是320×200的话,那么opengl会创建512×512的纹理来加载精灵,这样就有很多空白的地方。实际上就是浪费了内存。)
  • 使用不含抖动的Zwoptex并保存为png格式,同时减少像素格式:这样可以大幅地减少内存消耗,大约只需要15兆左右)。但是却增加了程序的启动时间,上升到大约1秒钟。我认为可能是由于不得不改变颜色缓冲的缘故吧。另外,图像显示的效果并没有在“像素格式和抖动”一节中的截屏效果那么好。
  • 使用通过Texture Packer创建的抖动过的spritesheet并且保存为pvr.ccz格式:这在启动时间和显示效果上都前进了一大步!(启动时间大约只有0.31秒左右,内存也只需要大约17兆左右,我认为这可能是由于内存泄漏,这个问题在现在的版本中已经解决了。

  好了,如果你按照上面所讲的最佳实践来做的话,我想你在大部分情况下都会做得非常好。

Texture Packer vs. Zwoptex

  首先,让我们再说一遍,我是Zwoptex的超级粉丝。我认为Robert已经做了一件非常好的事情来把这些东西整合到一起,而且我老实说,如果没有这个工具,cocos2d不会走到今天这一步!

  然后,当我们来比较Texture Packer和Zwoptex的特性的时候,Texture Packer似乎包含了Zwoptex90%的功能。但是,Texture Packer有三个杀手锏级别的功能是Zwoptex所没有的。

  • 抖动,抖动,抖动. 噢,我是多么喜欢抖动啊!在过去,有时我想使用比较低的像素格式,但是我不能,因为它看起来效果很不好。但是Texture Packer内置的抖动功能使得图片看起来还是那么棒,尽管此时的图片质量很低。 
  • pvr.ccz 支持. 我喜欢这个特性. 它确实能够使游戏启动得更快, 而且会使你的可执行程序更小. 现在我不用花很长时间来上传和下载我以前写的应用程序了,我可以很快的下载并更新。
  • 命令行工具支持. 一旦你花一点时间把Texture Packer集成到你的Xcode中去,你将会热爱生活。它是如此地方便,特别是在整个开发过程中,美工对图片改来改去的时候。

  尽管Zwoptex非常棒, 但是它目前为此还是没有提供我这里列举的这些功能特性。