ccsprite::create("ss.png") 加载到缓存 + 渲染需要两倍内存
/
如果你看看这个CCSprite::create(pszFileName)函数的内部实现方法,你将会发现它添加这些图片到了纹理缓存中:
/
CCSpriteBatchNode * batchNode = CCSpriteBatchNode::create(
"icon.png"
);
CCSprite * sprite = CCSprite::createWithTexture(batchNode->getTexture()); 多个sprite时只需要加载一次 一倍内存
cocos2dx针对游戏设计的不同方面会有不同的优化方案,可以对声音,对内存,对图片格式,对色彩等等进行优化。有关这些方面的方法请大家查找其他的文章。我今天要说的是如何对精灵进行优化,程序中我们用到的最多的就是精灵,大到背景、UI,小到 NPC、道具,只要是用图片展示的,都是精灵或它的子类。精灵是什么,在我看来精灵就是一张纹理图片,是按某种方式显示出来的图片。精灵如此的重要,我们当然要好好的优化优化了。我们可以减小精灵图片的大小,使用缓存Cache的方法将精灵提前加载到内存中,当有很多精灵的时候,使用批次渲染的方法。所以我就从缓存和批次渲染这俩个方面来说一下如何优化我们的精灵图片。
1、通过批次渲染的方法来优化精灵。在游戏中的某一时刻,有时候会用到大量的精灵,比如说发射子弹,粒子效果,这些精灵图片所使用的纹理都是相同的,如果一张一张的图片进行渲染势必会降低效率,大家在开发中看到的窗口的左下角的三行数字中,第一行数字就是渲染批次,这个渲染批次在我看来就是画了多少次,cocos2dx中使用opengl进行绘图,渲染的批次越少当然越好了,所以这个渲染批次才会显示在左下角让我们参考。我们应当尽量的减小这个渲染的批次,提高游戏的效率。方法就是使用CCSpriteBatchNode和CCParticleBatchNode精灵批节点类和粒子批节点类。先来看看在代码中如何使用它们。
1 | CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize(); |
5 | CCSprite * sprite = CCSprite::create( "icon.png" ); |
6 | sprite->setPosition(ccp(CCRANDOM_0_1()*visibleSize.width,CCRANDOM_0_1()*visibleSize.height)); |
7 | this ->addChild(sprite); |
上边的这种情况是采用一般的方式将精灵添加到层中的,这个时候的渲染批次是10。
2 | CCSpriteBatchNode * batchNode = CCSpriteBatchNode::create( "icon.png" ); |
6 | batchNode->setPosition(CCPointZero); |
10 | CCSprite * sprite = CCSprite::createWithTexture(batchNode->getTexture()); |
11 | sprite->setPosition(ccp(CCRANDOM_0_1()*visibleSize.width,CCRANDOM_0_1()*visibleSize.height)); |
12 | batchNode->addChild(sprite); |
14 | this ->addChild(batchNode); |
采用上边的方法添加精灵到层中,渲染批次是1,就是因为我们用到了CCSpriteBatchNode这个类,从道理上来说,这个类也是一个node,通常我们使用node的时候就是为了方便的管理精灵,创建一个node节点,然后将精灵放到这个节点中,而node本身是没法显示出来的,显示出来的东西是放到它里边的node子节点,node的长和宽都是0,坐标默认是在父节点的左下角也就是原点处。所以对子节点位置的设置不会产生影响。CCSpriteBatchNode就是这样的一个node,不同的是创建的时候需要一个纹理,要不怎么叫sprite node呢?里边传入的纹理图片是子节点用到的纹理图片,我们可以设置好这些子精灵节点的坐标,然后添加到这个node中,这个node再添加到其他的层中,这样就可以批次渲染了。这个node要求它的字精灵节点和它使用相同的纹理,那如果纹理不一样怎么办,那就把纹理都打包到一张图片上,用的时候从这张图片上截取,工具可以使用texturepacker。
1 | CCTexture2D * texture = CCTextureCache::sharedTextureCache()->addImage( "fire.png" ); |
5 | CCParticleSystem * particle = CCParticleSun::create(); |
6 | particle->setTexture(texture); |
7 | particle->setPosition(ccp(CCRANDOM_0_1()*visibleSize.width,CCRANDOM_0_1()*visibleSize.height)); |
8 | this ->addChild(particle); |
1 | CCTexture2D * texture = CCTextureCache::sharedTextureCache()->addImage( "fire.png" ); |
3 | CCParticleBatchNode * particleBatch = CCParticleBatchNode::createWithTexture(texture); |
9 | CCParticleSystem * particle = CCParticleSun::create(); |
11 | particle->setTexture(texture); |
12 | particle->setPosition(ccp(CCRANDOM_0_1()*visibleSize.width,CCRANDOM_0_1()*visibleSize.height)); |
13 | particleBatch->addChild(particle); |
15 | this ->addChild(particleBatch); |
这里的道理是和CCSpriteBatchNode相同的,就不用解释了吧。
2、使用缓存提前加载精灵。当我们使用纹理的时候可以制作一个loading界面,将将要用到的纹理加载到缓存中,同时将它们的引用计数增加一,以便这些纹理不会被释放。用的时候直接从缓存中取就可以了,这样也可以提高效率。我们用到的缓存类有CCTextureCache、CCSpriteFrameCache、CCAnimationCache,下面分别说明其用法。
1 | bool HelloWorld::init() |
5 | if ( !CCLayer::init() ) |
10 | CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize(); |
13 | CCTexture2D * texture = CCTextureCache::sharedTextureCache()->addImage( "icon.png" ); |
14 | CCLog( "%d" ,texture->retainCount()); |
17 | texture = CCTextureCache::sharedTextureCache()->addImage( "icon.png" ); |
18 | texture = CCTextureCache::sharedTextureCache()->textureForKey( "icon.png" ); |
19 | CCLog( "%d" ,texture->retainCount()); |
23 | CCTextureCache::sharedTextureCache()->addImageAsync( "icon1.png" , |
24 | this ,callfuncO_selector(HelloWorld::loadingCallBack)); |
29 | void HelloWorld::loadingCallBack(CCObject * object) |
31 | CCTexture2D * texture = (CCTexture2D *)object; |
32 | CCLog( "%d" ,texture->retainCount()); |
38 | CCTextureCache::sharedTextureCache()->removeAllTextures(); |
44 | CCLog( "%d" ,texture->retainCount()); |
47 | CCTextureCache::sharedTextureCache()->dumpCachedTextureInfo(); |
50 | CCDirector::sharedDirector()->replaceScene(TestScene::scene()); |
在新的场景中使用加载好的纹理。
4 | CCTexture2D * texture = CCTextureCache::sharedTextureCache()->textureForKey( "icon1.png" ); |
5 | CCLog( "%d" ,texture->retainCount()); |
CCTextureCache在我看来就是一个数组,将加载的纹理都放到这个数组中,然后将它们的引用计数加1以防释放掉,我们要使用加载好的纹理直接从这个数组中取就可以了,返回一个CCTexture2D的对象,然后我们使用CCSprite:createWithTexture这个方法来创建出精灵。我觉的这里最主要的问题就是纹理的释放,个人认为使用removeUnusedTexture这个函数比较好,它会释放掉引用计数为1的纹理,也就说明程序中没有再为它增加引用计数,肯定是没用了。那些大于1的纹理不会释放掉,即使我们在不知道的情况下再次加载,也只是返回已经加载好了的。接下来是另外俩个缓存了,和这个意思差不多,直接看代码吧。
1 | bool HelloWorld::init() |
5 | if ( !CCLayer::init() ) |
10 | CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize(); |
13 | CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile( "ghosts.plist" ); |
16 | CCSpriteFrame * spriteFrame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName( "child1.gif" ); |
17 | CCLog( "%d" ,spriteFrame->retainCount()); |
20 | CCAnimation * animation = CCAnimation::create(); |
22 | animation->addSpriteFrame(spriteFrame); |
23 | CCLog( "%d" ,spriteFrame->retainCount()); |
25 | CCLog( "%d" ,animation->retainCount()); |
28 | CCAnimationCache::sharedAnimationCache()->addAnimation(animation, "a" ); |
29 | CCLog( "%d" ,animation->retainCount()); |
31 | CCAnimate * animate = CCAnimate::create(CCAnimationCache::sharedAnimationCache()->animationByName( "a" )); |
34 | CCAnimationCache::sharedAnimationCache()->removeAnimationByName( "a" ); |
37 | CCSpriteFrameCache::sharedSpriteFrameCache()->removeUnusedSpriteFrames(); |