Augmented Reality Tutorial for iOS

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

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

In this augmented reality tutorial you will learn how to make a simple augmented reality game for the iPhone/iPod touch!

In this game you will utilize the camera, gyroscope, and Cocos2d framework. Sounds exciting right?

It definitely was fun exploring these technologies while writing this article. There is a bit of math and conversions, but don’t worry – it’s nothing too hard!

To use this augmented reality tutorial, you need an iPhone 4 because the augmented reality tutorial uses the gyroscope to move your view of the world.

You also need to have at least basic knowledge of Cocos2D and have Cocos2D installed. If you are a complete beginner to Cocos2D, you should go through some of the other Cocos2D tutorials on this site first.

Are you ready to start blasting some virtual aliens? Let’s go!

Getting Started

Open Xcode and from the File menu select New\New Project. Next choose the iOS\cocos2d\cocos2d template, and click Next. Name the project ARSpaceships, click Next, choose a folder to save your project in, and click Create.

We will reuse some of the resources from the Space Shooter game, so download them and unzip the file.

Once you have the file downloaded, drag the the Fonts, Sounds, and Spritesheets folders into the Resources group inside your Xcode project. Make sure Copy items into destination group’s folder is checked and ARSpaceships target is checked, then click Finish. At this point your project should look like this:

A default Xcode project with the project resources added

We will use some of these items later on.

Roll the Camera!

If you run the app right now, it is pretty boring. A black screen with Hello World, who hasn’t seen this before! Let’s start turning this into something cool. Select the AppDelegate.h file and add a UIView to the interface.

UIView *overlay;

Now open the AppDelegate.m file. Scroll down to the EAGLView *glView line and change the pixelFormat to kEAGLColorFormatRGBA8. It will now look like this:

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

If you do not change the pixel format the image from the camera will not display. Since this in an augmented reality game, we don’t want that!

Below the [window addSubview: viewController.view]; line of code we will add the following lines of code:

 
// 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];

Here we are setting the background color of the openGLView to clear, setting the view not opaque, setting the glClearColor, and finally creating and adding a UIView named overlay which we will use for the camera.

Next add the following code below the code you just added.

#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];

First we define a constant for the scaling of the camera. The camera has an aspect ratio of 4:3 and the screen of the iPhone has an aspect ratio of 3:4, so we will need to scale the camera image to make up for this discrepancy.

Second, we are creating an UIImagePickerController, setting some properties so it is just the camera with no controls, scaling the UIImagePickerController, and then adding it to the overlay view.

Lastly we need to bring the viewController..view (which contains the Cocos2D display) to the front so it appears in front of the camera view.

At this point we can go ahead and Run the app. Once it loads you will see that the image coming from the camera is now your background with Hello World on top.

The beginnings of an augmented reality app with Cocos2D!

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

Since we now have the reality portion of the app working, next we will focus on the tougher portion of this augmented reality tutorial.

First we need to add the CoreMotion framework to your project. Click on the project file, select the ARSpaceships target, select the Build Phases tab, then expand Link Binary With Libraries.

Adding CoreMotion to Link Binary with Libraries step in Xcode 4

Click the + button, select CoreMotion.framework, then click the Add button. Now we are all set to start using the gyroscope in the project.

Open HelloWorldLayer.h and add the following import statements at the top of the file.

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

Inside the interface add the following variables.

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

Add the property statement below the interface.

@property (nonatomic, retain) CMMotionManager *motionManager;

Now it is time to get into the meat and potatoes of this project. Open the HelloWorldLayer.m file. Inside the if ((self=[super init])) statement which is inside the init method, delete the current lines creating the “Hello World” label and add the following code to setup the new labels.

// 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];

Nothing fancy here, just adding some labels with a font and some text. The positions for the labels are all along the left hand side of the screen.

Next you need to setup the motion manager, which will start the gyroscope.

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

Here we allocate and initialize the motion manager. We also set the update interval to sixty times per second. If the device has the gyroscope capabilities, then start the updates. Finally we schedule the update.

Make sure to add the synthesize statement at the top of this file.

@synthesize motionManager;

Since we have scheduled the update, we will need to add an update method. Add the following code below the init method.

-(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]];
 
}

At this point you can Run the app. You will see the values for the Yaw and positionIn360 change in their respective labels.

How Did That Work?!

Although that worked, you might be wondering how it did! Let’s take a minute and explain the above code section by section.

Start by downloading the FREE Gyrosocope app from the iTunes app store. Run the app, and you’ll see a cool visual representation of what is actually going on when we move the phone around.

Free gyroscope app on iTunes App Store

The value we need to focus on is the Yaw. This is the movement from turning right or left. In the Gyroscope app this is measured in degrees, however the values presented to us from the motion manager is in radians. That is the reason why we needed to use the built-in CC_RADIANS_TO_DEGREES function to perform the conversion.

So in section 1 we are getting the yaw value in radians, converting it to degrees, and assigning it to the yaw variable. Section 2 simply displays this yaw value on the screen. If you run the app, you will see that the values range from 0 to 180 then from -180 back to zero.

Gyroscope yaw values displayed on a number line

If you look at section 3, you are probably wondering what the positionIn360 variable is. Well, this is just a trick we’re going to use to make things simple with placing flying saucers on the screen.

The logic used is very simple. If the yaw value is positive then we do nothing, but if it is negative then we subtract if from 360 (Adding a negative number is the same as subtracting). The final line of code just displays that value on the screen.

Converting yaw values to 0-360 range

If you haven’t already done so, go ahead and run the app and check out how the values change.

Lights, Camera, Action!

Now that we have the groundwork laid for the gyroscope let’s move on to adding space ships! We are going to start out by creating a new file. Ctrl click (left click) on ARSpaceships and choose New File. Select iOS\Cocoa Touch\Objective-C class and click Next. Make sure Subclass of NSObject is selected, and click Next. Name the file EnemyShip.m and click Save.

Replace the contents of EnemyShip.h with:

#import "cocos2d.h"
 
@interface EnemyShip : CCSprite {
    int yawPosition;
    int timeToLive;
}
 
@property (readwrite) int yawPosition;
@property (readwrite) int timeToLive;
 
@end

Replace the contents of EnemyShip.m with:

#import "EnemyShip.h"
 
 
@implementation EnemyShip
 
@synthesize yawPosition, timeToLive;
 
-(id)init {
    self = [super init];
    if (self){ 
        yawPosition = 0;
        timeToLive = 0;
	}
    return self;
}
 
@end

Now open back up HelloWorldLayer.h. Add the import statement for the EnemyShip at the top of the file.

#import "EnemyShip.h"

Inside the interface add:

NSMutableArray *enemySprites;
int enemyCount;
CCSpriteBatchNode *batchNode;

Finally below the interface, add the property for the enemyCount and the definition of methods:

 
@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;

Jump over to the HelloWorldLayer.m file, and make the following modifications to the file:

// 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"];

In these lines we are loading the spritesheet we added to our project at the beginning of this tutorial.

Next we will add the method to create new enemy space ships. Above the dealloc method add the following code.

-(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;
}

This method accepts an integer value for the tag and returns an EnemyShip CCSprite. The next line of code we are creating an EnemyShip sprite from the spritesheet. Next we are using the arc4random method to get a random integer from 0 to 360. Finally we set the position of the ship, set the timeToLive value to 100, adding the ship to the batch node, and returning the ship.

Below the addEnemyShip method add the following code for the checkEnemyShipPosition method:

-(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];
        } 
    }
}

This method might seem a little bit confusing with the alternate, min, and max ranges. Don’t worry it is pretty simple. First we start out by checking the yaw position of the device (positionIn360) and placing it in the range from 0 to 360 (think full circle).

Gyroscope yaw position visualized on iPhone

Since we have two ends to our number line of 0 to 360, we will need to check to see if the device’s positionIn360 is on either end. We use 23 as an arbitrary number representing the number of degrees that will show on the half of the screen.

Visible degrees on iPhone

So we only need to worry about the ranges from 0 to 23 and 337 to 360 because the other end of the line will need to wrap around.

Lastly we update the position of the enemy space ship if it is in the 46-degree range of the screen. The (checkAlternateRange) if statement is used for determining when to update the position of the enemy spaceship.

If checkAlternateRange is true, then we check to see if the enemy spaceship’s position falls within the min and max range. All of the checks in the first part of this if statement may seem extreme, but if we walk through it using values it makes perfect sense. Let’s assume:

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

Because we have to account for both ends of the number line, our min range is greater than the max range. Now we do all of the checks and find out that the enemy ship’s position is greater than rangeMin so we will display the ship on the screen.

The else in that if statement is more straightforward. It just checks to see if the enemy ship’s position is within the min and max range.

What a great segway into the update method! Add the following code below the checkEnemyShipPosition method.

-(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];
    }
}

In this method we are testing to see if the device’s positionIn360 is in one of the three ranges. In the first test we look to see if the positionIn360 is less than 23, if so we want to check to see if there are any enemy ships on the other end of the line (greater than 337).

The second test we look to see if the positionIn360 is greater than 337. If so we want to do the exact opposite of what we just did (check if the enemy ship is less than 23).

The third test (final outer else) we look to we set the position for the enemy ship if it falls between 23 and 337. We are calling the method runStandardPositionCheck. Place the following code below the last method.

-(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)];
    }
}

In this method we check to see if the enemyShip position is to the left or right of the device’s positionIn360. When the enemyShip position is less than the positionIn360, it appears on the left side of the screen. When the enemyShip position is greater than the positionIn360 it appears on the right.

Now you say wait a minute! You forgot the difference variable and describing what it does. Okay, here it goes.

If the enemy ship’s yaw position is in the screen’s range (from positionIn360 – 23 to positionIn360 + 23), then first we figure out which side of the screen it is on. If it is greater than the positionIn360 then it goes on the right side of the screen, else it goes on the left.

The difference variable is used to measure the degrees of difference between the device’s positionIn360 and the yaw position of the enemy ship. Once that is known, we multiply the difference by an arbitrary multiplier. This multiplier represents the amount of pixels for each degree. In this case we choose 15.

Based on which side of the screen we will add or subtract this calculated value from 240(screen width divided by 2). And that’s it for the updateEnemyShipPosition method.

Now that all of the required methods are in place we will move on to calling those methods.

At the bottom of the init method, add the following code to add five enemy space ships on the screen.

// 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;
}

Since we added the enemy space ships to the screen, we need to make sure their positions update. At the very end of the update method add the following code:

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

And before we forget, add the following to the bottom of your dealloc method to clean up the enemySpritesArray we created in init:

[enemySprites release];

Now for the moment of truth, go ahead and run your app! You will see 5 space ships at different locations when you rotate the device around.

A simple augmented reality game for the iPhone!

Gratuitous Lasers and Explosions!

So far our augmented reality game is coming along really well, except there’s one major problem: those darn spaceships are getting away scott free!

Obviously we can’t have that, so let’s add some awesome lasers and explosions.

Before we start, let’s get rid of the labels that are on the screen – those were just there for debugging purposes. So search through HelloWorldLayer.m and comment out all matches for yawLabel and posIn360Label. Once you’re done, compile and run and make sure everything still works ok.

Now for the fun part – let’s add some firepower to the game! First we will add a method to check if the player’s firing area hits a space ship. Inside the HelloWorldLayer.h file add the following line of code before @end.

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

Moving on to the HelloWorldLayer.m add the method above the 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;
}

This method is used to check if the radii of two points overlap. The input parameters are for the position of the enemy spaceship and the center of the screen. The radii for both points are set to 50.

First we find the difference between the two points for both x and y. Next we calculate the distance. You may remember this calculation from your studies, it’s called the Pythagorean Theorem. You can read more about this here.

Next we will add a scope to the screen so we can see where our firepower be aimed at. Download the resources for this project, unzip the file, and drag scope.png into your project under the Resources folder. Make sure the “Copy items into destination group’s folder” is checked then click Finish.

Find the init method in HelloWorldLayer.m and add the following code to setup that sprite just before [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];

If you run the app now, you will see the scope crosshairs in the center of the screen.

A scope added to the augmented reality iPhone game

Great, now let’s add some explosions when the player taps the screen. We will follow the same steps to add Explosion.plist as we did for scope.png. From the resources for the project that you downloaded earlier, drag Explosion.plist into the Resources folder in Xcode, make sure the “Copy items into destination group’s folder” is checked, and click Finish.

You may be wondering what this file is. I used an awesome program to create it, which you may have heard of. It is called Particle Designer, from the good folks of 71 Squared. I will not cover how to create these files, but it is as easy as selecting a type of particle system, making adjustments, and exporting it to a plist.

Now, right before the dealloc method add the following code.

-(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];
    }
}

The first section of this code uses the collision detection method we added earlier to check if a space ship is inside of the scope. If one of the space ships was shot, then we will set some properties on the ship to hide it and reduce the enemyCount variable by one. The second section adds the particle system explosion in the center of the screen. The third and final section checks to see if the enemyCount variable equals zero, and if it does, it displays a label to inform the player that the game is over.

The game is a little bit boring at this point in time, so let’s add some very basic AI to change up the position of the space ships after a certain amount of time. At the bottom of the update method add the following code.

// 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;
        }
    }

This code will loop through the enemySprites array and update the timeToLive property. Then it will check to see if that property is equal to zero, if it is then it will assign the ship a different random yawPosition and reset the timeToLive. Go ahead and run the game. The game is now much harder to track those space ships down and shoot them!

Pump up the Volume!

Games without audio can seem very boring, so let’s spice things up!

At the top of HellowWorldLayer.m add the import statement for the Simple Audio Engine, which is included with Cocos2D.

#import "SimpleAudioEngine.h"

Scroll down the file and add the following code to the bottom of the init method just before the end of the if ((self=[super init])) statement.

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

This will load the background music and preload the effects.

Now, continue scrolling down the file and locate the ccTouchesBegan method. At the top of this method add the following line of code.

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

This will play a laser sound effect when the user touches the screen.

Stay inside of the ccTouchesBegan method. In the enemyShip in enemySprites for loop, add the following line inside the (wasTouched) if statement.

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

This will play the explosion sound effect when a ship is hit!

Compile and run your code, and enjoy your new tunes!

Where To Go From Here?

Here is the sample project for the augmented reality game we made in the above tutorial.

If you want to learn more about making augmented reality games, here are some great resources to check out:

I hope you had as much fun reading the article as I did creating it! If you have any questions or suggestions for others learning about augmented reality, please join in the forum discussion below!

Nick Waynik

Nick Waynik is an independent iOS developer, and has done everything from network administration to web development. He started writing iOS apps when the SDK was first released. Since then he has gone on to start his own business focusing on iOS development. He loves spending his free time with his wife Allison and son Preston, and sometimes playing golf.

He can be found on Twitter as @n_dubbs, or at his website.

User Comments

81 Comments

[ 1 , 2 , 3 , 4 , 5 , 6 ]
  • Hi guys...im a big fan of your tutorials and i have been waiting for a long time for someone to come up with such detailed and easy-to-understand guides on how to work with iOS and Android. I've been watching the Augmented reality tutorial and i've been wondering if you ever considered to make a augmented reality tutorial for 3D objects and animations (i have a project at school and there is not much about augmenting a 3D model with animation on the WEB at least not tutorial-wise) so i don't know where to start.

    PLease help.Thanks in advance.

    Keep up the good work.
    MephistoFloyd
  • Love the tutorial. I have a question that I hope you can help with:
    How can I move the enemyShip to yaw position 0 ?

    Can I use a CCAction?

    Thanks, David
    David A
  • Hey guys,

    This is a great tutorial, perfect for what I need in my game! I had a question about the [window bringSubviewToFront:viewController.view] line, because Xcode gives me an error there. How is it possible to use view controllers in cocos2d? Doesn't it use layers instead of views? Is there another way to bring the main layer to the front? Thanks in advance for your help!
    drew
  • Hi,
    nice tutorial, i had a question,
    How to take screenshot of device camera and CCLayer with image at the same time?
    I've tried many things, but I could not.
    Yildirim
  • Are there any changes or best tips for trying this with Xcode 5.1 and Cocos2D 3.0? I can't get this to compile.

    Thanks! -chris
    sirhcmcd
  • Hello

    i check this tutorial and try to install this demo application in device but at that time camera not opens in the background and i only able to see the black screen.here Enemy ships and come and all work well but in the background only the black screen display so what is the issues with this in the tutorial.

    please update me regarding that.

    Thanks
    Hardik
    hardik.sptechnolab
[ 1 , 2 , 3 , 4 , 5 , 6 ]

Other Items of Interest

Ray's Monthly Newsletter

Sign up to receive a monthly newsletter with my favorite dev links, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

Hang Out With Us!

Every month, we have a free live Tech Talk - come hang out with us!


Coming up in July: Facebook Pop Tech Talk!

Sign Up - July

RWDevCon Conference?

We are considering having an official raywenderlich.com conference called RWDevCon in DC in early 2015.

The conference would be focused on high quality Swift/iOS 8 technical content, and connecting as a community.

Would this be something you'd be interested in?

    Loading ... Loading ...

Our Books

Our Team

Tutorial Team

  • Charlie Fulton
  • Jack
  • Dani Arnaout

... 50 total!

Update Team

  • Riccardo D'Antoni

Editorial Team

  • Matt Galloway

... 23 total!

Code Team

  • Orta Therox

... 1 total!

Translation Team

  • Zihan Xu
  • Heejun Han
  • David Hidalgo

... 33 total!

Subject Matter Experts

... 4 total!