Cocos2d开发系列(五)

本文详细介绍了Cocos2d游戏开发中的CCSpriteBatchNode,强调了其在提高渲染效率上的作用。通过示例解释了如何创建和使用CCSpriteBatchNode,以及需要注意的限制,如所有Sprite必须使用相同纹理。同时,文章提供了一个简单的ship类和GameScene类的代码示例,展示如何在实际项目中应用CCSpriteBatchNode。
摘要由CSDN通过智能技术生成

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               

 

Learn IPhone andiPad Cocos2d Game Delevopment》第6章(原文中部分无关紧要的内容没有进行翻译)。

一、 CCSpriteBatchNode

在屏幕上贴图时,图形硬件需要经过准备、渲染、清除等步骤。每次贴图都会重复这个过程。如果图形硬件能事先知道有一组拥有相同纹理的Sprite需要渲染,则这个过程会被简化。比如,一组Sprite的准备和清除动作总共只需要执行一次。

下图的例子使用了CCSpriteBacthNode。屏幕上同时有几百颗子弹飞过。如果一次只渲染一颗,那么帧率马上降到85%。使用CCSpriteBatchNode,可以避免这种情况:

 

通常我们这样创建一个CCSprite:

CCSprite*sprite=[CCSprite spriteWithFile:@”bullet.png”];

[selfaddChild:sprite];

而使用CCSpriteBatchNode则需要修改为:

CCSpriteBatchNode*batch=[CCSpriteBatchNode batchNodeWithFile:@”bullet.png”];

[selfaddChild:batch];

for(inti=0;i<100;i++){

CCSprite* sprite=[CCSpritespriteWithFile:@”bullet.png”];

[batch addChild:bullet];

}

注意,CCSpriteBatchNode需要一个图片文件名作为参数,哪怕它根本用不着这个图片(进行显示)。可以把它看做是一个Layer,你可以用它来加入一些CCSprite节点。由于它使用了一个图片文件作为构造参数,所以在后面加入的CCSprite中必须使用相同的文件作为构造参数,否则会导致如下错误:

SpriteBatches[13879:207]*** Terminating app due to uncaught exception'NSInternalInconsistencyException', reason: 'CCSprite is not using the sametexture id'

当采用相同纹理的CCSpite越多,则采用CCSpriteBatchNode的好处越明显。

但这有一个限制,所有的CCSprite节点都会位于同一个Z坐标(深度)上。如果子弹是“击穿”敌人并向后飞,你得使用两个Z轴不同的CCSpriteBatchNode。

另外一个限制是,CCSpriteBatchNode和加入其中的CCSprite必须使用相同的贴图。这一点在使用TextureAtlas时尤其显得重要。一个Texture Atlas可以画多个不同的图片,并且这些图片使用同一个CCSpriteBatchNode,以提高渲染速度。

Z轴的问题可以通过指定CCSpriteBatchNode中单个CCSprite的Z值来解决。如果你所有的图片都放到了一个TextureAtlas(纹理集),则你完全可以只使用一个CCSpriteBatchNode。

CCSpriteBatchNode看成一个简单的CCLayer,它只接受使用相同图片的CCSprite,这样,你就知道怎么用它了。

在下面代码中,隐藏有一个致命的陷阱:

(id)init{

    If ((self = [superinitWithFile:@"ship.png"])) {

[self scheduleUpdate];

    }

    return self;

}

由于-(id)init方法是默认的初始化方法,它会被其他初始化方法比如initWithFile调用。在-(id)init方法中调用了[super initWithFile…]方法,[super initWithFile…]会调用[super init], 该类覆盖了-(id)init方法,于是又会调用-(id)init方法,无限循环。

解决办法是修改方法名,比如修改为-(id)initWithShipImage。

这个教训告诉我们,在默认初始化方法-(id)init中,除了[superinit]之外,永远不要调用其他东西(其他的初始化方法)。如果你必须在初始化方法中调用[super initWith…]方法,你应当把方法名命名为initWith…。

二、示例代码

1、ship类

#import <Foundation/Foundation.h>

#import "cocos2d.h"

 

@interface Ship : CCSprite

{

}

 

+(id) ship;

 

@end

#import "Ship.h"

#import "Bullet.h"

#import "GameScene.h"

 

@interface Ship (PrivateMethods)

-(id) initWithShipImage;

@end

 

 

@implementation Ship

 

+(id) ship

{

return [[[self alloc] initWithShipImage] autorelease];

}

 

-(id) initWithShipImage

{

if ((self = [super initWithFile:@"ship.png"]))

{

[self scheduleUpdate];

}

return self;

}

 

-(void) dealloc

{

[super dealloc];

}

 

-(void) update:(ccTime)delta

{

[[GameScene sharedGameScene] shootBulletFromShip:self];

}

 

@end

 

ship类很简单,除了update方法。该方法调用了GameScene的shootBulletFromShip方法外([GameSceneshareGameScene]实际上只是获取GameScene 的单实例)。

2、GameScene类

#import <Foundation/Foundation.h>

#import "cocos2d.h"

 

#import "Ship.h"

 

typedef enum

{

GameSceneNodeTagBullet = 1,

GameSceneNodeTagBulletSpriteBatch,

} GameSceneNodeTags;

 

@interface GameScene : CCLayer

{

int nextInactiveBullet;

}

 

+(id) scene;

+(GameScene*) sharedGameScene;

 

-(void) shootBulletFromShip:(Ship*)ship;

 

@property (readonly) CCSpriteBatchNode*bulletSpriteBatch;

 

@end

#import "GameScene.h"

#import "Ship.h"

#import "Bullet.h"

 

@interface GameScene(PrivateMethods)

-(void) countBullets:(ccTime)delta;

@end

 

@implementation GameScene

 

static GameScene*instanceOfGameScene;

+(GameScene*) sharedGameScene

{

NSAssert(instanceOfGameScene != nil, @"GameSceneinstance not yet initialized!");

return instanceOfGameScene;

}

 

+(id) scene

{

CCScene *scene = [CCScene node];

GameScene *layer = [GameScene node];

[scene addChild: layer];

return scene;

}

 

-(id) init

{

if ((self = [super init]))

{

instanceOfGameScene = self;

CGSize screenSize = [[CCDirector sharedDirector] winSize];

CCColorLayer* colorLayer = [CCColorLayer layerWithColor:ccc4(255, 255, 255, 255)];

[self addChild:colorLayer z:-1];

CCSprite* background = [CCSprite spriteWithFile:@"background.png"];

background.position = CGPointMake(screenSize.width / 2, screenSize.height / 2);

[self addChild:background];

Ship* ship = [Ship ship];

ship.position = CGPointMake(ship.texture.contentSize.width / 2, screenSize.height / 2);

[self addChild:ship];

CCSpriteBatchNode* batch = [CCSpriteBatchNode batchNodeWithFile:@"bullet.png"];

[self addChild:batch z:1 tag:GameSceneNodeTagBulletSpriteBatch];

 

for (int i = 0; i < 400; i++)

{

Bullet* bullet = [Bullet bullet];

bullet.visible =NO;

[batch addChild:bullet];

}

[self schedule:@selector(countBullets:) interval:3];

}

return self;

}

 

-(void) dealloc

{

instanceOfGameScene = nil;

//don't forget to call "super dealloc"

[super dealloc];

}

 

-(void) countBullets:(ccTime)delta

{

CCLOG(@"Number ofactive Bullets: %i", [self.bulletSpriteBatch.children count]);

}

 

-(CCSpriteBatchNode*) bulletSpriteBatch

{

CCNode* node = [self getChildByTag:GameSceneNodeTagBulletSpriteBatch];

NSAssert([node isKindOfClass:[CCSpriteBatchNode class]], @"not aCCSpriteBatchNode");

return (CCSpriteBatchNode*)node;

}

 

-(void) shootBulletFromShip:(Ship*)ship

{

CCArray* bullets = [self.bulletSpriteBatch children];

CCNode* node = [bullets objectAtIndex:nextInactiveBullet];

NSAssert([node isKindOfClass:[Bullet class]], @"not abullet!");

Bullet* bullet = (Bullet*)node;

[bullet shootBulletFromShip:ship];

nextInactiveBullet++;

if (nextInactiveBullet >= [bullets count])

{

nextInactiveBullet = 0;

}

}

 

@end

现在你应该看到了,在init方法中使用CCSpriteBatchNode加入了400颗子弹(被设置为不可见了)。然后在接下来的shootBulletFromShip方法(在ship的update方法中调用)中,依次调用每一颗子弹的shootBulletFromShip方法。

3、Bullet类

#import <Foundation/Foundation.h>

#import "cocos2d.h"

 

#import "Ship.h"

 

@interface Bullet : CCSprite

{

CGPoint velocity;

float outsideScreen;

}

 

@property (readwrite, nonatomic) CGPoint velocity;

 

+(id) bullet;

 

-(void) shootBulletFromShip:(Ship*)ship;

 

@end

#import "Bullet.h"

 

 

@interface Bullet(PrivateMethods)

-(id) initWithBulletImage;

@end

 

 

@implementation Bullet

 

@synthesize velocity;

 

+(id) bullet

{

return [[[self alloc] initWithBulletImage] autorelease];

}

 

-(id) initWithBulletImage

{

if ((self = [super initWithFile:@"bullet.png"]))

{

}

return self;

}

 

-(void) dealloc

{

[super dealloc];

}

 

// Re-Uses the bullet

-(void) shootBulletFromShip:(Ship*)ship

{

float spread = (CCRANDOM_0_1() - 0.5f) * 0.5f;

velocity = CGPointMake(1, spread);

outsideScreen = [[CCDirector sharedDirector] winSize].width;

self.position = CGPointMake(ship.position.x + ship.contentSize.width * 0.5f, ship.position.y);

self.visible = YES;

[self scheduleUpdate];

}

 

-(void) update:(ccTime)delta

{

self.position =

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>