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