CCSpriteBatchNode(附:CCMenu與CCSpriteBatchNode的問題)


一般游戲圖片資源會打包成一張大圖,這樣節省空間,又提升速度。打包工具有Zwoptex和texturepacker等等。

   CCSpriteBatchNode的初始化只要一張圖片,也就是那張大圖。然后把所有用到那張大圖里面的小圖的sprite都加到 CCSpriteBatchNode的child,繪制效率就會提高。

1) 緩沖sprite幀和紋理

        // 從紋理貼圖集中預加載精靈幀,這個方法做了以下幾件事:

  • 尋找工程目錄下面和輸入的參數名字一樣,但是后綴是.png的圖片文件。然后把這個文件加入到共享的CCTextureCache中。
  • 解析plist文件,追蹤所有的sprite在spritesheet中的位置,內部使用CCSpriteFrame對象來追蹤這些信息。


        [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"table.plist"];

2) 創建一個精靈批處理結點

        // 為所有 靜態剛體 設置的批處理精靈節點
        CCSpriteBatchNode* batch = [CCSpriteBatchNode batchNodeWithFile:@"table.png"];
        [self addChild:batch];

  • 創建一個CCSpriteBatchNode對象,通過傳遞一個包含所有sprite的batch的名字作為參數,並把它加入到當前場景之中。
  • 接下來,你從batch中創建的任何sprite,你應該把它當作CCSpriteBatchNode的一個孩子加進去。只要sprite包含在batch中,那么就沒問題,否則會出錯。
  •         CCSprite* tableTop = [CCSprite spriteWithSpriteFrameName:@"tabletop.png"];
            tableTop.position = [Helper screenCenter];
            [batch addChild:tableTop];

  • CCSpriteBatchNode可以智能地遍歷它的所有的孩子結點,並通過一次OpenGL ES call來渲染這些孩子,而不是以前每個sprite都需要一個OpenGL call,這樣渲染速度就會更快。

   -(CCSpriteBatchNode*) getSpriteBatch
   {
       return (CCSpriteBatchNode*)[self getChildByTag:kTagBatchNode];
   }

      CCSpriteBatchNode* batch = [[PinballTable sharedTable] getSpriteBatch];
      sprite = [CCSprite spriteWithSpriteFrameName:spriteFrameName];
      [batch addChild:sprite];

//Batch是一個CCSpriteBatchNode,下面Batch-》Sprite-》ChildrenSprite,

//它們的Tag分別為:TagofBatch,TagofSprite,TagofChildrenSprite 得到ChildSprite的方法如下,

根據各自的Tag得到相應的數據。

id Batch = [self getChildByTag:TagofBatch];

id Sprite = [Batch getChildByTag:TagofSprite];

CCSprite *ChildrenSprite = (CCSprite*) [Sprite getChildByTag:TagofChildrenSprite];

 

關於CCSpriteBatchNode,一個CCSpriteBatchNode是一種效率比較高的渲染精靈的方式。比如,你把CCSprite加到CCLayer中,那么sprite的draw函數在 每一幀調用時都會執行7個opengl 調用來完成sprite的渲染。一個精靈的時候當然沒問題,但是,當你在屏幕上有200個精靈的時候,那么就會有200×7次opengl調用。。。而 CCSpriteBatchNode可以“批處理”它的孩子精靈的draw調用。這意味着,當把200個精靈加到 Spritesheet中去的時候,只要使用7個opengl調用就可以完成200個孩子的渲染了。

cocos2d裏面的描述

Detailed Description

CCSpriteBatchNode is like a batch node: if it contains children, it will draw them in 1 single OpenGL call (often known as "batch draw").

CCSpriteBatchNode can reference one and only one texture (one image file, one texture atlas). Only the CCSprites that are contained in that texture can be added to the CCSpriteBatchNode. All CCSprites added to a CCSpriteBatchNode are drawn in one OpenGL ES draw call. If the CCSprites are not added to a CCSpriteBatchNode then an OpenGL ES draw call will be needed for each one, which is less efficient.

Limitations:

  • The only object that is accepted as child (or grandchild, grand-grandchild, etc...) is CCSprite or any subclass of CCSprite. eg: particles, labels and layer can't be added to a CCSpriteBatchNode.
  • Either all its children are Aliased or Antialiased. It can't be a mix. This is because "alias" is a property of the texture, and all the sprites share the same texture.

 

 

 

優點:CCSpriteBatchNode 中的所有CCSprite只會被渲染1次,因此可以提高游戲的FPS。

限制:加入到 CCSpriteBatchNode 中的CCSprite必須使用同一張紋理圖。

 

問:什么時候應該用CCSpriteBatchNode?

答:比如游戲中的子彈 就很適合用它,因為子彈都是一個樣子。

答:通過TexturePacker生成的紋理圖也適合使用它。

 

看一個簡單的Demo:

 

[java]  view plain copy
  1. CCSpriteBatchNode *batch = [CCSpriteBatchNode batchNodeWithFile:@"shopAmber.png"];//初始化時給一張紋理圖  
  2. [self addChild:batch];//加入到當前Layer  
  3.           
  4. CCSprite *spr = [CCSprite spriteWithFile:@"shopAmber.png"];//切記! 這里的紋理圖必須和上面相同,否則會崩潰~  
  5. spr.position = ccp(10,10);  
  6. [batch addChild:spr z:2];  
  7.           
  8. CCSprite *spr2 = [CCSprite spriteWithFile:@"shopAmber.png"];  
  9. spr2.position = ccp(10,40);  
  10. [batch addChild:spr2 z:1];//可以指定z坐標。  

 

 

下面看看它的細節:

 

[java]  view plain copy
  1. //創建CCSpriteBatchNode  
  2. CCSpriteBatchNode *batch = [CCSpriteBatchNode batchNodeWithFile:@"shopAmber.png"];  

 

看看 batchNodeWithFile的實現:

 

[java]  view plain copy
  1. +(id)batchNodeWithFile:(NSString*) imageFile  
  2. {  
  3.     return [[[self alloc] initWithFile:imageFile capacity:defaultCapacity] autorelease];//defaultCapacity==29默認可以addChild29個精靈,應該會自動擴充<pre name="code" class="java">}  
 
         

 

 
         

 

再看看 initWithFile的實現:

 

[java]  view plain copy
  1. -(id)initWithFile:(NSString *)fileImage capacity:(NSUInteger)capacity  
  2. {  
  3.        //看看其實就是被加載成了一張2d紋理圖。  
  4.        CCTexture2D *tex = [[CCTextureCache sharedTextureCache] addImage:fileImage];  
  5.     return [self initWithTexture:tex capacity:capacity];  
  6. }  

 

 

[java]  view plain copy
  1. [self addChild:batch];//把CCSpriteBatchNode加入當前Layer,batch就相當於一個Layer  


之后你向CCSpriteBatchNode里加精靈 就相當於向一個層里加精靈:

 

 

[java]  view plain copy
  1. [batch addChild:spr z:2];  

 

可以使用 CCSpriteFrameCache配合CCSpriteBatchNode一起使用,效率會更高:

 

[java]  view plain copy
  1. [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"Resources.plist"];  
  2.           
  3. CCSprite *spr = [CCSprite spriteWithSpriteFrameName:@"Icon.png"];  
  4. spr.position = ccp(10,10);  
  5. [batch addChild:spr z:2];  
  6.           
  7. CCSprite *spr2 = [CCSprite spriteWithSpriteFrameName:@"shopAmber.png"];  
  8. spr2.position = ccp(10,40);  
  9. [batch addChild:spr2 z:1];  

這樣看上去使用了2張不同的圖片,但是它們是在同一張紋理圖里的。

 

 

附:

sprite能否同時使用CCSpriteBatchNode上的2張圖?

現在很糾結一個問題,搞了好幾天還是沒解決,大蝦們幫我看看,給個建議吧,謝謝。

我要實現的效果是這樣:在敵人受到攻擊受傷時,要增加一個冒煙的動畫。


我現在遇到的困難是如下:
敵人位置是變化的,受傷煙霧的效果想通過一張圖旋轉,淡出這樣,但是煙霧得跟隨受傷的敵人。
在主類 :GameLayer 中,先加載了enemyBatchNode 大圖,大圖中包含多個敵人和受傷煙霧
 

enemyBatchNode = [CCSpriteBatchNode batchNodeWithFile:@"enemy-hd.png"];        
[sharedSpriteFrameCache] addSpriteFramesWithFile:@"enemy-hd.plist"];

初始化一個敵人:         

Enemy *e =[ :2];
[enemyBatchNode addChild:e z:1];


敵人類:Enemy初始化

+(id) enemyInit:(int)enemyType
{  
  return [[ initWithMyEnemyImage:enemyType] autorelease];
}
-(id) initWithMyEnemyImage:(int)enemyType
{
  NSString *file = ;
  if((self == ))
  {
    type = enemyType;
  }
  return self;
}
-(void)addSmoke
{
  smokeSprite = ;
  smokeSprite.visible = NO;
}
-(void)playSmoke
{
  CGSize winSize = [ winSize];
  id ac1 = ;
  id ac2 = ;
  id ac3 = ;
  id ac4 = ;
}



問題來了:因為煙霧要跟隨敵人位置,所以我想是不是煙霧就要在敵人類里直接生成,然后到要播放的時候,就直接播放,但是我現在的addSmoke中,smokeSprite無論怎么初始化,都沒法得到smoke小圖,而是整張enemyBatchNode 大圖的縮小版,所以懇請各位教我下,我這種情況,煙霧要如何加入呢?

 

我還有試了另一種方法,就是把smoke也單獨用一個類,然后預先生成很多個,在哪個敵人受傷需要時,就加入進去,但是這樣又有新的問題,因為煙霧的播放過程,我不知道怎樣讓它播放完就直接初始化為原來的狀態下次再用,所以如果不能重用的話,要一次生成很多,fps就會嚴重下降,並且煙霧的位置不好控制,還有就是,因為多個敵人有層次的區分,如果單獨弄的,又有新的問題

 

 

答:煙霧做個粒子效果撒,打中了敵人就出現煙霧,添加在敵人身上。然后設置煙霧存在多少秒后消失

 

 

把所有的特效,比如煙霧、爆炸之類的效果做成一個spritesheet,然后新建一個EffectsManager類,專門負責游戲中的特效。然后再也一些方法來顯示這些特效就可以了。
每一個特效對應一個sprite,如果有多個特效要同時出現,則多創建幾個對應的sprite。
不知道這樣子你能理解不,目前我是這么設計的。

 

 

沒有看很明白,認真想了一下,我現在的實際問題是出在如何顯示上,也就是不知道是應該在敵人類內部顯示還是在外部顯示。內部顯示時在添加特效時就出錯了,而在外部顯示的出現的問題2樓種描述。

 

 

 

最近TexturePacker來壓縮圖片,減少游戲的體積。當然就用到了CCSpriteBatchNode。

但發現在CCMenu中使用CCMenuItemSprite的時候報錯,最終還是解決了這個問題

1)圖片不加到CCSpriteBatchNode里面,按照老方法正常使用CCMenuItem

但達不到我想要減少游戲體積的目的

2)使用CCSprite代替,然后增加touch事件,來判斷是否點擊了CCSprite所在的區域,做相應處理

太麻煩了,要寫很多代碼。但效果不錯,可以自己控制點擊的效果

3)把CCMenuItemSprite的selectedSprite設置為nil

這樣可以在CCMenu中使用CCSpriteBatchNode,但沒有了selectedSprite后一些效果不好做了

4)CCMenuAdvanced (在網上看到的,具體怎么用不清楚)


第三點能滿足我的需求,而且不增加任何多余代碼。所以就沒有去查第四點了

有興趣的朋友,可以參考http://www.cocos2d-iphone.org/forum/topic/1435

以下是我的Layer的init中的代碼片段

 

CCSpriteBatchNode *batchNode;

 

 

batchNode = [CCSpriteBatchNodebatchNodeWithFile:@"main.pvr.ccz"];

[selfaddChild:batchNode];

[[CCSpriteFrameCachesharedSpriteFrameCacheaddSpriteFramesWithFile:@"main.plist"];

 

 CCSprite *accelerate = [CCSpritespriteWithSpriteFrameName:@"accelerateIcon.png"];

CCSprite *sortition = [CCSpritespriteWithSpriteFrameName:@"sortitionIcon.png"];

 

CCMenuItemSprite *item1 = [CCMenuItemSpriteitemFromNormalSprite:sortition 

                                                          selectedSprite:nil 

                                                                  target:self 

                                                                selector:@selector(doSortition)];

item1.position = ccp(35,31);

CCMenuItemSprite *item2 = [CCMenuItemSpriteitemFromNormalSprite:accelerate 

                                                          selectedSprite:nil 

                                                                  target:self 

                                                                selector:@selector(doAccelerate)];

item2.position = ccp(443,28);

CCMenu *menu = [CCMenumenuWithItems:item1,item2, nil];

menu.position = CGPointZero;

[selfaddChild:menu];


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM