(译)iPhone上面的现实增强(Augmented Reality )入门教程

免责申明(必读!):本博客提供的所有教程的翻译原稿均来自于互联网,仅供学习交流之用,切勿进行商业传播。同时,转载时不要移除本申明。如产生任何纠纷,均与本博客所有人、发表该翻译稿之人无任何关系。谢谢合作!

原文链接地址:http://www.raywenderlich.com/3997/introduction-to-augmented-reality-on-the-iphone

教程截图:

Create a simple augmented reality game where you can blast some aliens!

Create a simple augmented reality game where you can blast some aliens!

  在这篇教程中,你将学习到如何为你的iphone和ipod touch制作一个简单的增强现实游戏。

  在这个游戏中,你将使用到摄像头,陀螺仪和cocos2d框架。听起来很振奋人心吧?

  在写作这篇教程的时候,探索上面提到的一些技术的过程真的是非常有趣。这里有一些数学和转换,不过不要担心---没有什么事情是很难的!

  学习这篇教程的时候,你需要一个iPhone4,因为这个教程使用陀螺仪来移动游戏世界视图。

  你也需要一些基本的cocos2d方面的知识,当然要安装好cocos2d。如果你对于cocos2d完全陌生的话,你可以先看看本博客上的其它cocos2d教程

  你准备好爆头一些虚拟外星人了吗?跟我来吧!

 

著作权声明:本文由http://www.cnblogs.com/andyque翻译,欢迎转载分享。请尊重作者劳动,转载时保留该声明和作者博客链接,谢谢!

Getting Started

  打开Xcode,然后从File菜单中选择New\New Project。然后选择 iOS\cocos2d\cocos2d template,接着点击Next。把工程命名为ARSpaceShips,并点击Next,同时选择一个文件夹位置来保存本项目,最后点Create。

  我们将重用Space Shooter游戏的一些资源文件,所以,先下载它们并解压缩。

  下载完后,把Fonts,Sounds和Spritesheet文件夹拖到Resources分组下面。同时确保 Copy items into destination group’s folder被复选中,并且ARSpaceships target也要被选中。然后点击Finish。这时,你的工程看起来应该如下图所示:

  我们将在后面使用到这些资源。

玩一玩摄像头!

  如果你现在运行项目,那简单太无聊了,就是一个黑屏的HelloWorld,这玩意儿谁没看过!让我们往里面添加一些非常好玩的内容吧。选择AppDelegate.h,然后在interface里面添加一个UIView成员变量。

UIView *overlay;

  现在,打开AppDelegate.m文件,找到EAGLView *glView所在的代码行,把像素格式改成kEAGLColorFormatRGBA8。改完后如下图所示:

EAGLView *glView = [EAGLView viewWithFrame:[window bounds]
pixelFormat:kEAGLColorFormatRGBA8 depthFormat:
0];

  如果你不改变像素格式的话,那么摄像头里拍摄出来的图像就显示不出来。因为我们现在正在做一个增强现实的游戏,所以必须这样做!

  在 [window addSubview: viewController.view];下面,我们添加了以下代码:


// set the background color of the view
[CCDirector sharedDirector].openGLView.backgroundColor = [UIColor clearColor];
[CCDirector sharedDirector].openGLView.opaque
= NO;

// set value for glClearColor
glClearColor(0.0, 0.0, 0.0, 0.0);

// prepare the overlay view and add it to the window
overlay = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
overlay.opaque
= NO;
overlay.backgroundColor
=[UIColor clearColor];
[window addSubview:overlay];

  这里,我们把openGLView的背景颜色清除了,把视图设置为透明的,同时指定了glClearColor,最后,我们创建并添加了一个新的视图,叫做overlay。这个视图后面用来显示camera里的内容。

  接下来,在你刚刚添加的代码后面增加以下代码行:

#define CAMERA_TRANSFORM 1.24299

UIImagePickerController
*uip;

@try {
uip
= [[[UIImagePickerController alloc] init] autorelease];
uip.sourceType
= UIImagePickerControllerSourceTypeCamera;
uip.showsCameraControls
= NO;
uip.toolbarHidden
= YES;
uip.navigationBarHidden
= YES;
uip.wantsFullScreenLayout
= YES;
uip.cameraViewTransform
= CGAffineTransformScale(uip.cameraViewTransform,
CAMERA_TRANSFORM, CAMERA_TRANSFORM);
}
@catch (NSException * e) {
[uip release];
uip
= nil;
}
@finally {
if(uip) {
[overlay addSubview:[uip view]];
[overlay release];
}
}

[window bringSubviewToFront:viewController.view];

  首先,我们了一个常量来缩放摄像头。摄像头的比率是4:3,而iphone屏幕的比例是3:4,所以,我们需要缩放一下摄像头,使之与屏幕匹配。

  其次,我们创建了一个UIImagePickerController,设置了它的一些属性,具体效果就是没有控件并且缩放了的摄像头。然后,我们把它添加到了overlay视图中。

  最后,我们需要把 viewController.view显示到前面来(因为它包含了cocos2d的显示内容)。这样子摄像头拍摄的内容也就会显示到前台来了。

  现在,运行一下应用程序。现在,你将看到摄像头里面捕捉的画面是你的Hello World的背景了。

Shake, Rattle, and Roll…Well at Least Yaw!

  因为我们现在的程序已经可以把现实捕捉并显示出来了,接下来,我们将集中精力来解决本教程中比较难的部分。

  首先,我们需要把CoreMotion framework添加到项目中去。点击project文件,然后选择ARSpaceships target,再选择Build Phases标签页,展开 Link Binary With Libraries.。

  点击上图中的+号,选择 CoreMotion.framework,然后点击Add按钮。现在,我们就可以在项目中使用陀螺仪啦。

  打开HelloWorldLayer.h,然后导入一些头文件:

#include <CoreMotion/CoreMotion.h>
#import<CoreFoundation/CoreFoundation.h>

  然后在interface里面添加一些成员变量:

CMMotionManager *motionManager;
CCLabelTTF
*yawLabel;
CCLabelTTF
*posIn360Label;

  同时添加属性声明语句:

@property (nonatomic, retain) CMMotionManager *motionManager;

  现在,重点要来了!打开HelloWorldLayer.m文件,在if ((self=[super init]))语句内部,删除原来的“Hello World”标签语句,同时添加下面的代码来设置一些新的标签.

// add and position the labels
yawLabel = [CCLabelTTF labelWithString:@"Yaw: " fontName:@"Marker Felt" fontSize:12];
posIn360Label
= [CCLabelTTF labelWithString:@"360Pos: " fontName:@"Marker Felt" fontSize:12];
yawLabel.position
= ccp(50, 240);
posIn360Label.position
= ccp(50, 300);
[self addChild: yawLabel];
[self addChild:posIn360Label];

  目前为止,并没有什么特别的。只是添加了一些标签,指明了字体和一些文字。标签的位置都是在屏幕的左边。

  接下来,你需要设置motion manager,它会启动陀螺仪。

self.motionManager = [[[CMMotionManager alloc] init] autorelease];
motionManager.deviceMotionUpdateInterval
=1.0/60.0;
if (motionManager.isDeviceMotionAvailable) {
[motionManager startDeviceMotionUpdates];
}

[self scheduleUpdate];

  这里,我们分配并初始化了 motion manager对象。同时,我们还设置了更新间隔为每秒60次。如果设置支持陀螺仪的话,那么就启动更新。最后,我们触发一个update定时器。

  不要忘了在.m文件中添加synthesize语句,如下所示:

@synthesize motionManager;

  因为我们触发了update定时器,所以我们需要添加一个update方法。同时,在init方法的下面增加下面一个方法:

-(void)update:(ccTime)delta {
CMDeviceMotion
*currentDeviceMotion = motionManager.deviceMotion;
CMAttitude
*currentAttitude = currentDeviceMotion.attitude;

// 1: Convert the radians yaw value to degrees then round up/down
float yaw = roundf((float)(CC_RADIANS_TO_DEGREES(currentAttitude.yaw)));

// 2: Convert the degrees value to float and use Math function to round the value
[yawLabel setString:[NSString stringWithFormat:@"Yaw: %.0f", yaw]];

// 3: Convert the yaw value to a value in the range of 0 to 360
int positionIn360 = yaw;
if (positionIn360 <0) {
positionIn360
=360+ positionIn360;
}

[posIn360Label setString:[NSString stringWithFormat:
@"360Pos: %d", positionIn360]];

}

  现在,你可以运行应用程序了。你将会看到Yaw和positionIn360的对应标签值在改变。

那玩意儿究竟如何工作?

  尽管可以跑起来了,但是你可能会问,它究竟是如何工作的呢?让我们花几分钟时间来一步步解释上面的代码是如何工作的。

  首先,下载iTunes app store上面的免费程序 Gyrosocope app。安装并运行它,当你移动iphone的时候,就可以看到每个值究竟是怎么变化的。

  我们关心的值是Yaw。这个值代表往左或往右移动。在Gyroscope程序中,它的单位是度,然而 motion manager获取的值却是弧度。这就是为什么我们需要使用内置的函数CC_RADIANS_TO_DEGREES来把弧度转换成角度的原因啦。

  因此,在第一部分中,我们得到了yaw的弧度值,并且把它转换成角度,最后赋值给yaw变量。第二部分,我们只是把yaw的值显示到屏幕上去。如果你运行工程,你会看到yaw值的变化范围是从0~180,然后又从-180回到0.

  现在看看第三部分,你可能会奇怪positionIn360的值倒底是什么啊?好吧,这里只是一个手段,目的是使得放置飞碟的过程变得容易。

  这里的逻辑其实非常简单。如果yaw值是正的,那么我们什么也不做。如果是负的,那么就减去360.(加上一个负值和减去一个正值是一样的)。最后一行代码只是在屏幕上显示那个值。

  如果你还没完全理解,没关系,运行一下程序,看看具体值是怎么变化的吧。

灯光,摄像机,Action!

  现在,我们为陀螺仪的使用奠定基础了,让我们往里面添加一些太空飞船吧!我们将创建一个新的文件。首先,左键点ARSpaceships工程文件,然后选择New File。接着选 iOS\Cocoa Touch\Objective-C class,然后点击Next。确保NSObject被选中基类,然后点Next。把文件命名为EnemyShip.m,最后点Save。

  把 EnemyShip.h里的内容换成下面的代码:

#import"cocos2d.h"

@interface EnemyShip : CCSprite {
int yawPosition;
int timeToLive;
}

@property (readwrite)
int yawPosition;
@property (readwrite)
int timeToLive;

@end

  同时修改EnemyShip.m:

#import"EnemyShip.h"


@implementation EnemyShip

@synthesize yawPosition, timeToLive;

-(id)init {
self
= [super init];
if (self){
yawPosition
=0;
timeToLive
=0;
}
return self;
}

@end

  现在,回到HelloWorldLayer.h。在顶部导入EnemyShip类的头文件,如下所示:

#import"EnemyShip.h"

  在interface里面声明以下成员变量:

NSMutableArray *enemySprites;
int enemyCount;
CCSpriteBatchNode
*batchNode;

  最后,在interface声明下面,添加enemyCount属性,同时定义一些方法,具体如下图所示:

@property (readwrite) int enemyCount;

-(EnemyShip *)addEnemyShip:(int)shipTag;
-(void)checkEnemyShipPosition:(EnemyShip *)enemyShip withYaw:(float)yawPosition;
-(void)updateEnemyShipPosition:(int)positionIn360 withEnemy:(EnemyShip *)enemyShip;
-(void)runStandardPositionCheck:(int)positionIn360 withDiff:(int)difference withEnemy:(EnemyShip *)enemyShip;

  打开 HelloWorldLayer.m文件,同时作以下修改:

// Place after the #import statement
#include <stdlib.h>

// Place after the other @synthesize statement
@synthesize enemyCount;
#define kXPositionMultiplier 15
#define kTimeToLive 100

// Add to the bottom of init
batchNode = [CCSpriteBatchNode batchNodeWithFile:@"Sprites.pvr.ccz"];
[self addChild:batchNode];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:
@"Sprites.plist"];

  这里,我们加载了spritesheet,它的资源文件在一开始的时候,我们就把它拖到项目中来了。

  接下来,我们将添加一个新的方法来创建新的敌人太空飞船。在dealloc方法的上面添加下列方法:

-(EnemyShip *)addEnemyShip:(int)shipTag {

EnemyShip
*enemyShip = [EnemyShip spriteWithSpriteFrameName:@"enemy_spaceship.png"];

// Set position of the space ship randomly
int x = arc4random() %360;
enemyShip.yawPosition
= x;

// Set the position of the space ship off the screen, but in the center of the y axis
// we will update it in another method
[enemyShip setPosition:ccp(5000, 160)];

// Set time to live on the space ship
enemyShip.timeToLive = kTimeToLive;
enemyShip.visible
=true;

[batchNode addChild:enemyShip z:
3 tag:shipTag];

return enemyShip;
}

  这个方法接收一个整数值作为tag,并且返回一个EnemyShip CCSprite。下面一行代码,我们从精灵表单中创建一个EnemyShip精灵。接着,我们使用arc4random方法来得到一个0~360的随机数。最后,我们设置了飞船的位置,并把timeToLive的值设置为100,把飞船添加到batchNode里面,最后返回飞船精灵对象。

  在addEnemyShip下面,我们添加一个checkEnemyShipPosition方法:

-(void)checkEnemyShipPosition:(EnemyShip *)enemyShip withYaw:(float)yawPosition {
// Convert the yaw value to a value in the range of 0 to 360
int positionIn360 = yawPosition;
if (positionIn360 <0) {
positionIn360
=360+ positionIn360;
}

BOOL checkAlternateRange
=false;

// Determine the minimum position for enemy ship
int rangeMin = positionIn360 -23;
if (rangeMin <0) {
rangeMin
=360+ rangeMin;
checkAlternateRange
=true;
}

// Determine the maximum position for the enemy ship
int rangeMax = positionIn360 +23;
if (rangeMax >360) {
rangeMax
= rangeMax -360;
checkAlternateRange
=true;
}

if (checkAlternateRange) {
if ((enemyShip.yawPosition < rangeMax || enemyShip.yawPosition > rangeMin ) || (enemyShip.yawPosition > rangeMin || enemyShip.yawPosition < rangeMax)) {
[self updateEnemyShipPosition:positionIn360 withEnemy:enemyShip];
}
}
else {
if (enemyShip.yawPosition > rangeMin && enemyShip.yawPosition < rangeMax) {
[self updateEnemyShipPosition:positionIn360 withEnemy:enemyShip];
}
}
}

  这个方法看起来似乎有点让人摸不着头脑,一会最大值,一会儿最小值,一会候选值。不过,不要担心,其实非常简单。首先,我们检查设置的yaw坐标址(positionIn360),然后把此值限制在0~360之间。

  因为,我们有两端范围都是0~360,所以需要检查一下设置的positionIn360具体属于哪一端。我们使用一个任意数23来代表将在屏幕一半处显示的度数。

  因为,我们只需要关心变化范围是0~23和337~360的空间了。因为,另一端的线将会包过来。(这里相像成3维空间的一个圆)

  最后,如果敌人飞船在屏幕46度的范围之内的话,那么就要更新敌人飞船。checkAlternateRange判断语句只是用来决定什么时候来更新敌人飞船。

  如果checkAlternateRange为真的话,那么我们就检查敌船的位置是否落在min和max的范围之内。

positionIn360 =20
rangeMin
=357
rangeMax
=20
enemyShip.yawPosition
=359

  因为我们要考虑线段的两端,我们的min范围比max范围要大一些。现在,我们做一个判断,看敌船的位置是不是大于rangeMin,如果是,则显示在屏幕上。

  if语句中的else就更加明了了。他检查敌船的位置是不是大于min且小于max。

  多么复杂的一个update方法啊!我们在checkEnemyShipPosition方法下面添加以下代码:

-(void)updateEnemyShipPosition:(int)positionIn360 withEnemy:(EnemyShip *)enemyShip {
int difference =0;
if (positionIn360 <23) {
// Run 1
if (enemyShip.yawPosition >337) {
difference
= (360- enemyShip.yawPosition) + positionIn360;
int xPosition =240+ (difference * kXPositionMultiplier);
[enemyShip setPosition:ccp(xPosition, enemyShip.position.y)];
}
else {
// Run Standard Position Check
[self runStandardPositionCheck:positionIn360 withDiff:difference withEnemy:enemyShip];
}
}
elseif(positionIn360 >337) {
// Run 2
if (enemyShip.yawPosition <23) {
difference
= enemyShip.yawPosition + (360- positionIn360);
int xPosition =240- (difference * kXPositionMultiplier);
[enemyShip setPosition:ccp(xPosition, enemyShip.position.y)];
}
else {
// Run Standard Position Check
[self runStandardPositionCheck:positionIn360 withDiff:difference withEnemy:enemyShip];
}
}
else {
// Run Standard Position Check
[self runStandardPositionCheck:positionIn360 withDiff:difference withEnemy:enemyShip];
}
}

  在这个方法中,我们测试,看是否设备的positionIn360是不是在讨论的3个范围内。在第一个测试中,我们检测positionIn360是不理小于23,如果是,我们就看看是不是有一些敌船在线的另一端(大于337)。

  第二部分测试,看是否positionIn360大于337.如果是的话,就再检测它是否小于23.

  第二部分测试,看敌船是否在23和337之间。如果是,则调用runStandardPositionCheck方法。这个方法的定义如下所示:

-(void)runStandardPositionCheck:(int)positionIn360 withDiff:(int)difference withEnemy:(EnemyShip *)enemyShip {
if (enemyShip.yawPosition > positionIn360) {
difference
= enemyShip.yawPosition - positionIn360;
int xPosition =240- (difference * kXPositionMultiplier);
[enemyShip setPosition:ccp(xPosition, enemyShip.position.y)];
}
else {
difference
= positionIn360 - enemyShip.yawPosition;
int xPosition =240+ (difference * kXPositionMultiplier);
[enemyShip setPosition:ccp(xPosition, enemyShip.position.y)];
}
}

  在这个方法中,我们检查看是否enemyShip是否在设备的positionIn360的左边还是右边。当enemyShip的位置小于positionIn360时,它将出现在屏幕的左边。当enemyShip的位置大于positionIn360,那么它将出现在屏幕的右边。

  现在,你会说,请等一下!你忘了描述这些变量的作用了!好吧,接下来就解释一下。

  如果敌船的yaw坐标值在屏幕的范围之内(从 positionIn360 – 23到 positionIn360 + 23),然后,首先我们计算它位于屏幕的哪一边。如果大于positionIn360,那么就在屏幕的右边,否则就在屏幕的左边。

  difference变量的作用是保存设备的positionIn360和敌船的 yaw位置的角度差值。一旦计算出来后,我们就把这个差值乘以一个任意的倍数。这个倍数代表每一度的像素个数。这个里,我们选择15.

  基于它位于于当前屏幕的位置,我们将把这个计算出来的值增加240或者减去240。

  现在,所有需要的方法已经全部准备就绪啦。

  在init方法的底部,添加下面的代码,在屏幕中增加5个飞船:

// Loop through 1 - 5 and add space ships
enemySprites = [[NSMutableArray alloc] init];
for(int i =0; i <5; ++i) {
EnemyShip
*enemyShip = [self addEnemyShip:i];
[enemySprites addObject:enemyShip];
enemyCount
+=1;
}

  因为,我们添加了敌船到屏幕中了,我们确保它们的位置被更新。在udpate方法的底部添加下面方法:

// Loop through array of Space Ships and check the position
for (EnemyShip *enemyShip in enemySprites) {
[self checkEnemyShipPosition:enemyShip withYaw:yaw];
}

  在我们忘记之前,在dealloc里面添加清理代码来清理我们之前创建的enemySpritesArray数组:

[enemySprites release];

  现在,编译并运行工程吧!当你旋转设备的时候,你将会看到5个飞船在不同的地方。

免费的激光和爆炸

  目前为止,这个现实增加的游戏完成的差不多了。不过,还有一个很严重的问题:这里飞船打中后没什么感觉。

  很明显,我们并不想这样,所以,让我们添加一些很酷的激光和爆炸效果吧。

  在开始之前,让我们移除屏幕上的label--他们只是作为调试时用的。因此,找开 HelloWorldLayer.m中关于labels的代码,并把它们注释掉。完成之后,编译并运行,保证没有错误。

  现在,看看有趣的部分---让我们往游戏中添加一些火力吧!首先,我们将添加一个方法用来判断玩家的开火区域是否击中了飞船。在HelloWorldLayer.h文件中,在@end之前声明下列方法:

- (BOOL) circle:(CGPoint) circlePoint withRadius:(float) radius collisionWithCircle:(CGPoint) circlePointTwo collisionCircleRadius:(float) radiusTwo;

  打开HelloWorldLayer.m,然后在dealloc方法上面实现上述方法:

- (BOOL) circle:(CGPoint) circlePoint withRadius:(float) radius collisionWithCircle:(CGPoint) circlePointTwo collisionCircleRadius:(float) radiusTwo {
float xdif = circlePoint.x - circlePointTwo.x;
float ydif = circlePoint.y - circlePointTwo.y;

float distance = sqrt(xdif*xdif+ydif*ydif);
if(distance <= radius+radiusTwo) return YES;

return NO;
}

  

  这个方法用来检测是否两个点的半径有交集。输入的参数是敌方飞船位置和屏幕的中心点位置。两个点的半径都设置为50.

  首先,我们计算两个点X和Y值的差。接下来计算两点的距离。这个在高中就学过的,叫勾股定理。你可以从这里得到更多的信息。

  接下来,我们往屏幕中添加一个区域,用作火力瞄准器。下载这些资源文件,解压缩,然后把scope.png拖到Resouces文件夹下去。确保“Copy items into destination group’s folder”被复选中,然后点Finish。

  打开HelloWorldLayer.m的init方法,然后在 [self scheduleUpdate]方法之前,添加以下代码:

// Add the scope crosshairs
CCSprite *scope = [CCSprite spriteWithFile:@"scope.png"];
scope.position
= ccp(240, 160);
[self addChild:scope z:
15];

// Allow touches with the layer
[self registerWithTouchDispatcher];

  如果你现在运行程序,你将看到一个瞄准器出现在屏幕的中间。

  非常好,现在让我们添加一些爆炸效果,在玩家点击屏幕的时候就触发。我们将按照添加scope.png的方法一样,来添加Explision.plist。先找到之前下载的本项目资源文件。把Explosion.plist拖到资源文件夹中,确保“Copy items into destination group’s folder”被复选上,然后点击Finish。

  你可能会奇怪这个文件到底是什么?我使用一个很酷的软件制作的,你可能之前也听说过了,叫做 Particle Designer,它是由71 Squared的工程师所开发的。我不会在这里讲解如何使用此软件来制作这样的粒子效果文件,但是,实际上这个过程是非常简单的。选择一种粒子效果,然后调节一些参数,最后导出plist文件就可以了。

  现在,在dealloc方法之前,添加下列代码:

-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint location
= CGPointMake(240,160);

// 1
for (EnemyShip *enemyShip in enemySprites) {
if (enemyShip.timeToLive >0) {
// Check to see if yaw position is in range
BOOL wasTouched = [self circle:location withRadius:50 collisionWithCircle:enemyShip.position collisionCircleRadius:50];

if (wasTouched) {
enemyShip.timeToLive
=0;
enemyShip.visible
=false;
enemyCount
-=1;
}
}
}

// 2
CCParticleSystemQuad *particle = [CCParticleSystemQuad particleWithFile:@"Explosion.plist"];
particle.position
= ccp(240,160);
[self addChild:particle z:
20];
particle.autoRemoveOnFinish
= YES;

// 3
if (enemyCount ==0) {
// Show end game
CGSize winSize = [CCDirector sharedDirector].winSize;
CCLabelBMFont
*label = [CCLabelBMFont labelWithString:@"You win!" fntFile:@"Arial.fnt"];
label.scale
=2.0;
label.position
= ccp(winSize.width/2, winSize.height/2);
[self addChild:label z:
30];
}
}

  这段代码的第一部分用来做碰撞检测,用来测试是否飞船在瞄准器范围之内。如果其中一个飞船被击中了,我们就飞船的属性来隐藏它,同时把enemyCount计数减1.第二部分代码,往屏幕中心添加了一个爆炸粒子系统。第二部分代码,也是最后一部分代码,它用来检查enemyCount是否为0,如果是的话,就显示一个label,告知玩家游戏结束了。

  这个游戏如果就这样的话,有点无聊。所以,让我们往游戏中添加一些基本的AI吧。其实也很简单的,就是随着时间的推移,我们会改变一下飞船的位置。所以,在update方法底部添加下列代码:

// Loop through array of Space Ships and if the timeToLive is zero
// change the yawPosition of the sprite
for (EnemyShip *enemyShip in enemySprites) {
enemyShip.timeToLive
--;
if (enemyShip.timeToLive ==0) {
int x = arc4random() %360;
[enemyShip setPosition:ccp(
5000, 160)];
enemyShip.yawPosition
= x;
enemyShip.timeToLive
= kTimeToLive;
}
}

  这段代码将会遍历所有的enemySprites,然后更新timeToLive属性。然后,检查这个timeToLive属性是否等于0,如果是的话,那么就给飞船的yawPositon设置一个随机值,同时重置timeToLive属性。编译并运行工程吧,现在你想要打中飞船的话就有一些难度了,开火!

Pump up the Volume!

  游戏如果没有声音的话,那就太没意思了!所以,让我们添加一些音乐吧!

  在HellowWorldLayer.m文件顶部包含Simple Audio Engine所需的头文件,具体如下所示:

#import"SimpleAudioEngine.h"

  然后在init方法的最后添加下列代码,记得添加在 if ((self=[super init]))语句内部:

[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"SpaceGame.caf" loop:YES];
[[SimpleAudioEngine sharedEngine] preloadEffect:
@"explosion_large.caf"];
[[SimpleAudioEngine sharedEngine] preloadEffect:
@"laser_ship.caf"];

  

  这里会加载背景音乐,同时预加载音效。

  现在,找到ccTouchesBegan,然后在这个方法的开头添加下列代码:

[[SimpleAudioEngine sharedEngine] playEffect:@"laser_ship.caf"];

  这里会在你点击屏幕的时候播放一个发射激光的音效。

  还是在ccTouchesBegan方法里面,打开遍历enemySprites那个循环,然后在 (wasTouched)的if判断句内添加下列代码:

[[SimpleAudioEngine sharedEngine] playEffect:@"explosion_large.caf"];

  当飞船被打中的时候,将会播放爆炸的音效!

  编译并运行代码,尽情玩吧!

何去何从?

  这里有本教程的完整源代码

  如果你想学习更多有关制作增强现实的游戏的话,下面有一些比较好的资源:

  我希望你在学习这个教程的时候会得到许多快乐!如果有什么建议或意见,请留言,谢谢!

posted on 2011-07-03 22:58  子龙山人  阅读(12299)  评论(12编辑  收藏  举报