OpenGL是一个基于C语言的三维图形API,是一个开放的、跨平台的图形接口。
OpenGL ES是OpenGL在移动设备上的版本。
Cocos2d-x是一个基于OpenGL的游戏引擎,渲染功能由OpenGL实现。
游戏中会用到许多图片资源,对图片资源渲染进行优化能明显提高效率。
OpenGL中纹理的长和宽像素是2的幂,大小不足的纹理补充到2的幂大小;可以通过把多张小图片合成一张大图加载到游戏中,减少纹理加载次数,减少补齐2的幂大小的空白。
之前学习的纹理缓存、精灵帧缓冲可以把纹理等预先加载到内存中,访问时候节省访问时间;除此之外还可以通过CCSpriteBatchNode节省渲染次数来达到优化的目的。
CCSpriteBatchNode(精灵批处理类):
原理借鉴一下权威指南内容:
当你需要显示两个或两个以上相同的精灵时,如果逐个渲染精灵,每一次渲染都会调用OpenGL的函数;
因为当系统在屏幕上渲染一张贴图的时候,图形处理硬件必须首先准备渲染,然后渲染图形,最后完成渲染以后的清理工作。
以上是每次渲染固定的开销,这样帧率就会下降15%左右或者更多。
如果将所有需要渲染的同一张贴图只进行一次准备,一次渲染,一次清理就可以解决这个问题了。
这时可以使用CCSpriteBatchNode类来批处理这些精灵。
CCSpriteBatchNode:
先来看看它的创建相关代码:
class CC_DLL CCSpriteBatchNode : public CCNode, public CCTextureProtocol { public: static CCSpriteBatchNode* create(const char* fileImage, unsigned int capacity); static CCSpriteBatchNode* create(const char* fileImage) { return CCSpriteBatchNode::create(fileImage, kDefaultSpriteBatchCapacity); } static CCSpriteBatchNode* createWithTexture(CCTexture2D* tex, unsigned int capacity); static CCSpriteBatchNode* createWithTexture(CCTexture2D* tex) { return CCSpriteBatchNode::createWithTexture(tex, kDefaultSpriteBatchCapacity); } }由上述代码可以看到CCSpriteBatchNode可以通过图片文件直接创建,也可以通过纹理来创建。
CCSpriteBatchNode
(一)、通过图片文件创建CCSpriteBatchNode并且使用它
static CCSpriteBatchNode* create(const char* fileImage, unsigned int capacity); static CCSpriteBatchNode* create(const char* fileImage) { return CCSpriteBatchNode::create(fileImage, kDefaultSpriteBatchCapacity); }通过纹理图片直接创建精灵批处理我们可以使用两个创建函数,不过由上述代码可以看出其实只有一个创建函数;
只指定纹理图片路径名称时候,其第二个参数capacity(表示纹理图像容量)为一个系统默认值,并且此函数调用的为两个参数的create函数。
实例:
CCSpriteBatchNode* batchNode = CCSpriteBatchNode::create("HelloWorld.png"); for (int i = 0; i < 50; i++) { CCSprite* sprite = CCSprite::create("HelloWorld.png"); batchNode->addChild(sprite); } addChild(batchNode);首先,通过纹理图片创建一个精灵批处理;
之后,通过循环创建50个精灵,把这50个精灵添加到批处理当中;
最后把批处理加入到父节点。
(二)通过CCTexture2D创建CCSpriteBatchNode并且使用它
实例一:通过纹理缓存中取得纹理创建精灵批处理,此后创建精灵时候可以使用纹理图片名字创建。
CCTexture2D * texture = CCTextureCache::sharedTextureCache()->addImage("HelloWorld.png"); CCSpriteBatchNode* batchNode = CCSpriteBatchNode::createWithTexture(texture); for (int i = 0; i < 29; i++) { CCSprite* sprite = CCSprite::create("HelloWorld.png"); batchNode->addChild(sprite); } addChild(batchNode);
CCTexture2D * texture = CCTextureCache::sharedTextureCache()->addImage("HelloWorld.png"); CCSpriteBatchNode* batchNode = CCSpriteBatchNode::createWithTexture(texture); for (int i = 0; i < 50; i++) { CCSprite* sprite = CCSprite::createWithTexture(texture); batchNode->addChild(sprite); } addChild(batchNode);
上诉两种方式实际上使用起来同样效果,解释如下:
首先,通过纹理缓存获取一个纹理;
之后,通过循环创建50个精灵,把这50个精灵添加到批处理当中,此时创建精灵的时候可以使用纹理图片名称,也可以使用之前从纹理缓冲中获取的纹理;
最后把批处理加入到父节点。
实例二:通过直接创建一个纹理(CCTexture2D)来创建精灵批处理,此后精灵创建时候必须使用之前创建的纹理来创建精灵。
CCImage* image = new CCImage(); image->initWithImageFile("HelloWorld.png"); image->autorelease(); CCTexture2D* texture = new CCTexture2D(); texture->initWithImage(image); texture->autorelease(); CCSpriteBatchNode* batchNode = CCSpriteBatchNode::createWithTexture(texture); for (int i = 0; i < 50; i++) { CCSprite* sprite = CCSprite::createWithTexture(texture); batchNode->addChild(sprite); } addChild(batchNode);
首先,通过new的方式获取一个纹理;
之后,通过循环创建50个精灵,把这50个精灵添加到批处理当中,此时的精灵需要用到之前创建的纹理来创建;
最后把批处理加入到父节点。
实例三:加载多张图片使用
CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("batch.plist"); CCSpriteBatchNode* batchNode = CCSpriteBatchNode::create("bach.png"); addChild(batchNode);
上诉代码在精灵帧缓冲中加入一个plist文件;
利用plist对应的图片创建CCSpriteBatchNode;
之后
static bool flag = true; CCSprite * sprite; for (int i = 0; i < 1000; i++) { if (flag) { sprite = CCSprite::createWithSpriteFrameName("one.png"); } else{ sprite = CCSprite::createWithSpriteFrameName("two.png"); } flag = !flag; sprite->setPosition(ccp(CCRANDOM_0_1() * 480, CCRANDOM_0_1() * 320)); batchNode->addChild(sprite); }之前把纹理图片添加到精灵帧缓冲中后使用CCSpriteBatchNode创建同一张图片;
此后通过精灵帧缓冲中的精灵帧创建精灵同样为优化后的。
总结:
当我们需要多次使用同一张纹理创建的精灵时候使用它。
因为所有CCSprite节点都添加到同一个CCSpriteBatchNode中,所以所有CCSprite的zOrder相同。
添加到同一个CCSpriteBatchNode中的CCSprite必须使用同一个纹理图片。
精灵帧缓冲(CCSpriteFrameCache)与精灵批处理(CCSpriteBatchNode)结合使用效果更好!