Cocos2dx遊戲教程(十八):三種緩存,TextureCache,SpriteFrameCache,AnimationCache

在前面的章節中,我們介紹了圖片常用的加載方式和相關內存優化。這一節中主要介紹下cocos2dx的緩存。

首先致敬大神
http://www.cocoachina.com/bbs/read.php?tid-200714.html (紋理緩存TextureCache)
http://www.cocoachina.com/bbs/read.php?tid-200359.html (精靈幀緩存SpriteFrameCache)
http://www.cocoachina.com/bbs/read.php?tid-201628.html (動畫緩存AnimationCache)

一、爲什麼使用緩存

在前面提到,在遊戲中需要加載大量的紋理圖片,這些操作都是很耗內存和資源的, 當遊戲中有個界面用到的圖片非常多,第一次點進這界面時速度非常慢(因爲要加載繪製很多圖片)出現卡頓的現象,第二次進入這個界面就非常快了,這是爲什麼呢?

下面就帶着這個問題來一起看一下吧

首先,我們通常使用如下方式創建一個精靈Sprite

auto sp = Sprite::create("sprite.png");
sp->setPosition(Vec2::ZERO);
this->addchild(sp);

沒錯,又是這個(出現了好多次)

我們可以通過源代碼瞭解到,我們創建精靈後,將精靈加入了緩存

Sprite* Sprite::create(const std::string& filename)
{    
    Sprite *sprite = new (std::nothrow) Sprite();
    if (sprite && sprite->initWithFile(filename))    
    {        
         sprite->autorelease();        
         return sprite;    
    }    
    CC_SAFE_DELETE(sprite);    
    return nullptr;
}

我們可以看到,在創建的時候調用了

sprite->initWithFile(filename)) 

那麼下面來看一下 initWithFile 這個方法

bool Sprite::initWithFile(const std::string& filename)
{
    if (filename.empty())
    {
        CCLOG("Call Sprite::initWithFile with blank resource filename.");
        return false;
    }   

    _fileName = filename;
    _fileType = 0;

    Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(filename);
    
    if (texture)
    {
        Rect rect = Rect::ZERO;
        rect.size = texture->getContentSize();
        return initWithTexture(texture, rect);
    }
    
    return false;
}

注意這一行,獲取了一個緩存加載了該圖並返回一個紋理

Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(filename);

Texture2D: 紋理,即圖片加載入內存後供CPU和GPU操作的貼圖對象。
TextureCache(紋理緩存),用於加載和管理紋理。一旦紋理加載完成,下次使用時可使用它返回之前加載的紋理,從而減少對GPU和CPU內存的佔用。

現在大家是不是對第二次加載速度變快有所瞭解了呢。

二、三種緩存

Cocos引擎主要有三種緩存類:紋理緩存:TextureCache,幀緩存:SpriteFrameCache,動畫緩存:AnimationCache

這3個緩存類分別維護各自的map容器對象,對於容器,下一張將會單獨介紹哦,現在先簡單看一下:

TextureCache

std::unordered_map<std::string, Texture2D*> _textures;

SpriteFrameCache

Map<std::string, SpriteFrame*> _spriteFrames;ValueMap _spriteFramesAliases;std::set<std::string>*  _loadedFileNames;

AnimationCache

 Map<std::string, Animation*> _animations;

1、紋理緩存:TextureCache

上面大家已經初步瞭解了Sprite加載,如果文件名以前沒有被加載時,它會創建一個新的Texture2D 對象,它會返回它。它將使用文件名作爲key否則,它會返回一個引用先前加載的圖像。

TextureCache屏蔽了加載紋理的許多細節;
addImage函數會返回一個紋理Texture2D的引用,可能是新加載到內存的,也可能是之前已經存在的;

Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(filename);

也可以通過getTextureForKey方法來獲得這個key所對應的紋理緩存,如果這個Key對應的紋理不存在,那麼就返回NULL

Texture2D *texture = Director::getInstance()->getTextureCache()->getTextureForKey(textureKeyName);

TextureCache類還支持異步加載資源的功能,利用addImageAsync方法。
可以在進度條或者跳轉界面預先將資源加載好哦,那麼在後面需要使用的時候就不會卡頓啦。

有加載當然有釋放,removeUnusedTextures則會釋放當前所有引用計數爲1的紋理,即目前沒有被使用的紋理。

Director::getInstance()->getTextureCache()->removeUnusedTextures();

指定釋放

Director::getInstance()->getTextureCache()->removeTextureForKey("sp.png");

全部釋放

Director::getInstance()->getTextureCache()->removeAllTextures();

2、幀緩存:SpriteFrameCache

SpriteFrameCache 主要服務於多張碎圖合併出來的紋理圖片。這種紋理在一張大圖中包含了多張小圖,直接通過TextureCache引用會有諸多不便,因而衍生出來精靈框幀的處理方式,即把截取好的紋理信息保存在一個精靈框幀內,精靈通過切換不同的框幀來顯示出不同的圖案。

SpriteFrameCache的常用接口和TextureCache類似,唯一需要注意的是添加精靈幀的配套文件一個plist文件和一張大的紋理圖。TexturePacker工具是合成大圖的良好選擇哈。

使用方法

SpriteFrameCache *frameCache = SpriteFrameCache::getInstance();
frameCache->addSpriteFramesWithFile("boy.plist","boy.png");
auto frame_sp = Sprite::createWithSpriteFrameName("boy1.png");
this->addChild(frame_sp,2);

3、動畫緩存:AnimationCache

Cocos2d-x中,動畫的具體內容是依靠精靈顯示出來的,爲了顯示動態圖片,我們需要不停切換精靈顯示的內容,通過把靜態的精靈變爲動畫播放器從而實現動畫效果。動畫由幀組成,每一幀都是一個紋理,我們可以使用一個紋理序列來創建動畫。

我們使用Animation類描述一個動畫,而精靈顯示動畫的動作則是一個Animate對象。動畫動作Animate是精靈顯示動畫的動作,它由一個動畫對象創建,並由精靈執行。

有兩種創建方法:

(1)手動添加序列幀到Animation類

手動添加的方法需要將每一幀要顯示的精靈有序添加到Animation類中,並設置每幀的播放時間,讓動畫能夠勻速播放。另外,還要通過setRestoreOriginalFrame來設置是否在動畫播放結束後恢復到第一幀。創建好Animation實例後,需要創建一個Animate實例來播放序列幀動畫。

auto animation = Animation::create();    
for( int i=1;i<15;i++)    {        
    char szName[100] = {0};        
    sprintf(szName, "sp_%02d.png", i);      
    animation ->addSpriteFrameWithFile(szName);
}      
animation->setDelayPerUnit(2.8f / 14.0f); 
animation->setRestoreOriginalFrame(true);
auto action = Animate::create(animation);    
_animation->runAction(Sequence::create(action, action->reverse(), NULL));

(2)使用文件初始化Animation類

AnimationCache可以加載xml/plist文件,plist文件裏保存了組成動畫的相關信息,通過該類獲取到plist文件裏的動畫。

使用文件添加的方法只需將創建好的plist文件添加到動畫緩存裏面,plist文件裏包含了序列幀的相關信息。再用動畫緩存初始化Animation實例,用Animate實例來播放序列幀動畫。

auto cache = AnimationCache::getInstance();    
cache->addAnimationsWithFile("animations/animations.plist");   
auto animation2 = cache->getAnimation("dance_1");    
auto action2 = Animate::create(animation2);   
_animation->runAction(Sequence::create(action2, action2->reverse(), NULL));
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章