(译)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];
}
}
else if (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 " ];

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

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

何去何从?

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ARKit by Tutorials: Building Augmented Reality Apps in Swift 4.2, 2nd Edition by Chris Language English | 2019 | ISBN: 1942878711 | 496 Pages | True PDF, CODE | 1623 MB Learn ARKit, Apple’s New Augmented Reality Development Framework! With ARKit, you can create immersive, engaging experiences by mixing virtual 2D and 3D content with the live camera feed of the world around you. If getting started with this new framework sounds difficult and time-consuming, don’t worry, we’ve got you covered. ARKit by Tutorials helps you learn ARKit the quick and easy way: by following fun and easy-to-read tutorials. This book is for intermediate to advanced developers who want to create ARKit apps and learn about the intricacies of developing for Augmented Reality on iOS. Topics Covered in ARKit by Tutorials Introduction to ARKit: Get a basic introduction to Augmented Reality and the ARKit framework concepts. Session Management: Discover what’s required to manage an AR session properly; this includes error handling and tracking issues. Adding Objects: Learn how to place 3D Objects into augmented space. Materials & Lighting: Explore the concepts of Physical Based Rendering, textures and light estimation. Creating 3D Assets: Get creative with the SceneKit editor and make your own 3D assets right inside Xcode. Face-based Apps: Find out what it takes to develop and ship Face-Based AR apps. And much, much more! After reading this book, you’ll be prepared to take advantage of the new ARKit framework and create your own AR-based games and apps.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值