紋理緩存     : TextureCache &n..."/>
溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

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

發(fā)布時間:2020-06-18 21:43:30 來源:網(wǎng)絡 閱讀:41003 作者:shahdza 欄目:開發(fā)技術(shù)

【嘮叨】

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

        > 紋理緩存     : TextureCache

        > 精靈幀緩存 : SpriteFrameCache

        > 動畫緩存     : AnimationCache

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

    本文對參考文獻的內(nèi)容進行了整理與整合,并加入一些自己的觀點。


【致謝】

    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、概述

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

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

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

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


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

//
	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、紋理的加載與獲取

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

    > addImage             :函數(shù)會返回一個紋理Texture2D的引用,可能是新加載到內(nèi)存的,也可能是之前已經(jīng)存在的。

    > 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方法添加一個回調(diào)方法,這樣,當紋理異步加載結(jié)束的時候,可以得到通知。

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

    你可以選擇異步加載方式,這樣你就可以為loading場景增加一個進度條。

    關(guān)鍵代碼如下:

//
	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
		// 加載完紋理后,會執(zhí)行回調(diào)函數(shù)
		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));
		....
	}
	
	// 異步加載的回調(diào)函數(shù)
	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、清理緩存

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

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

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




【SpriteFrameCache】


1、概述

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

    SpriteFrameCache 內(nèi)部封裝了一個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這些小圖。
	// 參數(shù)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”, 請調(diào)用這個方法。
    // 就眼前來說 : 它將釋放一些資源來阻止你的應用崩潰掉。
    // 中期的角度 : 它將分配更多的資源。
    // 從長遠來說 : 它將變成相同的。
    SpriteFrameCache::getInstance()->removeSpriteFrames();
    
    // 從一個.plist文件移除多個精靈幀。即:存儲在這個文件的精靈幀將被刪除。
    // 當某個特定的紋理需要被刪除時候調(diào)用這個方法很方便。
    SpriteFrameCache::getInstance()->removeSpriteFramesFromFile(const std::string &plist);
    
    // 移除與特定的紋理結(jié)合的所有的精靈幀。
    // 當某個特定的紋理需要被刪除時候調(diào)用這個方法很方便。
    SpriteFrameCache::getInstance()->removeSpriteFramesFromTexture(cocos2d::Texture2D *texture);
    
    // 移除沒用的精靈幀。保留數(shù)為1的精靈幀將被刪除。
    // 在開始一個新的場景之后調(diào)用這個方法很方便。
    SpriteFrameCache::getInstance()->removeUnusedSpriteFrames();
//


5、SpriteFrameCache  VS.  TextureCache

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

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

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

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

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

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




【AnimationCache】


1、概述

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

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


2、相關(guān)函數(shù)

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

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

    其相關(guān)函數(shù)如下:

//
	// 添加一個動畫到緩存,命名為name。
	// name - animation : 是一組 鍵-值對(key-value) 的關(guān)系。
	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。

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



向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI