分享一下我老师大神的人工智能教程!零基础,通俗易懂!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 =