在前面的章節中,我們介紹了圖片常用的加載方式和相關內存優化。這一節中主要介紹下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));