免责申明(必读!):本博客提供的所有教程的翻译原稿均来自于互联网,仅供学习交流之用,切勿进行商业传播。同时,转载时不要移除本申明。如产生任何纠纷,均与本博客所有人、发表该翻译稿之人无任何关系。谢谢合作!
原文链接地址:http://www.raywenderlich.com/3997/introduction-to-augmented-reality-on-the-iphone
教程截图:
在这篇教程中,你将学习到如何为你的iphone和ipod touch制作一个简单的增强现实游戏。
在这个游戏中,你将使用到摄像头,陀螺仪和cocos2d框架。听起来很振奋人心吧?
在写作这篇教程的时候,探索上面提到的一些技术的过程真的是非常有趣。这里有一些数学和转换,不过不要担心---没有什么事情是很难的!
学习这篇教程的时候,你需要一个iPhone4,因为这个教程使用陀螺仪来移动游戏世界视图。
你也需要一些基本的cocos2d方面的知识,当然要安装好cocos2d。如果你对于cocos2d完全陌生的话,你可以先看看本博客上的其它cocos2d教程。
你准备好爆头一些虚拟外星人了吗?跟我来吧!
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 " ];
当飞船被打中的时候,将会播放爆炸的音效!
编译并运行代码,尽情玩吧!
何去何从?
这里有本教程的完整源代码。
如果你想学习更多有关制作增强现实的游戏的话,下面有一些比较好的资源:
- 更多关于摄像头的纵横比的:1 2 3
- Apple’s documentation on UIImagePickerController Class
- Apple’s documentation on Core Motion
我希望你在学习这个教程的时候会得到许多快乐!如果有什么建议或意见,请留言,谢谢!
转自: http://www.cnblogs.com/andyque/archive/2011/07/03/2096510.html