Cocos2D Tutorial for iOS: How To Make A Space Shooter iPhone Game
A Cocos2D tutorial for iOS that will teach you how to make a space shooter iPhone game. By Ray Wenderlich.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Cocos2D Tutorial for iOS: How To Make A Space Shooter iPhone Game
30 mins
- Install Cocos2D
- Hello, Cocos2D!
- Adding Resources
- Adding a Space Ship
- Adding Parallax Scrolling
- Continuous Scrolling
- Adding Stars
- Moving the Ship with the Accelerometer
- Adding Asteroids
- Shooting Lasers
- Basic Collision Detection
- Win/Lose Detection
- Gratuitous Music and Sound Effects
- Want More?
- Where To Go From Here?
Adding Parallax Scrolling
We have a cool space ship on the screen, but it looks like it’s just sitting there! Let’s fix this by adding some cool parallax scrolling to the scene.
But wait a minute – what in the heck is parallax scrolling?!
Parallax scrolling is just a fancy way of saying “move some parts of the background more slowly than the other parts.” If you’ve ever played SNES games like Act Raiser, you’ll often see this in the background of the action levels.
It’s really easy to use parallax scrolling in Cocos2D. You just have to do three steps:
- Create a CCParallaxNode, and add it to the layer.
- Create items you wish to scroll, and add them to the CCParallaxNode with addChild:parallaxRatio:positionOffset.
- Move the CCParallaxNode to scroll the background. It will scroll the children of the CCParallaxNode more quickly or slowly based on what you set the parallaxRatio to.
Let’s see how this works. Start by opening HelloWorldLayer.h, and add the following inside the @interface:
CCParallaxNode *_backgroundNode;
CCSprite *_spacedust1;
CCSprite *_spacedust2;
CCSprite *_planetsunrise;
CCSprite *_galaxy;
CCSprite *_spacialanomaly;
CCSprite *_spacialanomaly2;
Then switch to HelloWorldLayer.m, and add the following to the bottom of your init method:
// 1) Create the CCParallaxNode
_backgroundNode = [CCParallaxNode node];
[self addChild:_backgroundNode z:-1];
// 2) Create the sprites we'll add to the CCParallaxNode
_spacedust1 = [CCSprite spriteWithFile:@"bg_front_spacedust.png"];
_spacedust2 = [CCSprite spriteWithFile:@"bg_front_spacedust.png"];
_planetsunrise = [CCSprite spriteWithFile:@"bg_planetsunrise.png"];
_galaxy = [CCSprite spriteWithFile:@"bg_galaxy.png"];
_spacialanomaly = [CCSprite spriteWithFile:@"bg_spacialanomaly.png"];
_spacialanomaly2 = [CCSprite spriteWithFile:@"bg_spacialanomaly2.png"];
// 3) Determine relative movement speeds for space dust and background
CGPoint dustSpeed = ccp(0.1, 0.1);
CGPoint bgSpeed = ccp(0.05, 0.05);
// 4) Add children to CCParallaxNode
[_backgroundNode addChild:_spacedust1 z:0 parallaxRatio:dustSpeed positionOffset:ccp(0,winSize.height/2)];
[_backgroundNode addChild:_spacedust2 z:0 parallaxRatio:dustSpeed positionOffset:ccp(_spacedust1.contentSize.width,winSize.height/2)];
[_backgroundNode addChild:_galaxy z:-1 parallaxRatio:bgSpeed positionOffset:ccp(0,winSize.height * 0.7)];
[_backgroundNode addChild:_planetsunrise z:-1 parallaxRatio:bgSpeed positionOffset:ccp(600,winSize.height * 0)];
[_backgroundNode addChild:_spacialanomaly z:-1 parallaxRatio:bgSpeed positionOffset:ccp(900,winSize.height * 0.3)];
[_backgroundNode addChild:_spacialanomaly2 z:-1 parallaxRatio:bgSpeed positionOffset:ccp(1500,winSize.height * 0.9)];
Compile and run your project, and you should see the start of a space scene:
However this isn’t very interesting yet, since nothing is moving!
To move the space dust and backgrounds, all you need to do is move the parallax node itself. For every Y points we move the parallax node, the dust will move 0.1Y points, and the backgrounds will move 0.05Y points.
To move the parallax node, you’ll simply update the position every frame according to a set velocity. Try this out for yourself by making the following changes to HelloWorldLayer.m:
// Add to end of init method
[self scheduleUpdate];
// Add new update method
- (void)update:(ccTime)dt {
CGPoint backgroundScrollVel = ccp(-1000, 0);
_backgroundNode.position = ccpAdd(_backgroundNode.position, ccpMult(backgroundScrollVel, dt));
}
Compile and run your project, and things should start to scroll pretty neatly with parallax scrolling!
However, after a few seconds goes by, you’ll notice a major problem: we run out of things to scroll through, and you end up with a blank screen! That would be pretty boring, so let’s see what we can do about this.
Continuous Scrolling
We want the background to keep scrolling endlessly. The strategy we’re going to take to do this is to simply move the background to the right once it has moved offscreen to the left.
One minor problem is that CCParallaxNode currently doesn’t have any way to modify the offset of a child node once it’s added. You can’t simply update the position of the child node itself, because the CCParallaxNode overwrites that each update.
However, I’ve created a category on CCParallaxNode that you can use to solve this problem, which you can find in the resources for this project in the Classes folder. Drag CCParallaxNode-Extras.h and CCParallaxNode-Extras.m into your project, make sure “Copy items into destination group’s folder” is checked, and click Finish.
Then make the following changes to HelloWorldLayer.m to implement continuous scrolling:
// Add to top of file
#import "CCParallaxNode-Extras.h"
// Add at end of your update method
NSArray *spaceDusts = [NSArray arrayWithObjects:_spacedust1, _spacedust2, nil];
for (CCSprite *spaceDust in spaceDusts) {
if ([_backgroundNode convertToWorldSpace:spaceDust.position].x < -spaceDust.contentSize.width) {
[_backgroundNode incrementOffset:ccp(2*spaceDust.contentSize.width,0) forChild:spaceDust];
}
}
NSArray *backgrounds = [NSArray arrayWithObjects:_planetsunrise, _galaxy, _spacialanomaly, _spacialanomaly2, nil];
for (CCSprite *background in backgrounds) {
if ([_backgroundNode convertToWorldSpace:background.position].x < -background.contentSize.width) {
[_backgroundNode incrementOffset:ccp(2000,0) forChild:background];
}
}
Compile and run your project, and now the background should scroll continuously through a cool space scene!
Adding Stars
No space game would be complete without some stars flying by!
We could create another image with stars on it and add that to the parallax node like we have with the other decorations, but stars are a perfect example of when you'd want to use a particle system.
Particle systems allow you to efficiently create a large number of small objects using the same sprite. Cocos2D gives you a lot of control over configuring particle systems, and Particle Designer is a great way to visually set these up.
But for this tutorial, I've already set up some particle effects for some stars racing from right to left across the screen that we can use. Simply add the following code to the bottom of the init method to set these up:
NSArray *starsArray = [NSArray arrayWithObjects:@"Stars1.plist", @"Stars2.plist", @"Stars3.plist", nil];
for(NSString *stars in starsArray) {
CCParticleSystemQuad *starsEffect = [CCParticleSystemQuad particleWithFile:stars];
[self addChild:starsEffect z:1];
}
By adding the particle systems to the layer, they automatically start running. Compile and run to see for yourself, and now you should see some stars flying across the scene!
Moving the Ship with the Accelerometer
So far so good, except this wouldn't be much of a game unless we can move our space ship!
We're going to take the approach of moving the space ship via the accelerometer. As the user tilts the device along the X-axis, the ship will move up and down.
This is actually pretty easy to implement, so let's jump right into it. First, add an instance variable inside the @interface in HelloWorldLayer.h to keep track of the points per second to move the ship along the Y-axis:
float _shipPointsPerSecY;
Then, make the following changes to HelloWorldLayer.m:
// 1) Add to bottom of init
self.isAccelerometerEnabled = YES;
// 2) Add new method
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
#define kFilteringFactor 0.1
#define kRestAccelX -0.6
#define kShipMaxPointsPerSec (winSize.height*0.5)
#define kMaxDiffX 0.2
UIAccelerationValue rollingX, rollingY, rollingZ;
rollingX = (acceleration.x * kFilteringFactor) + (rollingX * (1.0 - kFilteringFactor));
rollingY = (acceleration.y * kFilteringFactor) + (rollingY * (1.0 - kFilteringFactor));
rollingZ = (acceleration.z * kFilteringFactor) + (rollingZ * (1.0 - kFilteringFactor));
float accelX = acceleration.x - rollingX;
float accelY = acceleration.y - rollingY;
float accelZ = acceleration.z - rollingZ;
CGSize winSize = [CCDirector sharedDirector].winSize;
float accelDiff = accelX - kRestAccelX;
float accelFraction = accelDiff / kMaxDiffX;
float pointsPerSec = kShipMaxPointsPerSec * accelFraction;
_shipPointsPerSecY = pointsPerSec;
}
// 4) Add to bottom of update
CGSize winSize = [CCDirector sharedDirector].winSize;
float maxY = winSize.height - _ship.contentSize.height/2;
float minY = _ship.contentSize.height/2;
float newY = _ship.position.y + (_shipPointsPerSecY * dt);
newY = MIN(MAX(newY, minY), maxY);
_ship.position = ccp(_ship.position.x, newY);
Let's go over this bit-by-bit:
- Adding this line sets the layer up to receive the accelerometer:didAcccelerate callback.
- The first part of this method comes directly from Apple sample code, to filter the accelerometer values so it's not so "jiggly". Don't worry if you don't understand this, all you really need to know is that it makes things more smooth. If you're insatiably curious here's some info though. Anyway, after we run the filter, we test to see how much it's tilted. A rotation of -0.6 along the x-axis is considered "baseline", and the closer it gets to 0.2 in either direction, the faster the ship is set to move. These values were all gotten via experimentation and what "felt right"!
- Sets the position of the ship based on the points per second to move along the Y axis computed earlier, and the delta time since last update.
Compile and run your project (on your iPhone, the accelerometer does not work on the Simulator), and now you should be able to move the spaceship up and down by tilting your iPhone!