本章我們將深入了解精靈(Sprite)。我們可以通過很多方式用單個文件或者紋理貼圖集(Texture Atlases)來生成精靈。我也會在本章介紹如何創建和播放精靈動畫。
紋理貼圖集是一張包含很多圖片的紋理貼圖(圖片),通常用于存放單個角色動畫的所有動畫幀。不過它的作用不止于此。實際上你可以把任何圖片放進同一張紋理貼圖中。我們的目的是把盡可能多的圖片放進同一張紋理貼圖中,以達到節省空間的目的。 我主要用TexturePackerGUI來生成紋理貼圖集
下載地址:TexturePackerGUI
“精靈批處理”(Sprite Batching)是用于提高精靈渲染速度的技術。它可以提高渲染大量相同精靈的速度,不過它同紋理貼圖集配合使的效率最高。如果你將紋理貼圖集與精靈批處理配合使用的話,你只要調用一次渲染方法就可以完成紋理貼圖集里所有圖片的渲染。 “渲染方法調用”(draw call)是把必要的信息傳遞給圖形處理硬件以完成整個或者部份圖片渲染的過程。當你使用CCSprite的時候,每生成一個CCSprite節點都會調用一次渲染方法。每一次渲染方法調用導致的系統開銷疊加起來會使游戲的幀率大約降低15%或者更多(除非你使用CCSpriteBatchNode,它的作用是作為一個額外的層用于添加多個精靈節點。前提是這些精靈節點使用的是同一個紋理貼圖)。 |
CCSpriteBatchNode
每次系統在屏幕上渲染一張貼圖時,圖形處理硬件必須首先準備渲染,然后渲染圖形,最后在完成渲染以后進行清理。上述過程是每一次在啟動渲染和結束渲染之間存在的固定系統開銷。如果圖形處理硬件知道你需要使用同一張紋理貼圖渲染一組精靈的話,圖形處理硬件將只需要為這一組精靈執行一次準備,渲染,最后清理的過程了。
你可以看到屏幕上有幾百個相同的子彈。如果你逐個渲染它們的話,你的游戲幀率將會下降至少15%。如果你使用CCSpriteBatchNode,你就可以避免幀率的下降。 |
把使用同一張紋理貼圖的一組CCSprite節點添加到同一個CCSpriteBatchNode里,比逐個渲染CCSprite要高效很多。
以下是生成CCSprite的常用代碼:
- CCSprite *sprite=CCSprite::create("sprite.png");
- this->addChild(sprite);
當然,向CCSpriteBatchNode添加一個CCSprite節點不會有任何效率上的提高。
接下來,我把許多使用同一張紋理貼圖的精靈節點添加到了同一個CCSpriteBatchNode里
- CCSpriteBatchNode *node=CCSpriteBatchNode::create("s1.png");
- this->addChild(node);
-
- for(int i=0;i<100;i++){
- CCSprite *sprite=CCSprite::create("s1.png");
- node->addChild(sprite);
- }
CCSpriteBatchNode的作用很像CCLayer,因為它本身并不顯示在屏幕上。不過你只能把CCSprite加入CCSpriteBatchNode。CSpriteBatchNode將一個圖片文件名作為參數,使用這個參數的原因是所有被添加進CCSpriteBatchNode的CCSprite節點都必須使用同一個圖片文件。如果你沒有在CCSprite中使用相同的圖片,你將會在調試窗口中得到報錯信息。
----------------------------------------------------
什么時候應該使用CCSpriteBatchNode?
當你需要顯示兩個或者更多個相同的CCSprite節點時,你可以使用CCSpriteBatchNode。組合在一起的CCSprite節點越多,使用CCSpriteBatchNode得到的效果提升就越大。 不過也有一些限制。因為所有的CCSprite節點都添加到同一個CCSpriteBatchNode中,所以所有CCSprite節點都會使用相同的z-order(深度)來渲染. 另一個缺點是所有添加到同一個CCSpriteBatchNode中的CCSprite節點必須使用同一個紋理貼圖。不過那也意味著CCSpriteBatchNode非常適合使用紋理貼圖集。使用紋理貼圖集的時候,你不僅僅局限于渲染一張圖片;你可以把不同的圖片放到同一個紋理貼圖集中,然后利用CCSpriteBatchNode將所有圖片渲染出來,以提高渲染速度。 如果把紋理貼圖集和CCSpriteBatchNode配合使用的話,之前討論的z-order渲染問題就變得不那么明顯了,因為你可以在單個CCSprite節點里指定不同的zorder值. |
你可以把CCSpriteBatchNode看成CCLayer,唯一的區別是:CCSpriteBatchNode只接受使用同一張紋理貼圖的CCSprite節點。有了這樣的認識以后,我相信你會發現很多可以用到CCSpriteBatchNode的地方。
不使用CCSpriteBatchNode運行精靈動畫
接下去我們討論如何運行精靈動畫(Sprite Animation)。因為你可以將所有的動畫幀放在同一張紋理貼圖集里以節省內存,所以這是另一個使用CCSpriteBatchNode的好地方。 |
不使用紋理貼圖集將動畫添加到Ship類中需要不少的代碼
- bool MainScene::init() {
- bool bRet = false;
- do {
- // 1. 生成一個CCArray數組。
- // 2. 循環處理每一個動畫幀:
- // a. 為每一個圖片生成一個CCTexture2D節點
- // b. 利用CCTexture2D節點生成一個CCSpriteFrame
- // c. 把生成的CCSpriteFrame添加到CCArray中
- // 3. 利用CCArray中的動畫幀生成一個CCAnimation節點
- // 4. (可選)把CCAnimation添加到一個CCSprite中
- // 5. 使用CCAnimate動作播放動畫
-
- CCSprite *pSprite = CCSprite::create("q0.png");
- this->addChild(pSprite);
- pSprite->setPosition(CCPointMake(100,100));
-
- CCArray *array = CCArray::createWithCapacity(5);
- for (int i = 0; i < 5; i++) {
- CCString *filename = CCString::createWithFormat("q%i.png", i);
- CCTexture2D *tt2d = CCTextureCache::sharedTextureCache()->addImage(
- filename->getCString());
-
- CCSize size = tt2d->getContentSize();
- CCLog("size.width=%f,size.height=%f",size.width,size.height);
- CCRect rect = CCRectMake(0,0,size.width,size.height);
-
- CCSpriteFrame *frame = CCSpriteFrame::createWithTexture(tt2d, rect);
- array->addObject(frame);
- }
- CCAnimation *tion = CCAnimation::createWithSpriteFrames(array, 0.08f);
- CCAnimate *mate = CCAnimate::create(tion);
- CCRepeatForever *repeat = CCRepeatForever::create(mate);
- pSprite->runAction(repeat);
-
- bRet = true;
- } while (0);
- return bRet;
- }
代碼沒啥好講解的 邏輯順序都在注釋中寫出
在cocos2d中使用紋理貼圖集
“紋理貼圖集”可以幫助你節約寶貴的內存資源,也可以幫助提高精靈的渲染速度。因為一張紋理貼圖集其實就是一張大圖片,你可以使用CCSpriteBatchNode一次性渲染所有它所包含的圖片,從而降低內存使用量和提高渲染效率。 |
- bool MainScene::init() {
- bool bRet = false;
- do {
-
- CCSprite *pSprite = CCSprite::create();
- this->addChild(pSprite);
- pSprite->setPosition(CCPointMake(100,100));
-
- // 加載紋理貼圖集的精靈幀(sprite frame);同時加載相同名字的貼圖
- CCSpriteFrameCache *framecache =
- CCSpriteFrameCache::sharedSpriteFrameCache();
- framecache->addSpriteFramesWithFile("anim.plist");
-
- framecache->spriteFrameByName("q0.png");
- CCArray *array=CCArray::createWithCapacity(5);
- // 加載飛船的動畫幀
- for (int i = 0; i < 5; i++) {
- CCString *string = CCString::createWithFormat("q%i.png", i);
- CCSpriteFrame *spriteframe=framecache->spriteFrameByName(string->getCString());
- array->addObject(spriteframe);
- }
- // 使用所有生成的精靈動畫幀創建一個動畫對象
- CCAnimation *tion=CCAnimation::createWithSpriteFrames(array,0.5f);
- // 通過使用CCAnimate動作播放動畫
- CCAnimate *mate=CCAnimate::create(tion);
- CCRepeatForever *repeat=CCRepeatForever::create(mate);
- pSprite->runAction(repeat);
- bRet = true;
-
- } while (0);
- return bRet;
- }
因為cocos2d會自動生成所有圖片的緩存,所以你需要一個方法來卸載不再需要的貼圖內存。大多數情況下,你可以依賴cocos2d來幫你卸載:
- CCSpriteFrameCache::sharedSpriteFrameCache()->removeUnusedSpriteFrames();
- CCTextureCache::sharedTextureCache()->removeUnusedTextures();
如果你想在加載新場景之前完全刪除所有內存中的貼圖,你應該使用以下方法
- CCSpriteFrameCache::purgeSharedSpriteFrameCache();
- CCTextureCache::purgeSharedTextureCache();