[转载](译)iPhone上面的现实增强(Augmented Reality )入门教程 – 子龙山人 – 博客园.
免责申明(必读!):本博客提供的所有教程的翻译原稿均来自于互联网,仅供学习交流之用,切勿进行商业传播。同时,转载时不要移除本申明。如产生任何纠纷,均与本博客所有人、发表该翻译稿之人无任何关系。谢谢合作!
原文链接地址: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 #import
然后在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 // 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个飞船在不同的地方。
<img src="http://pic002.cnblogs.com/images/2011/283130/2011070222393336.jpg" alt="" /> <h2>免费的激光和爆炸</h2> 目前为止,这个现实增加的游戏完成的差不多了。不过,还有一个很严重的问题:这里飞船打中后没什么感觉。 很明显,我们并不想这样,所以,让我们添加一些很酷的激光和爆炸效果吧。 在开始之前,让我们移除屏幕上的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
我希望你在学习这个教程的时候会得到许多快乐!如果有什么建议或意见,请留言,谢谢!