游戏邦在:
杂志专栏:
gamerboom.com订阅到鲜果订阅到抓虾google reader订阅到有道订阅到QQ邮箱订阅到帮看

详述iPhone太空射击游戏的制作过程

作者:ray wenderlich

在本教程中,你将学习如何制作iPhone平台的太空射击游戏。

你将通过加速计驾驶太空飞船。通过稳固的激光器在充满危险小行星的领域里扫射穿行。

Spaceship game from raywenderlich.com

Spaceship game from raywenderlich.com

若你是iPhone游戏制作的新手,那么此教程就是专门为你服务。你将学习如何从零制作出一款游戏,你无需具备开发经验。

此教程也非常适合中级水平的开发者,因为它涉及若干特殊效果,例如视差滚动,预先配置的CCNodes,加速计运动及particle机制。

安装Cocos2D

要制作这款游戏,你需要成为iOS开发者计划的会员(游戏邦注:这样你就能够在自己的iPhone上运行这款游戏),还有就是安装Xcode 和Cocos2D架构。

若你已安装Cocos2D,欢迎跳至下个版块。下面是若干在Mac安装Cocos2D的操作指示:

* 从此页面下载Cocos2D。确保选择最新版本。

* 双击下载文件进行解压,然后将其存储在安全位置(自由选项)。

* 打开Terminal (Applications\Utilities\Terminal),通过cd命令导航至cocos2d文件夹。然后运行./install-templates.sh命令,安装你的Xcode模板,具体操作如下:

$ cd Downloads
$ cd cocos2d-iphone-1.0.0-rc2
$ ./install-templates.sh -f -u

如果一切运作顺利,你会看到若干行“Installing xxx template”内容。

然后重新启动Xcode,祝贺你,你已完成Cocos2D的安装工作。

Cocos2D

不妨从制作“Hello World” Cocos2D项目着手。

启动Xcode,锁定File\New\New Project,选择iOS\cocos2d模板,然后点击Next。将项目命做SpaceGame,然后点击Next,选择文件夹保存你的项目,然后点击Create。

编译且运行你的项目,你会看到“Hello World”出现于屏幕上:

HelloWorld from raywenderlich.com

HelloWorld from raywenderlich.com

添加资源

要制作这款iPhone游戏,你需要围绕太空主题的些许美术和声音特效。

但还不要拿出MS Paint。幸运的是,我妻子制作出若干杰出的太空游戏资源,你可以在此项目中进行利用。

不妨下载此太空游戏资源,将它们解压到你的硬盘驱动器中。

解压资源后, 将Backgrounds、Fonts、Particles、Sounds和Spritesheets文件夹拖到Xcode项目的Resources组中(游戏邦注:除Classes文件夹外)。

确保选中“将道具复制到目标群组的文件夹中”,点击Finish。

完成后,你的Groups和Files树状图应呈如下样式:

XcodeResourcesGroup from raywenderlich.com

XcodeResourcesGroup from raywenderlich.com

若你感到好奇,欢迎查看你刚添加至项目中的文件。下面是具体内容:

* Backgrounds:用于制作游戏侧滚动背景的若干图像。包含银河、日出及太空anomolies(这将缓慢移动)的图像,还有就是太空灰尘(这会出现在前方,移动更加迅速)图像。

* Fonts:这是基于Glyph Designer制作的位图字体,我们随后将用于呈现若干游戏文本。

* Particles:我们将用于创造星星飞行效果的太空特效,由Particle设计师负责。

* Sounds:若干太空主题的背景音乐和音效,通过Garage Band和cxfr制作而成。

* Spritesheets:主要是pvr.ccz格式的图像,其中包含若干我们将运用至游戏中的更小图像,有小行星和太空飞船等。这通过Texture Packer创建而成——若你想要基于pvr.ccz格式查看内容,那这就必不可少。

如果你没有安装这些工具,不必担心——在本教程中,你不需要这些内容,因为你可以利用这些预制文件。你可以随后试验这些工具。

下面是Sprites.pvr.ccz的具体外观:

SpritesheetTexturePacker from raywenderlich.com

SpritesheetTexturePacker from raywenderlich.com

也许你会好奇为什么我们要将所有这些图像整合到一个大规模图像中,这是因为它能够帮我们在制作游戏的过程中节约内存,提高运作性能。

这是个绝佳实践方式,所以我们初期就着手于此。

添加太空飞船

首先先简单地从在屏幕中添加太空飞船着手。

打开HelloWorldLayer.h,在界面中添加两个新的实体变量:

CCSpriteBatchNode *_batchNode;
CCSprite *_ship;

第一个变量(_batchNode)非常必要,因为我们将所有图像存储于单个画面中,这样我们就能够整合所有绘图工作。

第二个变量(_ship)代表屏幕上的太空船。

下面切换到HelloWorldLayer.m,将init方法替换成如下内容:

-(id) init
{
if( (self=[super init])) {

_batchNode = [CCSpriteBatchNode batchNodeWithFile:@"Sprites.pvr.ccz"]; // 1
[self addChild:_batchNode]; // 2
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@”Sprites.plist”]; // 3

_ship = [CCSprite spriteWithSpriteFrameName:@"SpaceFlier_sm_1.png"];  // 4
CGSize winSize = [CCDirector sharedDirector].winSize; // 5
_ship.position = ccp(winSize.width * 0.1, winSize.height * 0.5); // 6
[_batchNode addChild:_ship z:1]; // 7
}
return self;
}

下面我们就逐步重温整个过程:

1. 创建CCSpriteBatchNode,汇集同个大画面中所有物体的绘图工作。输入图像名称(Sprites.pvr.ccz)。

2. 将CCSpriteBatchNode添加至图层中,这样它就会被绘制。

3. 加载Sprites.plist文件,这包含较小图像在大型图像中的位置之类的信息。这让你随后能够通过spriteWithSpriteFrameName轻松检索子图像。

4. 通过SpaceFlier_sm_1.png图像制作新Sprite,这是大图像中的子图像。

5. 从CCDirectory中获取屏幕尺寸——我们很快就会需要此信息。

6. 设置船只的位置,让它位于距离两侧10%及距离上下边缘50%的位置。注意船只位置默认就是船只中心。

7. 将船只添加至batchNode,这样sprite的绘制就会分批处理。

编译及运行你的项目,你会看到船只图像出现在画面上。

JustSpaceShip from raywenderlich.com

JustSpaceShip from raywenderlich.com

添加视差滚动

我们的屏幕上有个很棒的太空飞船,但它看起来像是静坐其中。让我们通过在情境中添加杰出的视差滚动技术效果解决这一问题。

但什么是视差滚动?

视差滚动是指,“相比其他元素,我们更缓慢地移动某些背景元素。”若你有玩过《雷莎出击》之类的SNES游戏,你会经常在动作关卡背景中看到这一内容。

在Cocos2D中运用视差技术非常简单。你只需要完成3个步骤:

1. 创建CCParallaxNode,将其添加至图层中。

2. 制作你希望进行滚动的道具,通过addChild:parallaxRatio:positionOffset将其添加至CCParallaxNode中。

3. 移动CCParallaxNode,促使其滚动背景。它能够根据你对parallaxRatio的设定更快或更慢滚动CCParallaxNode的衍生物。

让我们查看具体运作方式。首先打开HelloWorldLayer.h,将如下内容添加至@interface中:

CCParallaxNode *_backgroundNode;
CCSprite *_spacedust1;
CCSprite *_spacedust2;
CCSprite *_planetsunrise;
CCSprite *_galaxy;
CCSprite *_spacialanomaly;
CCSprite *_spacialanomaly2;

然后切换至HelloWorldLayer.m,将如下内容添加至你HelloWorldLayer.m方法的底部:

_backgroundNode = [CCParallaxNode node];
[self addChild:_backgroundNode z:-1];

// 2) Create the sprites we’ll add to the CCParallaxNode
_spacedust1 = [CCSprite spriteWithFile:@"bg_front_spacedust.png"];
_spacedust2 = [CCSprite spriteWithFile:@"bg_front_spacedust.png"];
_planetsunrise = [CCSprite spriteWithFile:@"bg_planetsunrise.png"];
_galaxy = [CCSprite spriteWithFile:@"bg_galaxy.png"];
_spacialanomaly = [CCSprite spriteWithFile:@"bg_spacialanomaly.png"];
_spacialanomaly2 = [CCSprite spriteWithFile:@"bg_spacialanomaly2.png"];

// 3) Determine relative movement speeds for space dust and background
CGPoint dustSpeed = ccp(0.1, 0.1);
CGPoint bgSpeed = ccp(0.05, 0.05);

// 4) Add children to CCParallaxNode
[_backgroundNode addChild:_spacedust1 z:0 parallaxRatio:dustSpeed positionOffset:ccp(0,winSize.height/2)];
[_backgroundNode addChild:_spacedust2 z:0 parallaxRatio:dustSpeed positionOffset:ccp(_spacedust1.contentSize.width,winSize.height/2)];
[_backgroundNode addChild:_galaxy z:-1 parallaxRatio:bgSpeed positionOffset:ccp(0,winSize.height * 0.7)];
[_backgroundNode addChild:_planetsunrise z:-1 parallaxRatio:bgSpeed positionOffset:ccp(600,winSize.height * 0)];
[_backgroundNode addChild:_spacialanomaly z:-1 parallaxRatio:bgSpeed positionOffset:ccp(900,winSize.height * 0.3)];
[_backgroundNode addChild:_spacialanomaly2 z:-1 parallaxRatio:bgSpeed positionOffset:ccp(1500,winSize.height * 0.9)];

编译且运行你的项目,你会看到太空画面的初始情境:

SpaceStart from raywenderlich.com

SpaceStart from raywenderlich.com

但这还不是非常有趣,因为没有东西发生移动:

要移动太空灰尘和背景,你所要做的就是,移动视差节点本身。当我们将视差节点移动Y点时,灰尘就会移动0.1Y点,背景就会移动0.05Y点。

要移动视差节点,你只需根据设定速率在每个帧画面中更新位置。自己动手尝试对HelloWorldLayer.m做出如下变更:

// Add to end of init method
[self scheduleUpdate];

// Add new update method
- (void)update:(ccTime)dt {

CGPoint backgroundScrollVel = ccp(-1000, 0);
_backgroundNode.position = ccpAdd(_backgroundNode.position, ccpMult(backgroundScrollVel, dt));

}

编译且运行你的项目,内容会开始通过视差滚动模式整齐进行滚动!

ParallaxScrolling from raywenderlich.com

ParallaxScrolling from raywenderlich.com

但几秒钟过后,你会发现一个重大问题:我们耗尽滚动内容,你最终得到一个空白屏幕!这将相当乏味,所以下面就来看看我们能够对此采取什么举措。

持续滚动

我们希望背景永无止境地滚动。对此我们将采取的策略是,当幕后内容被移至左侧时,将背景移至右侧。

一个细微问题是,CCParallaxNode目前无法调节添加子节点的位移方式。你无法简单更新子节点的位置,因为CCParallaxNode覆盖各更新内容。

但我已创建有关CCParallaxNode的类,你可以以此解决这一问题,你可以在Classes文件夹的项目资源中找到相关内容。将CCParallaxNode-Extras.h和CCParallaxNode-Extras.m拖至你的项目中,确保选中“将项目复制至目标组的文件夹中”,点击Finish。

随后就HelloWorldLayer.m做出如下改变,以落实持续滚动:

// Add to top of file
#import “CCParallaxNode-Extras.h”

// Add at end of your update method
NSArray *spaceDusts = [NSArray arrayWithObjects:_spacedust1, _spacedust2, nil];
for (CCSprite *spaceDust in spaceDusts) {
if ([_backgroundNode convertToWorldSpace:spaceDust.position].x < -spaceDust.contentSize.width) {
[_backgroundNode incrementOffset:ccp(2*spaceDust.contentSize.width,0) forChild:spaceDust];
}
}

NSArray *backgrounds = [NSArray arrayWithObjects:_planetsunrise, _galaxy, _spacialanomaly, _spacialanomaly2, nil];
for (CCSprite *background in backgrounds) {
if ([_backgroundNode convertToWorldSpace:background.position].x < -background.contentSize.width) {
[_backgroundNode incrementOffset:ccp(2000,0) forChild:background];
}
}

编译且运行你的项目,现在背景要能够在美妙的太空情境中持续滚动!

ContinuousScrolling from raywenderlich.com

ContinuousScrolling from raywenderlich.com

添加星星

没有若干飞越而过的星星,太空游戏就算不上是完整内容。

我们可以制作另一包含星星的画面,像处理其他装饰品那样将其添加至视差节点中,但星星是你运用particle机制的典型代表。

particle机制让你能够通过运用相同sprite,高效制作出众多较小物件。Cocos2D给予你众多控制particle机制的权限,Particle Designer是进行视觉设置的突出方式。

但关于本指南,我已给若干在屏幕上左右跳窜的星星创造些许particle效果。你只需将下述代码添加至init方法的底部,就能够完成设置工作:

NSArray *starsArray = [NSArray arrayWithObjects:@"Stars1.plist", @"Stars2.plist", @"Stars3.plist", nil];
for(NSString *stars in starsArray) {
CCParticleSystemQuad *starsEffect = [CCParticleSystemQuad particleWithFile:stars];
[self addChild:starsEffect z:1];
}

将particle机制添加至图层中,它们会自动开始运作。编译且运行内容,以查看结果,现在你会看到若干星星在情境中飞过!

StarsParticleSystem from raywenderlich.com

StarsParticleSystem from raywenderlich.com

通过加速计移动船只

到目前为止情况还算不错,但这还不算不上一款游戏,除非我们可以移动自己的太空飞船!

我们打算通过加速计移动太空飞船。随着用户沿着x轴倾斜设备,船只将上下移动。

这执行起来其实相当简单,所以下面就来看看这部分内容。首先,在HelloWorldLayer.h的@interface中添加实体变量,以追踪每秒点数,进而沿着Y轴移动船只。

float _shipPointsPerSecY;

然后,就HelloWorldLayer.m做出如下改变:

// 1) Add to bottom of init
self.isAccelerometerEnabled = YES;

// 2) Add new method
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {

#define kFilteringFactor 0.1
#define kRestAccelX -0.6
#define kShipMaxPointsPerSec (winSize.height*0.5)
#define kMaxDiffX 0.2

UIAccelerationValue rollingX, rollingY, rollingZ;

rollingX = (acceleration.x * kFilteringFactor) + (rollingX * (1.0 – kFilteringFactor));
rollingY = (acceleration.y * kFilteringFactor) + (rollingY * (1.0 – kFilteringFactor));
rollingZ = (acceleration.z * kFilteringFactor) + (rollingZ * (1.0 – kFilteringFactor));

float accelX = acceleration.x – rollingX;
float accelY = acceleration.y – rollingY;
float accelZ = acceleration.z – rollingZ;

CGSize winSize = [CCDirector sharedDirector].winSize;

float accelDiff = accelX – kRestAccelX;
float accelFraction = accelDiff / kMaxDiffX;
float pointsPerSec = kShipMaxPointsPerSec * accelFraction;

_shipPointsPerSecY = pointsPerSec;

}

// 4) Add to bottom of update
CGSize winSize = [CCDirector sharedDirector].winSize;
float maxY = winSize.height – _ship.contentSize.height/2;
float minY = _ship.contentSize.height/2;

float newY = _ship.position.y + (_shipPointsPerSecY * dt);
newY = MIN(MAX(newY, minY), maxY);
_ship.position = ccp(_ship.position.x, newY);

下面就来逐步回顾整个过程:

1. 添加如下代码能够让图层接收加速计:didAcccelerate callback。

2. 这一方式的第一部分直接来自苹果样本代码,旨在过滤加速计数值,这样它就不会如此“不稳定”。若你不清楚这点,别担心,你所需要知道的是,这让内容变得更平滑。若你非常好奇,这里有若干相关信息。在你操作完过滤器后,我们将展开测试,查看过滤掉多少内容。沿着X轴旋转-0.6被视作是“基准线”,所处位置越是接近0.2(游戏邦注:无论基于何种方向),船只的移动速度就越快。这些数值都是通过试验和“感觉”获得。

3. 根据先前计算的Y轴每秒移动点数及距上次更新以来的差量时间设定船只位置。

编译且运行你的项目,现在你就可以通过倾斜你的iPhone上下移动太空飞船。

Spaceship from raywenderlich.com

Spaceship from raywenderlich.com

添加小行星

游戏目前看起来非常不错,但危险和刺激性何在?不妨给情境添加小行星,以丰富内容。

我们将在屏幕右侧制作幕后小行星。随后我们就可以运行Cocos2D动作,将其移动至屏幕左侧。

当我们需要生成内容时,我们可以简单创造出新的小行星,但分配内存是个缓慢过程,最好能够避免。所以我们需要给成群小行星预先分配内存,在需要的时候抓取下个可利用小行星。

下面就看看具体内容。首先在HelloWorldLayer.h的@interface中添加若干新实体变量:

CCArray *_asteroids;
int _nextAsteroid;
double _nextAsteroidSpawn;

就HelloWorldLayer.m做出如下变:

// Add to top of file
#define kNumAsteroids   15

// Add to bottom of init
_asteroids = [[CCArray alloc] initWithCapacity:kNumAsteroids];
for(int i = 0; i < kNumAsteroids; ++i) {
CCSprite *asteroid = [CCSprite spriteWithSpriteFrameName:@"asteroid.png"];
asteroid.visible = NO;
[_batchNode addChild:asteroid];
[_asteroids addObject:asteroid];
}

// Add new method, above update loop
- (float)randomValueBetween:(float)low andValue:(float)high {
return (((float) arc4random() / 0xFFFFFFFFu) * (high – low)) + low;
}

// Add to bottom of update loop
double curTime = CACurrentMediaTime();
if (curTime > _nextAsteroidSpawn) {

float randSecs = [self randomValueBetween:0.20 andValue:1.0];
_nextAsteroidSpawn = randSecs + curTime;

float randY = [self randomValueBetween:0.0 andValue:winSize.height];
float randDuration = [self randomValueBetween:2.0 andValue:10.0];

CCSprite *asteroid = [_asteroids objectAtIndex:_nextAsteroid];
_nextAsteroid++;
if (_nextAsteroid >= _asteroids.count) _nextAsteroid = 0;

[asteroid stopAllActions];
asteroid.position = ccp(winSize.width+asteroid.contentSize.width/2, randY);
asteroid.visible = YES;
[asteroid runAction:[CCSequence actions:
[CCMoveBy actionWithDuration:randDuration position:ccp(-winSize.width-asteroid.contentSize.width, 0)],
[CCCallFuncN actionWithTarget:self selector:@selector(setInvisible:)],
nil]];

}

// Add new method
- (void)setInvisible:(CCNode *)node {
node.visible = NO;
}

关于上述代码,有几点需要指出:

* CCArray和NSArray颇为相似,但速度有所提高,所以这是Cocos2D的最佳选择。

* 注意,游戏开始后,我们将15个小行星添加至成批节点中,但要将它们都设置成隐藏式。如果它们处于隐藏状态,我们就可以将它们看作是不活跃。

* 我们通过实体变量(_nextAsteroidSpawn)获悉何时生成下个小行星。我们通常在更新循环中查看这点。

* 若你不熟悉Cocos2D操作,这些是让sprite逐步展开操作(游戏邦注:例如移动、缩放和旋转等)的简单方式。这里我们连续进行两个操作:稍微向左移动,然后调用能够将小行星设置成隐藏状态的方法。

编译且运行你的代码,现在有若干小行星会飞过屏幕。

Asteroids from raywenderlich.com

Asteroids from raywenderlich.com

射击激光

我不了解你,但我看到小行星想到的第一个操作是射击它们。

所以不妨通过添加炮火激光满足玩家的这一欲望。这一代码和我们添加小行星的方法类似,因为我们会创建大批可再利用的激光光束,通过操作将它们移动于画面中。

主要差别在于,我们会通过触控操作检测何时射击它们。

首先在HelloWorldLayer.h的@interface中添加若干新的实体变量:

CCArray *_shipLasers;
int _nextShipLaser;

然后就HelloWorldLayer.m做出如下变更:

// Add to top of file
#define kNumLasers      5

// Add to bottom of init
_shipLasers = [[CCArray alloc] initWithCapacity:kNumLasers];
for(int i = 0; i < kNumLasers; ++i) {
CCSprite *shipLaser = [CCSprite spriteWithSpriteFrameName:@"laserbeam_blue.png"];
shipLaser.visible = NO;
[_batchNode addChild:shipLaser];
[_shipLasers addObject:shipLaser];
}

self.isTouchEnabled = YES;

// Add new method
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

CGSize winSize = [CCDirector sharedDirector].winSize;

CCSprite *shipLaser = [_shipLasers objectAtIndex:_nextShipLaser];
_nextShipLaser++;
if (_nextShipLaser >= _shipLasers.count) _nextShipLaser = 0;

shipLaser.position = ccpAdd(_ship.position, ccp(shipLaser.contentSize.width/2, 0));
shipLaser.visible = YES;
[shipLaser stopAllActions];
[shipLaser runAction:[CCSequence actions:
[CCMoveBy actionWithDuration:0.5 position:ccp(winSize.width, 0)],
[CCCallFuncN actionWithTarget:self selector:@selector(setInvisible:)],
nil]];

}

所以这清楚展示出在Cocos2D中接收触控事件的简单性——将isTouchEnabled设置成YES,然后你就可以落实ccTouchesBegan(或是ccTouchesMoved、ccTouchesEnded等)。

编译且运行你的代码。

ShootingLasers from raywenderlich.com

ShootingLasers from raywenderlich.com

基础碰撞检测

就目前来看,这看起来像是款游戏,但操作起来不像是款游戏,因为没有什么爆炸元素。

由于我天生淘气,现在是时候给这款游戏添加些许暴力元素!

首先将下述内容添加至HelloWorldLayer.h的@interface中:

int _lives;

然后将如下内容添加至更新循环的底部:

for (CCSprite *asteroid in _asteroids) {
if (!asteroid.visible) continue;

for (CCSprite *shipLaser in _shipLasers) {
if (!shipLaser.visible) continue;

if (CGRectIntersectsRect(shipLaser.boundingBox, asteroid.boundingBox)) {
shipLaser.visible = NO;
asteroid.visible = NO;
continue;
}
}

if (CGRectIntersectsRect(_ship.boundingBox, asteroid.boundingBox)) {
asteroid.visible = NO;
[_ship runAction:[CCBlink actionWithDuration:1.0 blinks:9]];
_lives–;
}
}

这是个非常基础的碰撞检测方式,通过检测sprite的边界框,查看它们是否发生触碰。注意这将透明情况也计算在内,因此不是检查触碰方式的完美方式,但对于一款像这样简单的游戏来说,这已非常足够。

编译且运行你的代码,现在游戏就包含爆炸元素。

胜败检测

我们差不多快完工了——只需要添加让玩家成功或失败的方法!

我们会做出相关设置,这样当玩家存活30秒,他就取得成功,当他被击中3次,他就失败。

首先就HelloWorldLayer.h做出些许变更:

// Add before @interface
typedef enum {
kEndReasonWin,
kEndReasonLose
} EndReason;

// Add inside @interface
double _gameOverTime;
bool _gameOver;

然后针对HelloWorldLayer.m做出如下调整:

_lives = 3;
double curTime = CACurrentMediaTime();
_gameOverTime = curTime + 30.0;

// Add at end of update loop
if (_lives <= 0) {
[_ship stopAllActions];
_ship.visible = FALSE;
[self endScene:kEndReasonLose];
} else if (curTime >= _gameOverTime) {
[self endScene:kEndReasonWin];
}

// Add new methods above update
- (void)restartTapped:(id)sender {
[[CCDirector sharedDirector] replaceScene:[CCTransitionZoomFlipX transitionWithDuration:0.5 scene:[HelloWorldLayer scene]]];
}

- (void)endScene:(EndReason)endReason {

if (_gameOver) return;
_gameOver = true;

CGSize winSize = [CCDirector sharedDirector].winSize;

NSString *message;
if (endReason == kEndReasonWin) {
message = @”You win!”;
} else if (endReason == kEndReasonLose) {
message = @”You lose!”;
}

CCLabelBMFont *label;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
label = [CCLabelBMFont labelWithString:message fntFile:@"Arial-hd.fnt"];
} else {
label = [CCLabelBMFont labelWithString:message fntFile:@"Arial.fnt"];
}
label.scale = 0.1;
label.position = ccp(winSize.width/2, winSize.height * 0.6);
[self addChild:label];

CCLabelBMFont *restartLabel;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
restartLabel = [CCLabelBMFont labelWithString:@"Restart" fntFile:@"Arial-hd.fnt"];
} else {
restartLabel = [CCLabelBMFont labelWithString:@"Restart" fntFile:@"Arial.fnt"];
}

CCMenuItemLabel *restartItem = [CCMenuItemLabel itemWithLabel:restartLabel target:self selector:@selector(restartTapped:)];
restartItem.scale = 0.1;
restartItem.position = ccp(winSize.width/2, winSize.height * 0.4);

CCMenu *menu = [CCMenu menuWithItems:restartItem, nil];
menu.position = CGPointZero;
[self addChild:menu];

[restartItem runAction:[CCScaleTo actionWithDuration:0.5 scale:1.0]];
[label runAction:[CCScaleTo actionWithDuration:0.5 scale:1.0]];

}

如果你不清楚endScene方法的运作模式,不要担心——这是我过去运用在某些游戏快速胜败菜单中的若干代码。

最重要的部分是,由于你已把握剩余代码——各更新循环,你只需查看玩家成功,还是失败,然后调用方法。

编译且运行代码,查看自己是否能够进入失败状态!

GameOver from raywenderlich.com

GameOver from raywenderlich.com

音乐和音效

正如你知道的,添加若干杰出的音效和音乐必不可少!

你已将音效添加至项目中,因此你只需通过添加若干代码在HelloWorldLayer.m中播放内容:

// Add to top of file
#import “SimpleAudioEngine.h”

// Add to bottom of init
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@”SpaceGame.caf” loop:YES];
[[SimpleAudioEngine sharedEngine] preloadEffect:@”explosion_large.caf”];
[[SimpleAudioEngine sharedEngine] preloadEffect:@”laser_ship.caf”];

// Add inside BOTH CGRectIntersectsRect tests
[[SimpleAudioEngine sharedEngine] playEffect:@”explosion_large.caf”];

// Add inside ccTouchBegan
[[SimpleAudioEngine sharedEngine] playEffect:@”laser_ship.caf”];

YouWin from raywenderlich.com

YouWin from raywenderlich.com

就是这样——祝贺你,现在你已从零制作出一款完整的太空游戏。(本文为游戏邦/gamerboom.com编译,拒绝任何不保留版权的转载,如需转载请联系:游戏邦

How To Make A Space Shooter iPhone Game

by ray wenderlich

In this tutorial, you’ll learn how to make a space shooter game for the iPhone!

You’ll pilot a space ship with the accelerometer, and blast your way through a field of dangerous asteroids with your trusty laser gun!

If you’re a complete beginner to making iPhone games, this tutorial is for you! You’ll learn how to make a complete game from scratch, with no prior experience necessary!

If you’re completely new to programming in general you might want to check out this slightly easier introduction first.

This tutorial is also good for intermediate developers, because it covers some neat effects such as parallax scrolling, pre-allocating CCNodes, accelerometer movement, and particle systems.

Without further ado, let’s get blasting!

Install Cocos2D

To make this game, you’ll need to be a member of the iOS developer program (so you can run this game on your iPhone) and have Xcode and the Cocos2D framework installed.

If you already have Cocos2D installed, feel free to skip to the next section. Otherwise, here are some instructions for how to install Cocos2D on your Mac:

* Download Cocos2D from this page. Be sure to pick the very latest version – at the time of this writing it is 1.0.0-rc2 in the Unstable category. Don’t worry that it says Unstable – it actually works quite well!

* Double click the downloaded file to unzip it, and (optionally) store it somewhere safe.

* Open a Terminal (Applications\Utilities\Terminal), and use the cd command to navigate to where your cocos2d folder. Then run the ./install-templates.sh command to install your Xcode templates, like the following:

If all works well, you should see several lines saying “Installing xxx template”.

Then restart Xcode, and congrats – you’ve installed Cocos2D!

Hello, Cocos2D!

Let’s get started by creating a “Hello World” Cocos2D project.

Start up Xcode, go to File\New\New Project, choose the iOS\cocos2d template, and click Next. Name the project SpaceGame, click Next, choose a folder to save your project in, and click Create.

Compile and run your project, and you should see “Hello World” appear on the screen:

Adding Resources

To make this iPhone game, you are going to need some art and sound effects with a space theme.

But don’t whip out MS Paint quite yet – luckily my lovely wife has made some cool space game resources you can use in this project!

So go ahead and download the space game resources and unzip them to your hard drive.

Once you’ve unzipped the resources, drag the Backgrounds, Fonts, Particles, Sounds, and Spritesheets folders into the Resources group in your Xcode project. (Basically everything except the Classes folder).

Make sure that “Copy items into destination group’s folder (if needed)” is checked, and click Finish.

When you’re done your Groups and Files tree should look something like this:

If you’re curious, feel free to take a peek through the contents of the folders you just added to your project. Here’s what’s inside:

* Backgrounds: Some images that you’ll use to create a side-scrolling background for the game. Includes images of a galaxy, sunrise, and spatial anomolies (that will move quite slowly), and an image of some space dust (that will go in front and move a little bit faster).

* Fonts: A bitmap font created with Glyph Designer that we’ll use to display some text in the game later on.

* Particles: Some special effects we’ll be using to create the effect of some stars flying by, created with Particle Designer.

* Sounds: Some space-themed background music and sound effects, created with Garage Band and cxfr.

* Spritesheets: Contains an image in the pvr.ccz format containing several smaller images that we’ll be using in the game, including the asteroid, space ship, etc. This was created with Texture Packer – you’ll need this if you want to look at the pvr.ccz.

Don’t worry if you don’t have any of these tools installed – you don’t need them for this tutorial, since you can use these premade files. You can always try out these tools later!

Here’s what Sprites.pvr.ccz looks like by the way:

In case you’re wondering why we’re combining all those images into a large image like this, it’s because it helps conserve memory and improve performance while making the game.

It’s a good practice to get into, so we’re starting you out with it early!

Adding a Space Ship

Let’s start things out nice and simple by adding the space ship to the screen!

Start by opening HelloWorldLayer.h, and add two new instance variables inside the @interface:

The first variable (_batchNode) is necessary because we’re storing all of our images inside a single image, and using this helps us batch up all the drawing work.

The second variable (_ship) represents the space ship on the screen.

Next move to HelloWorldLayer.m, and replace the init method with the following:

Let’s go over this step by step:

1. Creates a CCSpriteBatchNode to batch up all of the drawing of objects from the same large image. Passes in the image name (Sprites.pvr.ccz).

2. Adds the CCSpriteBatchNode to the layer so it will be drawn.

3. Loads the Sprites.plist file, which contains information on where inside the large image each of the smaller images lies. This lets you easily retrieve the sub-images later with spriteWithSpriteFrameName.

4. Creates a new Sprite using the SpaceFlier_sm_1.png image, which is a sub-image within the large image.

5. Gets the size of the screen from the CCDirectory – we’ll need this in a second.

6. Sets the position of the ship so that it’s 10% along the width of the screen, and 50% along the height. Note that by default the position of the ship is the center of the ship.

7. Adds the ship to the batchNode so that the drawing of the sprite is batched up.

Compile and run your project, and you should see your ship image appear on the screen!

Adding Parallax Scrolling

We have a cool space ship on the screen, but it looks like it’s just sitting there! Let’s fix this by adding some cool parallax scrolling to the scene.

But wait a minute – what in the heck is parallax scrolling?!

Parallax scrolling is just a fancy way of saying “move some parts of the background more slowly than the other parts.” If you’ve ever played SNES games like Act Raiser, you’ll often see this in the background of the action levels.

It’s really easy to use parallax scrolling in Cocos2D. You just have to do three steps:

1. Create a CCParallaxNode, and add it to the layer.

2. Create items you wish to scroll, and add them to the CCParallaxNode with addChild:parallaxRatio:positionOffset.

3. Move the CCParallaxNode to scroll the background. It will scroll the children of the CCParallaxNode more quickly or slowly based on what you set the parallaxRatio to.

Let’s see how this works. Start by opening HelloWorldLayer.h, and add the following inside the @interface:

Then switch to HelloWorldLayer.m, and add the following to the bottom of your init method:

Compile and run your project, and you should see the start of a space scene:

However this isn’t very interesting yet, since nothing is moving!

To move the space dust and backgrounds, all you need to do is move the parallax node itself. For every Y points we move the parallax node, the dust will move 0.1Y points, and the backgrounds will move 0.05Y points.

To move the parallax node, you’ll simply update the position every frame according to a set velocity. Try this out for yourself by making the following changes to HelloWorldLayer.m:

Compile and run your project, and things should start to scroll pretty neatly with parallax scrolling!

However, after a few seconds goes by, you’ll notice a major problem: we run out of things to scroll through, and you end up with a blank screen! That would be pretty boring, so let’s see what we can do about this.

Continuous Scrolling

We want the background to keep scrolling endlessly. The strategy we’re going to take to do this is to simply move the background to the right once it has moved offscreen to the left.

One minor problem is that CCParallaxNode currently doesn’t have any way to modify the offset of a child node once it’s added. You can’t simply update the position of the child node itself, because the CCParallaxNode overwrites that each update.

However, I’ve created a category on CCParallaxNode that you can use to solve this problem, which you can find in the resources for this project in the Classes folder. Drag CCParallaxNode-Extras.h and CCParallaxNode-Extras.m into your project, make sure “Copy items into destination group’s folder” is checked, and click Finish.

Then make the following changes to HelloWorldLayer.m to implement continuous scrolling:

Compile and run your project, and now the background should scroll continuously through a cool space scene!

Adding Stars

No space game would be complete without some stars flying by!

We could create another image with stars on it and add that to the parallax node like we have with the other decorations, but stars are a perfect example of when you’d want to use a particle system.

Particle systems allow you to efficiently create a large number of small objects using the same sprite. Cocos2D gives you a lot of control over configuring particle systems, and Particle Designer is a great way to visually set these up.

But for this tutorial, I’ve already set up some particle effects for some stars racing from right to left across the screen that we can use. Simply add the following code to the bottom of the init method to set these up:

By adding the particle systems to the layer, they automatically start running. Compile and run to see for yourself, and now you should see some stars flying across the scene!

Moving the Ship with the Accelerometer

So far so good, except this wouldn’t be much of a game unless we can move our space ship!

We’re going to take the approach of moving the space ship via the accelerometer. As the user tilts the device along the X-axis, the ship will move up and down.

This is actually pretty easy to implement, so let’s jump right into it. First, add an instance variable inside the @interface in HelloWorldLayer.h to keep track of the points per second to move the ship along the Y-axis:

Then, make the following changes to HelloWorldLayer.m:

Let’s go over this bit-by-bit:

1. Adding this line sets the layer up to receive the accelerometer:didAcccelerate callback.

2. The first part of this method comes directly from Apple sample code, to filter the accelerometer values so it’s not so “jiggly”. Don’t worry if you don’t understand this, all you really need to know is that it makes things more smooth. If you’re insatiably curious here’s some info though. Anyway, after we run the filter, we test to see how much it’s tilted. A rotation of -0.6 along the x-axis is considered “baseline”, and the closer it gets to 0.2 in either direction, the faster the ship is set to move. These values were all gotten via experimentation and what “felt right”!

3. Sets the position of the ship based on the points per second to move along the Y axis computed earlier, and the delta time since last update.

Compile and run your project (on your iPhone, the accelerometer does not work on the Simulator), and now you should be able to move the spaceship up and down by tilting your iPhone!

Adding Asteroids

The game is looking good so far, but where’s the danger and excitement?! Let’s spice things up by adding some wild asteroids to the scene!

The approach we’re going to take is every so often, we’ll create an asteroid offscreen to the right of the screen. Then we’ll run a Cocos2D action to move it to the left of the screen.

We could simply create a new asteroid every time we needed to spawn, but allocating memory is a slow operation and it’s best when you can avoid it. So we’ll pre-allocate memory for a bunch of asteroids, and simply grab the next available asteroid when we need it.

OK, let’s see what this looks like. Start by adding a few new instance variables inside the @interface in HelloWorldLayer.h:

Then make the following changes to HelloWorldLayer.m:

Some things to point out about the above code:

* CCArray is similar to NSArray, but optimized for speed. So it’s good to use in Cocos2D when possible.

* Notice that we add all 15 asteroids to the batch node as soon as the game starts, but set them all to invisible. If they’re invisible we treat them as inactive.

* We use an instance variable (_nextAsteroidSpawn) to tell us the time to spawn an asteroid next. We always check this in the update loop.

* If you’re new to Cocos2D actions, these are easy ways to get sprites to do things over time, such as move, scale, rotate, etc. Here we perform a sequence of two actions: move to the left a good bit, then call a method that will set the asteroid to invisible again.

Compile and run your code, and now you have some asteroids flying across the screen!

Shooting Lasers

I don’t know about you, but the first thing I think of when I see asteroids is MUST SHOOT THEM!

So let’s take care of this urge by adding the ability to fire lasers! This code will be similar to how we added asteroids, because we’ll create an array of reusable laser beams and move them across the screen with actions.

The main difference is we’ll be using touch handling to detect when to shoot them.

Start by adding a few new instance variables inside the @interface in HelloWorldLayer.h:

Then make the following changes to HelloWorldLayer.m:

So this shows you how easy it is to receive touch events in Cocos2D – just set isTouchEnabled to YES, then you can implement ccTouchesBegan (and/or ccTouchesMoved, ccTouchesEnded, etc)!

Compile and run your code, and now you can go pew-pew!

Basic Collision Detection

So far things look like a game, but don’t act like a game, because nothing blows up!

And since I’m naughty by nature (and not ’cause I hate ya), it’s time to add some violence into this game!

Start by adding the following to the @interface in HelloWorldLayer.h:

Then add the following to the bottom of the update loop:

This is a very basic method of collision detection that just checks the bounding box of the sprites to see if they collide. Note that this counts transparency, so isn’t a perfect way of checking for collisions, but it’s good enough for a simple game like this.

For more info on a better way to do collision detection in Cocos2D, check out the How To Use Box2D For Just Collision Detection tutorial.

Compile and run your code, and now things should blow up!

Win/Lose Detection

We’re almost done – just need to add a way for the player to win or lose!

We’ll make it so the player wins if he survives for 30 seconds, and loses if he gets hit 3 times.

Start by making a few changes to HelloWorldLayer.h:

Then make the following changes to HelloWorldLayer.m:

Don’t worry if you don’t understand how the endScene method works – that’s some code I’ve used for a bunch of games for a quick win/lose menu in the past.

The important part is just that you understand the rest of the code – every update loop, you just check to see if the player has won or lost, and call that method if so.

Compile and run the code, and see if you can lose!

Gratuitous Music and Sound Effects

As you know, I can’t leave you guys without some awesome sound effects and music to add in!

You’ve already added the sounds to your project, so just add a bit of code to play them in HelloWorldLayer.m:

And that’s it – congratulations, you’ve made a complete space game for the iPhone from scratch!(Source:raywenderlich


上一篇:

下一篇: