Cocos2dx對精靈的優化

cocos2dx針對遊戲設計的不一樣方面會有不一樣的優化方案,能夠對聲音,對內存,對圖片格式,對色彩等等進行優化。有關這些方面的方法請你們查找其餘的文章。我今天要說的是如何對精靈進行優化,程序中咱們用到的最多的就是精靈,大到背景、UI,小到 NPC、道具,只要是用圖片展現的,都是精靈或它的子類。精靈是什麼,在我看來精靈就是一張紋理圖片,是按某種方式顯示出來的圖片。精靈如此的重要,咱們固然要好好的優化優化了。咱們能夠減少精靈圖片的大小,使用緩存Cache的方法將精靈提早加載到內存中,當有不少精靈的時候,使用批次渲染的方法。因此我就從緩存和批次渲染這倆個方面來講一下如何優化咱們的精靈圖片。
一、經過批次渲染的方法來優化精靈。在遊戲中的某一時刻,有時候會用到大量的精靈,好比說發射子彈,粒子效果,這些精靈圖片所使用的紋理都是相同的,若是一張一張的圖片進行渲染勢必會下降效率,你們在開發中看到的窗口的左下角的三行數字中,第一行數字就是渲染批次,這個渲染批次在我看來就是畫了多少次,cocos2dx中使用opengl進行繪圖,渲染的批次越少固然越好了,因此這個渲染批次纔會顯示在左下角讓咱們參考。咱們應當儘可能的減少這個渲染的批次,提升遊戲的效率。方法就是使用CCSpriteBatchNode和CCParticleBatchNode精靈批節點類和粒子批節點類。先來看看在代碼中如何使用它們。

<pre class="brush:cpp; toolbar: true; auto-links: false;">

CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
//建立了十個精靈將這十個精靈添加到當前的層中
for(int i=0;i<10;i++)
{
    CCSprite * sprite = CCSprite::create("icon.png");
    sprite->setPosition(ccp(CCRANDOM_0_1()*visibleSize.width,CCRANDOM_0_1()*visibleSize.height));
    this->addChild(sprite);
}


</pre>





html



上邊的這種狀況是採用通常的方式將精靈添加到層中的,這個時候的渲染批次是10。

<pre class="brush:cpp; toolbar: true; auto-links: false;">

//建立一個CCSpriteBatchNode,傳入的參數就是精靈們將要用到的圖片
    CCSpriteBatchNode * batchNode = CCSpriteBatchNode::create("icon.png");
    //或者使用texture2d初始化,裏邊傳入一個texture
    //CCSpriteBatchNode * batch = CCSpriteBatchNode::createWithTexture();
    //這一句寫不寫均可以,由於node的默認座標就是(0,0)
    batchNode->setPosition(CCPointZero);
    for(int i=0;i<10;i++)
    {
        //建立的這些精靈所使用的紋理必須和CCSpriteBatchNode相同,並且全部這些精靈必須在同一個渲染層
        CCSprite * sprite = CCSprite::createWithTexture(batchNode->getTexture());
        sprite->setPosition(ccp(CCRANDOM_0_1()*visibleSize.width,CCRANDOM_0_1()*visibleSize.height));
        batchNode->addChild(sprite);
    }
    this->addChild(batchNode);


</pre>node

採用上邊的方法添加精靈到層中,渲染批次是1,就是由於咱們用到了CCSpriteBatchNode這個類,從道理上來講,這個類也是一個node,一般咱們使用node的時候就是爲了方便的管理精靈,建立一個node節點,而後將精靈放到這個節點中,而node自己是無法顯示出來的,顯示出來的東西是放到它裏邊的node子節點,node的長和寬都是0,座標默認是在父節點的左下角也就是原點處。因此對子節點位置的設置不會產生影響。CCSpriteBatchNode就是這樣的一個node,不一樣的是建立的時候須要一個紋理,要不怎麼叫sprite node呢?裏邊傳入的紋理圖片是子節點用到的紋理圖片,咱們能夠設置好這些子精靈節點的座標,而後添加到這個node中,這個node再添加到其餘的層中,這樣就能夠批次渲染了。這個node要求它的字精靈節點和它使用相同的紋理,那若是紋理不同怎麼辦,那就把紋理都打包到一張圖片上,用的時候從這張圖片上截取,工具可使用texturepacker。
 



<pre class="brush:cpp; toolbar: true; auto-links: false;">

CCTexture2D * texture = CCTextureCache::sharedTextureCache()->addImage("fire.png");
    for(int i=0;i<10;i++)
    {
        //CCParticleSun裏邊沒有參數
        CCParticleSystem * particle = CCParticleSun::create();
        particle->setTexture(texture);
        particle->setPosition(ccp(CCRANDOM_0_1()*visibleSize.width,CCRANDOM_0_1()*visibleSize.height));
        this->addChild(particle);
    }


</pre>數組

 


<pre class="brush:cpp; toolbar: true; auto-links: false;">

CCTexture2D * texture = CCTextureCache::sharedTextureCache()->addImage("fire.png");
    //參數一樣須要一張紋理圖片
    CCParticleBatchNode * particleBatch = CCParticleBatchNode::createWithTexture(texture);
    //也能夠採用以下的方式建立
    //CCParticleBatchNode * particleBatch = CCParticleBatchNode::create("fire.png");
    for(int i=0;i<10;i++)
    {
        //CCParticleSun裏邊沒有參數
        CCParticleSystem * particle = CCParticleSun::create();
        //傳入的texture必須和CCParticleBatchNode相同
        particle->setTexture(texture);
        particle->setPosition(ccp(CCRANDOM_0_1()*visibleSize.width,CCRANDOM_0_1()*visibleSize.height));
        particleBatch->addChild(particle);
    }
    this->addChild(particleBatch);


</pre>緩存

這裏的道理是和CCSpriteBatchNode相同的,就不用解釋了吧。
二、使用緩存提早加載精靈。當咱們使用紋理的時候能夠製做一個loading界面,將將要用到的紋理加載到緩存中,同時將它們的引用計數增長一,以便這些紋理不會被釋放。用的時候直接從緩存中取就能夠了,這樣也能夠提升效率。咱們用到的緩存類有CCTextureCache、CCSpriteFrameCache、CCAnimationCache,下面分別說明其用法。


<pre class="brush:cpp; toolbar: true; auto-links: false;">
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !CCLayer::init() )
    {
        return false;
    }

    CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
    //緩存其實就是一個數組,把用到的紋理圖片放到這個數組中,紋理的引用計數加1,這樣的話就不會釋放紋理圖片了
    //等下一次使用的時候直接從這個數組中取就能夠了,這樣的話就沒必要再次加載到內存中了
    CCTexture2D * texture = CCTextureCache::sharedTextureCache()->addImage("icon.png");
    CCLog("%d",texture->retainCount()); //count=1
    //使用以下倆種方法能夠得到緩存中的紋理,第二種方法若是以前已經加載了紋理,這個時候不會從新加載
    //而是直接返回
    texture = CCTextureCache::sharedTextureCache()->addImage("icon.png");
    texture = CCTextureCache::sharedTextureCache()->textureForKey("icon.png");
    CCLog("%d",texture->retainCount()); //count=1

    //異步加載圖片,開闢一個新的線程專門用來加載圖片,加載完畢之後調用loadingCallBack函數
    //因此在這個init函數中是不該該去調用函數textureForKey的,由於你不知道是否加載好了紋理啊
    CCTextureCache::sharedTextureCache()->addImageAsync("icon1.png",
        this,callfuncO_selector(HelloWorld::loadingCallBack));

    return true;
}
//object就是加載好了的紋理
void HelloWorld::loadingCallBack(CCObject * object)
{
    CCTexture2D * texture = (CCTexture2D *)object;
    CCLog("%d",texture->retainCount()); //count=2
    //經過如下的方法取得的texture和上邊的那個texture相同
    //CCTexture2D * texture2 = CCTextureCache::sharedTextureCache()->textureForKey("icon1.png");
    //CCLog("%d",texture2->retainCount());

    //會清除掉全部的紋理,在其餘的地方不能夠再引用這些紋理了
    CCTextureCache::sharedTextureCache()->removeAllTextures();

    //如下的方法會remove掉count值爲1的紋理,icon.png被remove掉了,我的認爲實際用的時候就用這個
    //CCTextureCache::sharedTextureCache()->removeUnusedTextures();

    //count=1
    CCLog("%d",texture->retainCount());

    //查看紋理清除的信息
    CCTextureCache::sharedTextureCache()->dumpCachedTextureInfo();

    //切換場景
    CCDirector::sharedDirector()->replaceScene(TestScene::scene());
}


</pre>
在新的場景中使用加載好的紋理。
<pre class="brush:cpp; toolbar: true; auto-links: false;">

bool TestScene::init()
{
    //由於將全部的紋理清除掉了,因此這裏引用的時候會出錯
    CCTexture2D * texture = CCTextureCache::sharedTextureCache()->textureForKey("icon1.png");
    CCLog("%d",texture->retainCount());

    return true;
}


</pre>
CCTextureCache在我看來就是一個數組,將加載的紋理都放到這個數組中,而後將它們的引用計數加1以防釋放掉,咱們要使用加載好的紋理直接從這個數組中取就能夠了,返回一個CCTexture2D的對象,而後咱們使用CCSprite:createWithTexture這個方法來建立出精靈。我覺的這裏最主要的問題就是紋理的釋放,我的認爲使用removeUnusedTexture這個函數比較好,它會釋放掉引用計數爲1的紋理,也就說明程序中沒有再爲它增長引用計數,確定是沒用了。那些大於1的紋理不會釋放掉,即便咱們在不知道的狀況下再次加載,也只是返回已經加載好了的。接下來是另外倆個緩存了,和這個意思差很少,直接看代碼吧。
<pre class="brush:cpp; toolbar: true; auto-links: false;">

bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !CCLayer::init() )
    {
        return false;
    }

    CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
    //可使用texturepacker生成.plist文件,將plist文件和png文件放到資源目錄下,經過add方法將ghosts.plist紋理
    //加載到了緩存中
    CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("ghosts.plist");

    //spriteFrameByName中傳入的參數能夠到.plist文件中查看,經過這個方法能夠從緩存中得到一個精靈幀
    CCSpriteFrame * spriteFrame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName("child1.gif");
    CCLog("%d",spriteFrame->retainCount()); //count=1
    //之因此建立精靈幀就是爲了播放動畫作準備的,這裏能夠是一個循環不到的建立精靈幀而後添加到animation中
    //關於這部分的內容能夠查看我前邊的文章,這裏是爲了說明緩存的使用
    CCAnimation * animation = CCAnimation::create();

    animation->addSpriteFrame(spriteFrame);
    CCLog("%d",spriteFrame->retainCount()); //count=2

    CCLog("%d",animation->retainCount()); //count=1,這個1是建立時候的引用計數
    //動畫緩存,將animation放到緩存中,裏邊存入一個key,方便之後取出,若是不把動畫放到這個緩存中,下一幀的時候
    //動畫就會被釋放掉的,再播放動畫就會出錯了
    CCAnimationCache::sharedAnimationCache()->addAnimation(animation,"a"); //count=2
    CCLog("%d",animation->retainCount());

    CCAnimate * animate = CCAnimate::create(CCAnimationCache::sharedAnimationCache()->animationByName("a"));

    //當再也不須要播放動畫的時候從動畫緩存中清除,應該先清除動畫再清除精靈幀
    CCAnimationCache::sharedAnimationCache()->removeAnimationByName("a");

    //將引用計數爲1的精靈幀從緩存中清除
    CCSpriteFrameCache::sharedSpriteFrameCache()->removeUnusedSpriteFrames();
    //釋放掉全部的精靈幀
    //CCSpriteFrameCache::sharedSpriteFrameCache()->removeSpriteFrames();

    return true;
}


</pre>
原文轉載:http://www.zaojiahua.com/optimization.html皂莢花異步

相關文章
相關標籤/搜索