1. 程式人生 > >我的Cocos2d-x學習筆記(七)紋理快取、幀快取、精靈的建立、zOrder

我的Cocos2d-x學習筆記(七)紋理快取、幀快取、精靈的建立、zOrder

一、紋理快取幀快取動畫快取

快取機制:儲存設施有快慢之分,PC機上有硬碟與記憶體之分,計算機軟體執行時候把資源載入到記憶體中執行;手機與PC也是類似的。當手機軟體執行時候,把經常需要用到的資源預先載入到存取速度快的記憶體中,之後訪問該資源將會節省大量時間。

Cocos2d-x為我們提供了三個實現快取的介面,有紋理快取幀快取動畫快取,都是全域性單例

紋理快取(CCTextureCache):

    紋理快取快取載入到記憶體中的紋理資源,紋理是OpenGL中對圖片的叫法,將紋理快取起來方便之後的繪製工作。

    每一個快取的影象的大小,顏色和區域範圍都是可以被修改的。這些資訊都是儲存在記憶體中的,不用在每一次繪製的時候都發送給GPU。

    原理:對加入快取的紋理資源進行一次引用,使其引用計數加一,保持不被清楚,從而使Cocos2d-x的渲染機制可以重複使用同一紋理多次渲染。

   CCTextureCache的繼承關係:class CC_DLL CCTextureCache : public CCObject。

    CCTextureCache.h中部分程式碼如下:

class CC_DLL CCTextureCache : public CCObject
{
protected:
	CCDictionary* m_pTextures;
public:
	/** Returns the shared instance of the cache*/
	static CCTextureCache * sharedTextureCache();
	/** purges the cache. It releases the retained instance.*/
	static void purgeSharedTextureCache();
	/** Returns a Texture2D object given an file image
	* If the file image was not previously loaded, it will create a new CCTexture2D
	*  object and it will return it. It will use the filename as a key.
	* Otherwise it will return a reference of a previously loaded image.
	* Supported image extensions: .png, .bmp, .tiff, .jpeg, .pvr, .gif
	*/
	CCTexture2D* addImage(const char* fileimage);
	/** Reload texture from the image file
	* If the file image hasn't loaded before, load it.
	* Otherwise the texture will be reloaded from the file image.
	* The "filenName" parameter is the related/absolute path of the file image.
	* Return true if the reloading is succeed, otherwise return false.*/
	bool reloadTexture(const char* fileName);
	/** Purges the dictionary of loaded textures.
	* Call this method if you receive the "Memory Warning"
	* In the short term: it will free some resources preventing your app from being killed
	* In the medium term: it will allocate more resources
	* In the long term: it will be the same*/
	void removeAllTextures();
	/** Removes unused textures
	* Textures that have a retain count of 1 will be deleted
	* It is convenient to call this method after when starting a new Scene*/
	void removeUnusedTextures();
	/** Deletes a texture from the cache given a texture*/
	void removeTexture(CCTexture2D* texture);
	/** Deletes a texture from the cache given a its key name*/
	void removeTextureForKey(const char *textureKeyName);
	/** Reload all textures
	It's only useful when the value of CC_ENABLE_CACHE_TEXTURE_DATA is 1
	*/
	static void reloadAllTextures();
};

上面程式碼都有英文註釋,簡單易懂,下面解釋一些常用的。

sharedTextureCache:得到CCTextureCache的單例物件。

purgeSharedTextureCache:清空快取,銷燬CCTextureCache單例物件。

addImage:載入一個圖片生成紋理,返回生成的紋理指標。

removeAllTextures:釋放所有紋理。

removeUnusedTextures:清除未被外部使用的紋理。怎麼知道未使用呢?這裡只需要遍歷下計數器值為1的紋理即未被外部使用的紋理進行釋放即可。 新場景建立好後,使用此方法釋放沒有使用的紋理非常方便。

removeTexture:移除指定的紋理。

再來看看下面程式碼:

bool CCSprite::initWithFile(const char *pszFilename)
{
    CCAssert(pszFilename != NULL, "Invalid filename for sprite");

    CCTexture2D *pTexture = CCTextureCache::sharedTextureCache()->addImage(pszFilename);
    if (pTexture)
    {
        CCRect rect = CCRectZero;
        rect.size = pTexture->getContentSize();
        return initWithTexture(pTexture, rect);
    }

    // don't release here.
    // when load texture failed, it's better to get a "transparent" sprite than a crashed program
    // this->release(); 
    return false;
}
由此可知,當建立一個精靈時候,實際上是通過紋理快取建立的。

精靈幀快取(CCSpriteFrameCache)

    CCSpriteFrameCache也是一個單例物件,使用xml格式檔案(plist檔案)。

    我們可以載入很多的精靈幀到快取中,之後就可以從這個快取中建立精靈物件了。

    精靈幀快取主要用來快取多張小圖合併後的圖片,一張圖中包含多張小圖,通過CCSpriteFrameCache快取後通過CCSpriteFrame引用其中一個小圖片。

    繼承關係:class CC_DLL CCSpriteFrameCache : public CCObject。

    CCSpriteFrameCache.h部分程式碼如下:

class CC_DLL CCSpriteFrameCache : public CCObject
{
public:
	void addSpriteFramesWithFile(const char *pszPlist);
	void addSpriteFramesWithFile(const char* plist, const char* textureFileName);
	void addSpriteFramesWithFile(const char *pszPlist, CCTexture2D *pobTexture);
	void addSpriteFrame(CCSpriteFrame *pobFrame, const char *pszFrameName);
	void removeSpriteFrames(void);
	void removeUnusedSpriteFrames(void);
	void removeSpriteFrameByName(const char *pszName);
	void removeSpriteFramesFromFile(const char* plist);
}

void addSpriteFramesWithFile(const char *pszPlist):從一個.plist檔案新增多個精靈幀。 一個紋理將被自動載入。紋理名稱將把.plist字尾名替換為.png來組成。

void addSpriteFramesWithFile(const char* plist, const char* textureFileName):通過一個.plist檔案新增多個精靈幀。紋理將與被建立的精靈幀結合。

void addSpriteFramesWithFile(const char *pszPlist, CCTexture2D *pobTexture):通過一個.plist檔案新增多個精靈幀。紋理將與被建立的精靈幀結合。

void addSpriteFrame(CCSpriteFrame *pobFrame, const char *pszFrameName):通過給定的名稱新增一個精靈幀。 如果名稱已經存在,那麼原來名稱的內容將被新的所替代。

二、精靈的建立

精靈的建立:

精靈是遊戲中主要的元素,建立的方式有很多種,通過紋理建立,通過精靈幀建立等。

    精靈的繼承關係:class CC_DLL CCSprite : public CCNodeRGBA, public CCTextureProtocol

    精靈的部分程式碼如下:

class CC_DLL CCSprite : public CCNodeRGBA, public CCTextureProtocol
{
public:
	static CCSprite* create();
	static CCSprite* create(const char *pszFileName);
	static CCSprite* create(const char *pszFileName, const CCRect& rect);
	static CCSprite* createWithTexture(CCTexture2D *pTexture);
	static CCSprite* createWithTexture(CCTexture2D *pTexture, const CCRect& rect);
	static CCSprite* createWithSpriteFrame(CCSpriteFrame *pSpriteFrame);
	static CCSprite* createWithSpriteFrameName(const char *pszSpriteFrameName);
	virtual bool initWithTexture(CCTexture2D *pTexture);
	virtual bool initWithTexture(CCTexture2D *pTexture, const CCRect& rect);
	virtual bool initWithTexture(CCTexture2D *pTexture, const CCRect& rect, bool rotated);
	virtual bool initWithSpriteFrame(CCSpriteFrame *pSpriteFrame);
	virtual bool initWithSpriteFrameName(const char *pszSpriteFrameName);
	virtual bool initWithFile(const char *pszFilename);
	virtual bool initWithFile(const char *pszFilename, const CCRect& rect);
}
光看上面程式碼就知道它們是建立和初始化精靈的函式。有許多種方法可以建立一個精靈。

建立精靈方式如下圖所示:

提示:系統預設的圖片路徑是工程下面Resources,如果是其下有更深層次的目錄。需要手動新增。

第一種:利用圖片直接建立精靈

static CCSprite* create(const char *pszFileName);
static CCSprite* create(const char *pszFileName, const CCRect& rect);
通過這兩個create函式可以從圖片直接建立一個精靈出來,pszFileName為圖片的名字,第二個引數可以指定圖片擷取大小,方向為從左上放到右下方擷取。
1:	CCSprite* sprite1 = CCSprite::create("HelloWorld.png");
	addChild(sprite1);
上面例項僅使用一張圖片建立精靈,精靈的預設錨點為(0.5,0.5),加到父節點上預設位置為(0,0)。

利用第二個函式建立精靈先來看看相關的巨集:

#define CCPointMake(x, y) CCPoint((float)(x), (float)(y))
#define CCSizeMake(width, height) CCSize((float)(width), (float)(height))
#define CCRectMake(x, y, width, height) CCRect((float)(x), (float)(y), (float)(width), (float)(height))
CCRect::CCRect(void)
{
	setRect(0.0f, 0.0f, 0.0f, 0.0f);
}

CCRect::CCRect(float x, float y, float width, float height)
{
	setRect(x, y, width, height);
}
由上面程式碼可以知道我們可以用CCRectMake建立一個CCRect。

下面例項:

	CCSize winSize = CCDirector::sharedDirector()->getWinSize();
	CCSprite* sprite2 = CCSprite::create("HelloWorld.png",CCRect(0,0,240,100));
	sprite2->setPosition(ccp(winSize.width/2, winSize.height/2));
	addChild(sprite2);
實際上顯示出HelloWorld.png圖片,左上角擷取的一部分圖片。方向為從左上放到右下方擷取。

第二種:利用紋理來建立精靈

	static CCSprite* createWithTexture(CCTexture2D *pTexture);
	static CCSprite* createWithTexture(CCTexture2D *pTexture, const CCRect& rect);
這兩個函式引數pTexture是一個紋理物件指標,那麼首先就要先建立這個紋理;第二個引數同樣是擷取大小所用,從左上到右下擷取。

CCTexture2D類中沒有create函式,只能用new建立一個CCTexture2D物件,之後通過initWithImage來新增紋理內容。 

CCTexture2D中關於initWithImage函式的程式碼如下:

class CC_DLL CCTexture2D : public CCObject
{
public:
	bool initWithImage(CCImage * uiImage);
}
由此可知道想要建立一個CCTexture2D物件需要CCImage物件指標。

CCImage類:支援從JPG, PNG, TIFF以及資料流,字串中建立供Cocos2d - x進行訪問的圖片資料物件。

看看CCImage中,只列出我們目前關心的內容:

class CC_DLL CCImage : public CCObject
{
public:
	bool initWithImageFile(const char * strPath, EImageFormat imageType = kFmtPng);
}
第一個引數為圖片檔案路徑與名字,第二個引數為型別,指明是從那種資料建立CCImage,使用時使用其預設值就好。

例項一,通過直接建立紋理來建立精靈:

	CCSize winSize = CCDirector::sharedDirector()->getWinSize();
	CCImage* image = new CCImage();
	image->initWithImageFile("HelloWorld.png");

	CCTexture2D* texture = new CCTexture2D;
	texture->autorelease();
	texture->initWithImage(image);
	CCSprite* sprite = CCSprite::createWithTexture(texture);
	sprite->setPosition(ccp(winSize.width/2,winSize.height/2));
	addChild(sprite);

首先建立一個CCImage物件,用來建立CCTexture2D;

因為CCTexture2D沒有新增到渲染樹中,不會自動釋放,所以用autorelease來延遲釋放;

之後利用CCTexture2D來建立CCSprite。

CCTexture2D還有另一種獲取的方法,就是之前介紹的從紋理快取中獲取,利用這種方法需要現在紋理快取中新增圖片。

例項二,通過紋理快取獲取紋理建立精靈:

	CCSize winSize = CCDirector::sharedDirector()->getWinSize();
	CCTexture2D* texture = CCTextureCache::sharedTextureCache()->addImage("HelloWorld.png");
	CCSprite* sprite = CCSprite::createWithTexture(texture);
	sprite->setPosition(ccp(winSize.width/2,winSize.height/2));
	addChild(sprite);

首先建立一個紋理快取,然後把圖片載入到紋理快取中;

從紋理快取中獲取紋理;

之後利用紋理建立精靈。

第三種:利用精靈幀建立精靈

	static CCSprite* createWithSpriteFrame(CCSpriteFrame *pSpriteFrame);
	static CCSprite* createWithSpriteFrameName(const char *pszSpriteFrameName);

上面兩行程式碼雖然相似,但是也有些區別,第一個函式需要精靈幀物件指標作為引數,第二個函式則需要精靈幀的名字做引數。

之前介紹了精靈幀快取,現在再來看看精靈幀,部分程式碼如下:

class CC_DLL CCSpriteFrame : public CCObject
{
public:
	static CCSpriteFrame* create(const char* filename, const CCRect& rect);
	static CCSpriteFrame* create(const char* filename, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize);
	static CCSpriteFrame* createWithTexture(CCTexture2D* pobTexture, const CCRect& rect);
	static CCSpriteFrame* createWithTexture(CCTexture2D* pobTexture, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize);
}
由上面程式碼可以看出,建立精靈幀可以通過圖片檔案直接建立,也可以通過紋理建立精靈幀。精靈幀也可以有精靈幀快取獲得。

例項一,通過圖片直接建立精靈幀建立精靈:

	CCSize winSize = CCDirector::sharedDirector()->getWinSize();
	CCSpriteFrame* spriteFrame = CCSpriteFrame::create("HelloWorld.png", CCRectMake(0, 0, 200, 200));
	CCSprite* sprite = CCSprite::createWithSpriteFrame(spriteFrame);
	sprite->setPosition(ccp(winSize.width/2, winSize.height/2));
	addChild(sprite);
這裡首先通過圖片建立了一個精靈幀;

利用這個精靈幀建立了一個精靈。

例項二,通過精靈幀緩衝取出精靈幀建立精靈:

	CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("sprite/zombie.plist");
	CCSpriteFrame* spriteFrame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName("z_1_attack_01.png");
	CCSprite* sprite = CCSprite::createWithSpriteFrame(spriteFrame);
	sprite->setPosition(ccp(100, 100));
	addChild(sprite);
首先,把plist檔案新增到精靈幀緩衝當中,精靈幀緩衝新增精靈幀的方法在之前介紹精靈幀緩衝時候介紹過;

然後,通過精靈幀緩衝取得我們需要用到的精靈幀,用到了CCSpriteFrameCache中spriteFrameByName函式,

此函式通過小圖片的名字從小圖片集合成的大圖片建立的精靈幀緩衝中取出對應的精靈幀;

最後通過精靈幀建立精靈。

例項三,通過精靈幀緩衝中精靈幀的名字直接建立精靈:

	CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("sprite/zombie.plist");
	CCSprite* sprite = CCSprite::createWithSpriteFrameName("z_1_attack_01.png");
	sprite->setPosition(ccp(100,100));
	addChild(sprite);
首先建立了一個精靈幀緩衝;

建立精靈時候直接通過精靈幀緩衝中精靈幀名字建立。

三、zOrder

zOrder在Cocos2d-x中用來描述節點渲染的順序,CCNode派生的類都有zOrder,預設值是0。

zOrder的值越大,繪製越靠後,以精靈為例子zOrder值大的精靈呈現在zOrder值小的精靈上面。

zOrder在CCNode中相關內容程式碼精簡後如下:

class CC_DLL CCNode : public CCObject
{
protected:
	int m_nZOrder;  //< z-order value that affects the draw order
	CCNode *m_pParent;
public:
	CCNode::CCNode(void) :..., m_nZOrder(0)...
	{
	}
	void setZOrder(int z)
	{
		_setZOrder(z);
		if (m_pParent)
		{
			m_pParent->reorderChild(this, z);
		}
	}
	void reorderChild(CCNode *child, int zOrder)
	{
		child->_setZOrder(zOrder);
	}
	void _setZOrder(int z)
	{
		m_nZOrder = z;
	}
}
由上訴程式碼可以知道,如果zOrder沒有人為指定,在建構函式中初始化為0;

我們可以通過setZOrder來修改zOrder的值。