紋理緩存
概述
在遊戲中需要加載大量的紋理圖檔,這些操作都是很耗記憶體和資源的。
當遊戲中有個界面用到的圖檔非常多,第一次點進這界面時速度非常慢(因為要加載繪制很多圖檔)出現卡頓,我們可以使用TextureCache提前異步加載紋理,等加載結束,進入到這個界面再使用這些圖檔速度就會非常快。
Texture2D: 紋理,即圖檔加載入記憶體後供CPU和GPU操作的貼圖對象。
TextureCache(紋理緩存),用于加載和管理紋理。一旦紋理加載完成,下次使用時可使用它傳回之前加載的紋理,進而減少對GPU和CPU記憶體的占用。
常用的方法
當你建立一個精靈,你一般會使用Sprite::create(pszFileName)。假如你去看Sprite::create(pszFileName)的實作方式,你将看到它将這個圖檔增加到紋理緩存中去了,
Sprite* Sprite::create(const std::string& filename)
{
Sprite *sprite = new Sprite();
if (sprite && sprite->initWithFile(filename))
{
sprite->autorelease();
return sprite;
}
_SAFE_DELETE(sprite);
return nullptr;
}
bool Sprite::initWithFile(const std::string& filename)
{
ASSERT(filename.size()>0, "Invalid filename for sprite");
Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(filename);
if (texture)
{
Rect rect = Rect::ZERO;
rect.size = texture->getContentSize();
return initWithTexture(texture, rect);
}
// don't release here.
// when load texture failed, it's better to get a "transparent" sprite then a crashed program
// this->release();
return false;
}
上面代碼顯示在控制加載紋理。一旦這個紋理被加載了,在下一時刻就會傳回之前加載的紋理引用,并且減少加載的時候瞬間增加的記憶體。(詳細API請看TextureCache API)
擷取TextureCache
在3.0版本中,TextureCache不再作為單例模式使用。作為Director的成員變量,通過以下方式擷取
Director::getInstance()->getTextureCache();
擷取紋理
如果檔案名以前沒有被加載時,它會建立一個新的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方法。你可以很方面地給addImageAsync方法添加一個回調方法,這樣,當紋理異步加載結束的時候,可以得到通知。
你可以選擇異步加載方式,這樣你就可以為loading場景增加一個進度條。關鍵代碼如下:
TextureCacheTest::TextureCacheTest()
: _numberOfSprites(20)
, _numberOfLoadedSprites(0)
{
auto size = Director::getInstance()->getWinSize();
_labelLoading = Label::createWithTTF("loading...", "fonts/arial.ttf", 15);
_labelPercent = Label::createWithTTF("%0", "fonts/arial.ttf", 15);
_labelLoading->setPosition(Point(size.width / 2, size.height / 2 - 20));
_labelPercent->setPosition(Point(size.width / 2, size.height / 2 + 20));
this->addChild(_labelLoading);
this->addChild(_labelPercent);
// load textrues
Director::getInstance()->getTextureCache()->addImageAsync("Images/HelloWorld.png", _CALLBACK_1(TextureCacheTest::loadingCallBack, this));
Director::getInstance()->getTextureCache()->addImageAsync("Images/grossini.png", _CALLBACK_1(TextureCacheTest::loadingCallBack, this));
Director::getInstance()->getTextureCache()->addImageAsync("Images/grossini_dance_01.png", _CALLBACK_1(TextureCacheTest::loadingCallBack, this));
....
}
void TextureCacheTest::loadingCallBack(cocos2d::Texture2D *texture)
{
++_numberOfLoadedSprites;
char tmp[10];
sprintf(tmp,"%%%d", (int)(((float)_numberOfLoadedSprites / _numberOfSprites) * 100));
_labelPercent->setString(tmp);
if (_numberOfLoadedSprites == _numberOfSprites)
{
this->removeChild(_labelLoading, true);
this->removeChild(_labelPercent, true);
addSprite();
}
}
清理緩存
removeUnusedTextures則會釋放目前所有引用計數為1的紋理,即目前沒有被使用的紋理。比如新場景建立好後,使用此方法釋放沒有使用的紋理非常友善。
Director::getInstance()->getTextureCache()->removeUnusedTextures();
當沒有其它對象(比如sprite)持有紋理的引用的時候,紋理仍然會存在記憶體之間。基于這一點,我們可以立馬從緩存中移除出去,這樣,當紋理不存需要的時候,馬上就會從記憶體中釋放掉。如下代碼所示:
Director::getInstance()->getTextureCache()->removeTextureForKey("Images/grossinis_sister2.png");
當收到"Memory Warning"時,可以調用removeAllTextures()方法。在短期内: 它還将釋放一些資源,防止您的應用程式被殺害; 中期: 它将配置設定更多的資源;從長遠來看:它會是相同的。
Director::getInstance()->getTextureCache()->removeAllTextures();
來源網址:https://github.com/chukong/cocos-docs/blob/master/manual/framework/native/v3/texture-cache/zh.md