cocos2dx[3.2](22)——三種緩存類

【嘮叨】

    Cocos引擎主要有三種緩存類:

        > 紋理緩存     : TextureCache

        > 精靈幀緩存 : SpriteFrameCache

        > 動畫緩存     : AnimationCache

    緩存的目的就是:先將所需資源(如紋理圖片)加載到內存中,之後再次使用該資源的時候,就可以直接從內存中取出,而不需要重新加載。從而減少了CPU和GPU的內存佔用。

    本文對參考文獻的內容進行了整理與整合,並加入一些自己的觀點。


【致謝】

    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)

    http://blog.csdn.net/star530/article/details/23612487 (三種緩存類介紹)




【TextureCache】


1、概述

    在遊戲中需要加載大量的紋理圖片,這些操作都是很耗內存和資源的。

    當遊戲中有個界面用到的圖片非常多,第一次點進這界面時速度非常慢(因爲要加載繪製很多圖片)出現卡頓,我們可以使用TextureCache提前異步加載紋理,等加載結束,進入到這個界面再使用這些圖片速度就會非常快。

    > Texture2D(紋理)                :即圖片加載入內存後供CPU和GPU操作的貼圖對象。  

    > TextureCache(紋理緩存):用於加載和管理紋理。一旦紋理加載完成,下次使用時可使用它返回之前加載的紋理,從而減少對GPU和CPU內存的佔用。


    當你創建一個精靈Sprite,你一般會使用Sprite::create(fileName)。假如你去看Sprite::create(fileName)的實現方式,你將看到它將這個圖片增加到紋理緩存中去了。

//
	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");
		// 加載filename的紋理圖片
		Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(filename);
		if (texture)
		{
			Rect rect = Rect::ZERO;
			rect.size = texture->getContentSize();
			return initWithTexture(texture, rect);
		}
		return false;
	}
//


2、獲取TextureCache

    在3.x版本中,TextureCache不再作爲單例模式使用。而是作爲Director的成員變量,通過以下方式獲取。

//
	// 獲取紋理緩存類 TextureCache
	Director::getInstance()->getTextureCache();
//


3、紋理的加載與獲取

    如果文件名以前沒有被加載時,它會創建一個新的 Texture2D 對象,它會返回它。它將使用文件名作爲key否則,它會返回一個引用先前加載的圖像。

    > addImage             :函數會返回一個紋理Texture2D的引用,可能是新加載到內存的,也可能是之前已經存在的。

    > getTextureForKey :獲得這個key所對應的紋理緩存,若這個Key對應的紋理不存在,那麼就返回nullptr。

    > 支持的圖片格式有: .png,.bmp,.tiff, .jpeg, .pvr 。

//
	// addImage 加載紋理圖片
	// 支持圖片格式: .png, .bmp, .tiff, .jpeg, .pvr
	Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(filename);
	
	// getTextureForKey 獲取紋理圖片
	Texture2D *texture = Director::getInstance()->getTextureCache()->getTextureForKey(textureKeyName);
//


4、異步加載紋理

    TextureCache類還支持異步加載資源的功能,利用 addImageAsync 方法。你可以很方面地給addImageAsync方法添加一個回調方法,這樣,當紋理異步加載結束的時候,可以得到通知。

    > 支持的圖片格式有:.png, .jpg 。

    你可以選擇異步加載方式,這樣你就可以爲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);
		
		// 異步加載紋理圖片 addImageAsync
		// 加載完紋理後,會執行回調函數
		Director::getInstance()->getTextureCache()->addImageAsync("Images/HelloWorld.png", CC_CALLBACK_1(TextureCacheTest::loadingCallBack, this));
		Director::getInstance()->getTextureCache()->addImageAsync("Images/grossini.png", CC_CALLBACK_1(TextureCacheTest::loadingCallBack, this));
		Director::getInstance()->getTextureCache()->addImageAsync("Images/CloseNormal.png", CC_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();
		}
	}
//


5、清理緩存

//
	// 釋放當前所有引用計數爲1的紋理,即目前沒有被使用的紋理。
	// 比如新場景創建好後,使用此方法釋放沒有使用的紋理非常方便。
	Director::getInstance()->getTextureCache()->removeUnusedTextures();

	// 通過給定的紋理貼圖(texture)的關鍵名(key name)從緩存中刪除該紋理貼圖
	Director::getInstance()->getTextureCache()->removeTextureForKey("Images/grossinis.png");

	// 清除加載紋理貼圖的記錄,如果你收到“內存警告”,請調用該方法。 
	// 在短期內 : 會釋放一些資源文件來防止你的app出現閃退現象 
	// 在中期內 : 會分配更多資源 
	// 長遠來看 : 沒有區別
	Director::getInstance()->getTextureCache()->removeAllTextures();
//




【SpriteFrameCache】


1、概述

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

    SpriteFrameCache 內部封裝了一個Map<std::string, SpriteFrame*> _spriteFrames對象。(其中key爲幀的名稱)

    SpriteFrameCache一般用來 處理 plist文件 (這個文件指定了每個獨立的精靈在這張“大圖”裏面的位置和大小),該文件對應一張包含多個精靈的大圖,plist文件可以使用TexturePacker製作。

    SpriteFrameCache的常用接口和TextureCache類似,不再贅述了,唯一需要注意的是添加精靈幀的配套文件一個plist文件和一張大的紋理圖。


2、獲取與銷燬SpriteFrameCache

    SpriteFrameCache是一個單例對象,所以獲取方法與Director一樣。

//
	// 獲取單例對象
	SpriteFrameCache* cache = SpriteFrameCache::getInstance(); 

	// 銷燬單例對象
	SpriteFrameCache::destroyInstance();
//


3、精靈幀的加載與使用

     通過 addSpriteFramesWithFile 加載 plist文件,將plist文件中的多張小圖加載到精靈幀緩存中。

//
	// boy.png 裏集合了boy1.png,boy2.png這些小圖。
	// 參數2 可不寫
	SpriteFrameCache *frameCache = SpriteFrameCache::getInstance();	
	frameCache->addSpriteFramesWithFile("boy.plist", "boy.png");    

	//從SpriteFrameCache緩存中找到boy1.png這張圖片.
	auto frame_sp = Sprite::createWithSpriteFrameName("boy1.png"); 
	this->addChild(frame_sp, 2);
//


4、清理緩存

//
    // 從精靈幀緩存中刪除一個精靈幀.
    SpriteFrameCache::getInstance()->removeSpriteFrameByName(const std::string &name);
    
    // 清除載入精靈幀的字典。如果接收到“Memory Warning”, 請調用這個方法。
    // 就眼前來說 : 它將釋放一些資源來阻止你的應用崩潰掉。
    // 中期的角度 : 它將分配更多的資源。
    // 從長遠來說 : 它將變成相同的。
    SpriteFrameCache::getInstance()->removeSpriteFrames();
    
    // 從一個.plist文件移除多個精靈幀。即:存儲在這個文件的精靈幀將被刪除。
    // 當某個特定的紋理需要被刪除時候調用這個方法很方便。
    SpriteFrameCache::getInstance()->removeSpriteFramesFromFile(const std::string &plist);
    
    // 移除與特定的紋理結合的所有的精靈幀。
    // 當某個特定的紋理需要被刪除時候調用這個方法很方便。
    SpriteFrameCache::getInstance()->removeSpriteFramesFromTexture(cocos2d::Texture2D *texture);
    
    // 移除沒用的精靈幀。保留數爲1的精靈幀將被刪除。
    // 在開始一個新的場景之後調用這個方法很方便。
    SpriteFrameCache::getInstance()->removeUnusedSpriteFrames();
//


5、SpriteFrameCache  VS.  TextureCache

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

    跟TextureCache功能一樣,將SpriteFrame緩存起來,在下次使用的時候直接去取。

    不過跟TextureCache不同的是:如果內存池中不存在要查找的圖片,它會提示找不到,而不會去本地加載圖片。

    > TextureCache時最底層也是最有效的紋理緩存,緩存的是加載到內存中的紋理資源,也就是圖片資源。

    > SpriteFrameCache精靈框幀緩存,緩存的時精靈幀。

    > SpriteFrameCache是基於TextureCache上的封裝。緩存的是精靈幀,是紋理指定區域的矩形塊。各精靈幀都在同一紋理中,通過切換不同的框幀來顯示出不同的圖案。




【AnimationCache】


1、概述

    通常情況下,對於一個精靈動畫,每次創建時都需要加載精靈幀,然後按順序添加到數組,再用Animation讀取數組創建動畫。這是一個非常煩瑣的計算過程。而對於使用頻率高的動畫,例如角色的走動、跳舞等,可以將其加入到AnimationCache中,每次使用都從這個緩存中調用,這樣可以有效的降低創建動畫的巨大消耗。

    所以將創建好的動畫Animation直接放在動畫緩存AnimationCache中,當需要執行動畫動作時,就直接從動畫緩存中拿出來,去創建初始化Animation會非常方便。


2、相關函數

    AnimationCache是一個單例對象,所以獲取方法與Director一樣。

    通過 AnimationCache::getInstance() 獲取單例對象。

    其相關函數如下:

//
	// 添加一個動畫到緩存,命名爲name。
	// name - animation : 是一組 鍵-值對(key-value) 的關係。
	void addAnimation(Animation *animation, const std::string& name);

	// 添加動畫的plist文件到緩存
	void addAnimationsWithFile(const std::string& plist);

	// 獲得指定名稱爲name的動畫
	Animation* getAnimation(const std::string& name);

	// 移除一個指定的動畫 
	void removeAnimation(const std::string& name);
//


3、使用舉例

//
//
    //動畫命名爲 Explosion,加入到動畫緩存中
    Animation* animation = Animation::createWithSpriteFrames(arr, 0.04);
    AnimationCache::getInstance()->addAnimation(animation, "Explosion");

    //直接從動畫緩存中取出 "Explosion" 動畫
    Animation* animation = AnimationCache::getInstance()->getAnimation("Explosion");
//
//




【清理順序】

    值得注意的是清理的順序。

    我們推薦 清理順序 如下:

        > 首先清理,動畫緩存AnimationCache,

        > 然後清理,精靈幀緩存SpriteFrameCache,

        > 最後清理,紋理緩存TextureCache。

    按照引用層級由高到低,以保證釋放引用有效。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章