The Complete iOS Game Course - Build a Flappy Bird Clone

Over 350+ videos taking you step-by-step through process of building 8+ different applications using Sprite Kit
4.1 (66 ratings) Instead of using a simple lifetime average, Udemy calculates a
course's star rating by considering a number of different factors
such as the number of ratings, the age of ratings, and the
likelihood of fraudulent ratings.
5,014 students enrolled
$20
Take This Course
  • Lectures 349
  • Contents Video: 30 hours
    Other: 11 mins
  • Skill Level All Levels
  • Languages English
  • Includes Lifetime access
    30 day money back guarantee!
    Available on iOS and Android
    Certificate of Completion
Wishlisted Wishlist

How taking a course works

Discover

Find online courses made by experts from around the world.

Learn

Take your courses with you and learn anywhere, anytime.

Master

Learn and practice real-world skills and achieve your goals.

About This Course

Published 4/2014 English

Course Description

We built an immersive game development course that teaches you all of the fundamentals so you can build your own games on iPhones and iPads. We combine videos, notes, collaborative discussion forums, and challenging assignments to have students build real apps. Teaching online is not new to us. Our highly successful Beginner iOS Bootcamp (over 18k students and 5 star review) teaches students the basics of programming iOS apps. We use the same proven pedagogy to teach game development.

The course will include over 120 hours of content (30 hours of video plus notes and challenges), an active discussion forum where students and teachers answer questions, and multiple fully coded example projects that you can download.

Topics covered:

  • Introduction to Game Development , Sprite Kit , Scenes and Nodes, Coordinates, Game Loop
  • Sprite/Texture Creation, Sprite Theory, Atlas Files
  • Animation, Parallax Scrolling, Character animation
  • Physics Bodies, Physics Properties, Bit Fields, Collisions, Contact Response
  • User Input, Input Handler
  • Character Movement/Physics, State machines, Player Physics, Jump Curve, Double Jump, Boost, etc.
  • Tile Based Game World, Tile Sprites, Tiled + Kobold Kit, Creating and Loading Tiled files
  • Collision Detection, Axis Alined Bounding Box, Collision Bit Masks
  • Obstacles & Enemies, Basic Enemy AI
  • Collectables, Power Ups
  • Effects, Particle Effects
  • Sound, Adding Music, Adding Sound Effects
  • Scoring, HUD, Stats/Progress Tracking, Score Board
  • Game Menus, Saving/Loading Game Progression

Reviews from our iOS course

"I must say that so far, this course is awesome. Having the challenging assignments, daily discussions and feedback from the instructors, has been the missing piece that I have been looking for. I have read a handful of books, watched hours of video & typed in a bunch of tutorials, and finally, having to work through tough assignments and applying what I have been learning, it is all starting to click - Finally!" - Mark S.

“Code Coalition's discussion board is one of the best resources for a beginning iOS developer. So much help being offered” - Omar S.

“I've just completed the iOS101 course, which I thought was a great intro to the XCode environment... I feel it's been well worth the investment. ” - Herdy H.

"Wow, @CodeCoalition! Moving quickly! Week 2 is so different than week 1...and I like it :) Thnx for the challenge" - Melissa B.

“Just discovered @CodeCoalition! An excellent resource for anyone who wants to make their first iPhone app.” -Novall K.

“Can't reiterate it enough how this course is helping me with my iOS dev skills. I think using protocols and delegation is finally becoming second nature. Fingers crossed :-)” -Alex P.

“I am really loving the class. I have taken classes at Code School & Treehouse and both were missing a key element. The ability to ask questions as you complete a section and get an answer. “ -Lisa A.

“Your training is the best out there so far. I wish I had the time away from regular job to follow along.” -Christian S.

“Im loving this.. I have been through at least 5 books, and many online deals. Yours is super so far. Finally, i can get from start to finish on a lesson without wondering why on "Everything" thank youCant wait to do more.. “ -Kevin R.

Why take this course and how much time does it take to complete?

iOS game development, and software development as a whole, is a booming career. The demand for new developers at all levels far outweighs the supply. Barrier to entry is not as high as one would think - though passion and hard work are necessary, a degree in computer science or any other technical field is not required.

We aim to take you from complete beginner to junior game developer in three months. The apps you create in this course can be used on your portfolio so you have something to show when you apply for jobs. Not only that, you can throw your apps on the app store and be the next Flappy Bird!

What are the requirements?

  • Familiarity with object-oriented programming (does not need to be Objective-C)
  • An intel-based Mac that has Mac OS X Snow Leopard or later installed

What am I going to get from this course?

  • Build iOS Games using Sprite Kit
  • Use the Objective-C language to build iOS games
  • Understand game physics and mechanics

What is the target audience?

  • Novice or advanced programmers who are interested in game development
  • Game programmers from other languages/frameworks

What you get with this course?

Not for you? No problem.
30 day money back guarantee.

Forever yours.
Lifetime access.

Learn on the go.
Desktop, iOS and Android.

Get rewarded.
Certificate of completion.

Curriculum

Section 1: Sprite Kit Concepts
01:38

Project Repositories and Resources

We will be posting additional course information here. During the course if you find yourself unable to get a bit of code to work you can find the completed code repositories here:

Main Space Cannon repo:

Bomb power up challenge:

Multi power up challenge:

Music On/Off button challenge:

Bouncing Ball challenge:

Alien Animation Challenge:

Scrolling Challenge:

Tappy Plane:

08:57

Nodes

Code

In this video, we use the following code to update the spaceship's rotate action to instead make it move forward:

SKAction *action = [SKAction moveByX:0.0f y:200.0f duration:1];
Links

Apple's introduction to Sprite Kit: Sprite Kit Programming Guide: About Sprite Kit

06:54
A Closer Look at Nodes

In this video we talk about nodes in a bit more detail. We discuss a few of the different types of nodes that Sprite Kit makes available to us as well as many of the properties and methods that make up the SKNode class. We run through an example of creating a colored rectangle using SKSpriteNode and see how adding it as a child to different nodes in our node tree affects how it behaves.

Links

* SKNode Class Reference

04:44
A Closer Look At Nodes

In this video we talk about nodes in a bit more detail. We discuss a few of the different types of nodes that Sprite Kit makes available to us as well as many of the properties and methods that make up the SKNode class. We run through an example of creating a colored rectangle using SKSpriteNode and see how adding it as a child to different nodes in our node tree affects how it behaves.

Links

* SKNode Class Reference

05:21
Adding Images to Projects

This video is just a refresher on how to add images to your Xcode projects. If you are familiar with doing this then you may want to skip this video. Graphics are a big part of games, and so we'll often be adding images to our projects throughout the course. Whenever we add new images in a video, you'll be able to find the images featured in that video for download as part of that video's notes.

* Space Backgrounds by Rawdanitsu

11:04

Coordinates

Having a solid understanding of how your game assets are positioned within the coordinate systems of the various nodes and scenes within your game is important for developing code that does exactly what you expect it to. In this video we explore how nodes can effect each others positions depending on how they are organised in the hierarchy of your scene's node tree and we look at how we can make adjustments to the coordinate systems of our nodes by changing the AnchorPoint property.

Be sure that you understand the positioning relationships between nodes by spending a bit of time playing with them. Try building up a node tree with a number of levels by adding nodes as children of children. Notice how this effects the order in which your nodes are drawn. Also note that position and rotation are not the only properties that a node passes along to its descendants. Other properties that are passed down the tree include scale, alpha, hidden and speed.

Suggested Exercise

Change the action on the spaceship back to the original rotation action of the default Sprite Kit template and then see if you can adjust the anchor point of the spaceship and positions of the flame nodes such that the spaceship rotates around its nose with its flames still in the correct positions.

Code

In this video we use the following code to add the two flames to our spaceship:

SKSpriteNode *leftFlame = [SKSpriteNode spriteNodeWithImageNamed:@"Flame"];
leftFlame.position = CGPointMake(-12, -109);
[sprite addChild:leftFlame];
SKSpriteNode *rightFlame = [SKSpriteNode spriteNodeWithImageNamed:@"Flame"];
rightFlame.anchorPoint = CGPointMake(0.5, 1.0);
rightFlame.position = CGPointMake(12, -87);
[sprite addChild:rightFlame];

Links

Apple's documentation on coordinates, anchor points, node tree, draw order, etc. Well worth having a glance at: Sprite Kit Programming Guide: Building Your Scene

06:20
A Closer Look at Anchor Points

In this video we discuss anchor points in a bit more detail and experiment with some code in the standard Sprite Kit template to see how changing the anchor point property of the space ship, alters the positioning of the sprite image and affects how it rotates.

04:06

Game Loops

The game loop is at the core of just about every game. The state of our game needs to be updated over time, and the game loop is responsible for driving this process. Our game loop will run once per frame of animation and each time it runs we need to process all aspects of our game, e.g. player input, artificial intelligence, physics, scoring etc. When developing games, getting into the mindset of the game loop can help when thinking about how to solve certain problems as it's important that we know when different events will occur throughout the processing of a frame. Developing a solid game loop can be challenging, and fortunately, Sprite Kit has already done that work for us and so we don't need implement our own game loop. In this video we have a quick look at the Sprite Kit game loop and see how different aspects of our game state are processed throughout a frame.

Links

A breakdown of how a Sprite Kit scene processes a frame of animation: Sprite Kit Programming Guide: Advanced Scene Processing

An in depth discussion of game loops as a pattern. In Sprite Kit the design of the game loop has already been done for us, so this link only serves as further (somewhat advanced) reading if you are interested: Game Programming Patterns: Game Loop

06:24
Overview of Actions

Actions in Sprite Kit allow us to relatively easily add movement and animation to the nodes in our game. In this video we discuss actions and the tasks things we can use them for and how we implement them.

02:57

Performance

The performance of our game is obviously important, as we want our games to run as smoothly as possible. In order to make sure that we're achieving this goal, we need some sort of benchmark that we can keep track of. The frame rate is the most fundamental benchmark we can use for this purpose. If we see our frame rate drop when a particularly complex event occurs in our game, it could be a clue to us that we might need to do some optimizing in that area. Sprite Kit can report the frame rate to us along with a couple of other basic pieces of diagnostic information about how our scene is being processed. In this video we'll have a quick discussion how this info can help us when considering our game's performance.

Section 2: Our First Game! Build a Space Cannon Shooter
09:39

Our First Game

With the basics out of the way, it's time to get stuck into creating a real game! Over the next couple of sections we are going to produce a physics based space shooter. We'll be covering a number of different concepts and Sprite Kit features along the way and at the end of it, you'll have a fun little game to play with. There'll be plenty of room for you to expand the game as well, if you'd like to take it further.

Code

In this video we declare two class level variables:

SKNode *_mainLayer;
SKSpriteNode *_cannon;

We setup our cannon and get it rotating using the following code:

// Add background.
SKSpriteNode *background = [SKSpriteNode spriteNodeWithImageNamed:@"Starfield"];
background.position = CGPointZero;
background.anchorPoint = CGPointZero;
background.blendMode = SKBlendModeReplace;
[self addChild:background];
// Add main layer.
_mainLayer = [[SKNode alloc] init];
[self addChild:_mainLayer];
// Add cannon.
_cannon = [SKSpriteNode spriteNodeWithImageNamed:@"Cannon"];
_cannon.position = CGPointMake(self.size.width * 0.5, 0.0);
[_mainLayer addChild:_cannon];
// Create cannon rotation actions.
SKAction *rotateCannon = [SKAction sequence:@[[SKAction rotateByAngle:M_PI duration:2],
[SKAction rotateByAngle:-M_PI duration:2]]];
[_cannon runAction:[SKAction repeatActionForever:rotateCannon]];

Links

Not sure how radians work? Wikipedia has a nice little animated GIF that explains it well: Wikipedia: Radian

07:53
Review: Space Cannon Setup

This video is a review of some of the code we've added to our Space Cannon game so far. We discuss SKSpriteNodes, instance variables, actions, SKScene and SKView.

05:10

Making our Cannon Shoot

Time to get our cannon shooting. In order to do so, we are going to need to convert the rotation angle of our cannon into usable x and y coordinates. We can use good old trigonometry for this. In this video we'll create a helper method to convert our angle and setup a method to create a new cannon ball node every time we tap the screen.

Code

static inline CGVector radiansToVector(CGFloat radians)
{
CGVector vector;
vector.dx = cosf(radians);
vector.dy = sinf(radians);
return vector;
}
-(void)shoot
{
// Create ball node.
SKSpriteNode *ball = [SKSpriteNode spriteNodeWithImageNamed:@"Ball"];
CGVector rotationVector = radiansToVector(_cannon.zRotation);
ball.position = CGPointMake(_cannon.position.x + (_cannon.size.width * 0.5 * rotationVector.dx),
_cannon.position.y + (_cannon.size.width * 0.5 * rotationVector.dy));
[_mainLayer addChild:ball];
}

Links

Need to brush up on your sine and cosine? Wikipedia: Trigonometric Functions

05:58
Working With Angles

In this video we discuss a few of the concepts we need to know when working with angles in our games. We discuss radians, the various constants we have available related to the value of pi, as well as the functions, sine and cosine.

Notes

We can convert degrees to radians using: radians = degrees * (π / 180)

We can convert radians to degrees using: degrees = radians * (180 / π)

05:17
Data Types

In this video we discuss a number of the basic data types that we'll be regularly working with while developing games. We look at CGFloat, CGPoint, CGVector, CGSize, CGRect

04:50

Adding Physics

To get our cannon balls moving, we can take advantage of Sprite Kit's physics simulation features. By adding a physics body to our cannon ball node and giving it a velocity, we can get things firing.

Code

In order to make our cannon balls shoot, we declare the following constant:

static const CGFloat SHOOT_SPEED = 1000.0;

And add physics to our cannon balls by adding to our shoot method:

ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:6.0];
ball.physicsBody.velocity = CGVectorMake(rotationVector.dx * SHOOT_SPEED, rotationVector.dy * SHOOT_SPEED);

We also turn off gravity for all nodes:

// Turn off gravity.
self.physicsWorld.gravity = CGVectorMake(0.0, 0.0);

Links

Sprite Kit Programming Guide: Simulating Physics

05:54

Shoot Method Review

This is a review video of the code we've set up for the shoot method of our Space Cannon game.

Section 3: Review of Shooting with our Space Cannon
03:28

Keeping Things Clean

If we want to keep our game's performance smooth, it's a good idea to remove any nodes from our node tree that we're no longer using. By enumerating through the nodes in the tree, we can identify those that are out of bounds of the screen and go ahead and remove them from our scene.

Code

We set the name property on our ball nodes to make them easy to find:

ball.name = @"ball";

Then we override the didSimulatePhysics method, enumerate our nodes, and remove those we don't need.

-(void)didSimulatePhysics
{
// Remove unused nodes.
[_mainLayer enumerateChildNodesWithName:@"ball" usingBlock:^(SKNode *node, BOOL *stop) {
if (!CGRectContainsPoint(self.frame, node.position)) {
[node removeFromParent];
}
}];
}
07:40
Using Breakpoints

In this video we look at how we can use breakpoints to interrupt the execution of our code and check the state of our game to help when debugging our code. If you are familiar with how to use breakpoints, feel free to skip this video. We also look at how we can use the output window to print out the values of properties of our objects.

07:37
Review of Physics Body Basics

In this video we have a play with physics bodies. Starting with the standard Sprite Kit template project, we create a node to represent a soccer ball and get it moving around by creating a physics body for it. We see how the dynamic and affectedByGravity properties affect our node and discuss some of the different types of physics bodies.

Assets

* SoccerBall@2x.png

Links

* Soccer Ball by gothicfan95

03:43

Timing

As we process each new frame of our game's state, we need to consider the order in which things should be processed. In this video we see first hand how the timing of when we call our shoot method within the game loop, can have an impact on our gameplay.

Code

We delay the call of our shoot method by adding to the didSimulatePhysics method:

// Shoot.
if (_didShoot) {
[self shoot];
_didShoot = NO;
}

Links

Sprite Kit Programming Guide: Advanced Scene Processing

06:18
Review of Game Loop

In this video we take a closer look at methods update, didEvaluateActions and didSimulatePhysics that we have as part of our scene. We examine the timing of when these methods are called in regards to our game loop by using some log statements to see how the properties of a node changes each frame.

Links

* Advanced Scene Processing

05:55

Edges and Physical Properties

In this video we add walls to our scene using a new physics body type: edge. Apple's documentaion describes an edge thus:

  • An edge is a static volume-less body. Edges are never moved by the simulation and their mass doesn’t matter. Edges are used to represent negative space within a scene (such as a hollow spot inside another entity) or an uncrossable, invisibly thin boundary. For example, edges are frequently used to represent the boundaries of your scene.
  • The main difference between a edge and a volume is that an edge permits movement inside its own boundaries, while a volume is considered a solid object. If edges are moved through other means, they only interact with volumes, not with other edges.

We also need to update a few of the properties on the physics bodies of our cannon balls so that they maintain their momentum when bouncing around:

  • The friction property determines the roughness of the body’s surface. It is used to calculate the frictional force that a body applies to other bodies moving along its surface.
  • The linearDamping and angularDamping properties are used to calculate friction on the body as it moves through the world. For example, this might be used to simulate air or water friction.
  • The restitution property determines how much energy a body maintains during a collision—its bounciness.

Code

We add edges for our cannon balls to bounce off by adding some new nodes in the init method:

// Add edges.
SKNode *leftEdge = [[SKNode alloc] init];
leftEdge.physicsBody = [SKPhysicsBody bodyWithEdgeFromPoint:CGPointZero toPoint:CGPointMake(0.0, self.size.height)];
leftEdge.position = CGPointZero;
[self addChild:leftEdge];
SKNode *rightEdge = [[SKNode alloc] init];
rightEdge.physicsBody = [SKPhysicsBody bodyWithEdgeFromPoint:CGPointZero toPoint:CGPointMake(0.0, self.size.height)];
rightEdge.position = CGPointMake(self.size.width, 0.0);
[self addChild:rightEdge];

We also adjust some physics properties on our cannon balls:

ball.physicsBody.restitution = 1.0;
ball.physicsBody.linearDamping = 0.0;
ball.physicsBody.friction = 0.0;

Links

There is much more info on the vairous aspects of Sprite Kit's physics simulation in Apple's documentation: Sprite Kit Programming Guide: Configuring the Physical Properties of a Physics Body

Section 4: Challenge 1: Bouncing Balls
01:16
Challenge: Ball Challenge

The idea of this challenge is to play with physics bodies by creating a bouncing ball simulator. Create a new sprite kit project for the iPhone and setup the project in in portrait orientation. Set the background colour to white. Remove the code that comes with the template project that creates the hello world label and space ship nodes. Add code to the touches began event so that each time you tap on the screen a random ball is created at the location of the tap. The ball should automatically fall and bounce around inside the screen. You will need to setup a edge based physics body that loops around the border of the screen (hint: you can use the scene's frame when creating your physics body). You should have different types of balls (Choose at random each time there is a tap event) each with their own physics properties that make them behave differently from one another. There are graphics provided for 3 different types of balls:

* An 8 ball - this should be the heaviest and not bounce as much as the others.

* A soccer ball - a bit lighter than the 8 ball and more bouncy.

* A beach ball - this ball should be very light and bouncy.

Once you've setup the basic 3 balls, experiment with Sprite Kit's physics simulator by applying different physics properties.

Here are some suggestions for things to try:

* Make one of the balls bounce forever.

* Make one of the balls spin when it is created.

* See what happens when you turn off the friction for all the balls.

* See what happens when you set the friction to maximum for all the balls.

Assets

* 8Ball@2x.png

* BeachBall@2x.png

* SoccerBall@2x.png

Links

* Billiard Balls by FacadeGaikan

* Soccer Ball by gothicfan95

04:11
Challenge: Ball Challenge: Solution

In this video we go through the solution to the ball challenge and have a bit of a play around with the properties of our physics bodies.

Code

Create a new Sprite Kit template project, add the 3 ball graphics to the project, and remove the code that generates the Hello World text and the code that creates a rotating spaceship node.

In the init method, change the background color to white:

self.backgroundColor = [SKColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];

In the init method, set up an invisible wall around the edge of the screen so that when we add some balls to the scene they will bounce around. Do this by creating an instance of SKNode that has an edge based physics body using the frame of the scene as the physics body's rectangle:

SKNode *edge = [SKNode node];
edge.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
[self addChild:edge];

Change the touchesBegan method to create a random ball when a touch event occurs. The 3 ball types should each have their own mass and restitution properties so that they bounce and affect each other in different ways:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
int random = arc4random_uniform(3);
SKSpriteNode *ball;
switch (random) {
case 0:
ball = [SKSpriteNode spriteNodeWithImageNamed:@"8Ball"];
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.size.width * 0.5];
ball.physicsBody.mass = 0.8;
ball.physicsBody.restitution = 0.2;
break;
case 1:
ball = [SKSpriteNode spriteNodeWithImageNamed:@"BeachBall"];
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.size.width * 0.5];
ball.physicsBody.mass = 0.1;
ball.physicsBody.restitution = 0.8;
break;
default:
ball = [SKSpriteNode spriteNodeWithImageNamed:@"SoccerBall"];
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.size.width * 0.5];
ball.physicsBody.mass = 0.4;
ball.physicsBody.restitution = 0.5;
break;
}
ball.position = location;
[self addChild:ball];
}
}
08:56
Challenge: Ball Challenge: Solution

In this video we go through the solution to the ball challenge and have a bit of a play around with the properties of our physics bodies.

Code

Create a new Sprite Kit template project, add the 3 ball graphics to the project, and remove the code that generates the Hello World text and the code that creates a rotating spaceship node.

In the init method, change the background color to white:

self.backgroundColor = [SKColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];

In the init method, set up an invisible wall around the edge of the screen so that when we add some balls to the scene they will bounce around. Do this by creating an instance of SKNode that has an edge based physics body using the frame of the scene as the physics body's rectangle:

SKNode *edge = [SKNode node];
edge.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
[self addChild:edge];

Change the touchesBegan method to create a random ball when a touch event occurs. The 3 ball types should each have their own mass and restitution properties so that they bounce and affect each other in different ways:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
int random = arc4random_uniform(3);
SKSpriteNode *ball;
switch (random) {
case 0:
ball = [SKSpriteNode spriteNodeWithImageNamed:@"8Ball"];
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.size.width * 0.5];
ball.physicsBody.mass = 0.8;
ball.physicsBody.restitution = 0.2;
break;
case 1:
ball = [SKSpriteNode spriteNodeWithImageNamed:@"BeachBall"];
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.size.width * 0.5];
ball.physicsBody.mass = 0.1;
ball.physicsBody.restitution = 0.8;
break;
default:
ball = [SKSpriteNode spriteNodeWithImageNamed:@"SoccerBall"];
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.size.width * 0.5];
ball.physicsBody.mass = 0.4;
ball.physicsBody.restitution = 0.5;
break;
}
ball.position = location;
[self addChild:ball];
}
}
05:42
Challenge: Ball Challenge: Solution

In this video we go through the solution to the ball challenge and have a bit of a play around with the properties of our physics bodies.

Code

Create a new Sprite Kit template project, add the 3 ball graphics to the project, and remove the code that generates the Hello World text and the code that creates a rotating spaceship node.

In the init method, change the background color to white:

self.backgroundColor = [SKColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];

In the init method, set up an invisible wall around the edge of the screen so that when we add some balls to the scene they will bounce around. Do this by creating an instance of SKNode that has an edge based physics body using the frame of the scene as the physics body's rectangle:

SKNode *edge = [SKNode node];
edge.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
[self addChild:edge];

Change the touchesBegan method to create a random ball when a touch event occurs. The 3 ball types should each have their own mass and restitution properties so that they bounce and affect each other in different ways:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
int random = arc4random_uniform(3);
SKSpriteNode *ball;
switch (random) {
case 0:
ball = [SKSpriteNode spriteNodeWithImageNamed:@"8Ball"];
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.size.width * 0.5];
ball.physicsBody.mass = 0.8;
ball.physicsBody.restitution = 0.2;
break;
case 1:
ball = [SKSpriteNode spriteNodeWithImageNamed:@"BeachBall"];
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.size.width * 0.5];
ball.physicsBody.mass = 0.1;
ball.physicsBody.restitution = 0.8;
break;
default:
ball = [SKSpriteNode spriteNodeWithImageNamed:@"SoccerBall"];
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.size.width * 0.5];
ball.physicsBody.mass = 0.4;
ball.physicsBody.restitution = 0.5;
break;
}
ball.position = location;
[self addChild:ball];
}
}
Section 5: Adding Halos to our Space Cannon Game
02:45

Adding Randomness

Most games will involve some sort of random element. For this game, we want to spawn enemy halos at random positions. In this video we create a convenience method that will give us a random float value within a specified range.

Code

static inline CGFloat randomInRange(CGFloat low, CGFloat high)
{
CGFloat value = arc4random_uniform(UINT32_MAX) / (CGFloat)UINT32_MAX;
return value * (high - low) + low;
}

Links

Nice little FAQ on randomization functions: NSHipster: rand(3) / random(3) / arc4random(3) / et al.

08:02

Something To Shoot

Our cannon is pointless if we don't have anything to shoot at. It's time to fix that up. In this video we'll setup a new method to create halo nodes that will randomly float down from the top of the screen. We'll use an action sequence to periodically call this method to give us some enemies to target.

Code

We add some constants for the angle and speed values we're going to use when setting a halo's velocity:

static const CGFloat kCCHaloLowAngle = 200.0 * M_PI / 180.0;
static const CGFloat kCCHaloHighAngle = 340.0 * M_PI / 180.0;
static const CGFloat kCCHaloSpeed = 100.0;

We add a new method that will add a new halo node at a random position above the top of the screen and give it a random downward velocity:

-(void)spawnHalo
{
// Create halo node.
SKSpriteNode *halo = [SKSpriteNode spriteNodeWithImageNamed:@"Halo"];
halo.position = CGPointMake(randomInRange(halo.size.width * 0.5, self.size.width - (halo.size.width * 0.5)),
self.size.height + (halo.size.height * 0.5));
halo.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:16.0];
CGVector direction = radiansToVector(randomInRange(kCCHaloLowAngle, kCCHaloHighAngle));
halo.physicsBody.velocity = CGVectorMake(direction.dx * kCCHaloSpeed, direction.dy * kCCHaloSpeed);
halo.physicsBody.restitution = 1.0;
halo.physicsBody.linearDamping = 0.0;
halo.physicsBody.friction = 0.0;
[_mainLayer addChild:halo];
}

We setup a new action sequence that we run against the scene to periodically spawn a new halo. Note: in the video I say that the duration will be modified by up to plus or minus the range, but it actually will only alter the duration by up to half the range. So that would give us a wait time of 1.5 - 2.5 seconds.

// Create spawn halo actions.

SKAction *spawnHalo = [SKAction sequence:@[[SKAction waitForDuration:2 withRange:1],
[SKAction performSelector:@selector(spawnHalo) onTarget:self]]];
[self runAction:[SKAction repeatActionForever:spawnHalo]];

Links

More detailed info on actions: Sprite Kit Programming Guide: Adding Actions to Nodes

06:10
Review - Random in Range and Spawn Halo Action

This is a review video of the code we've implemented to spawn our halos. We take a closer look at the RandomInRange method and the actions we created to periodically spawn halos. Feel free to skip this video if you feel you have a pretty solid understanding of what's going on.

Section 6: Detecting Collisions and Contacts
06:38

Bitfield

In this video we discuss bitfields. In order to detect contacts between nodes and to control how nodes react to collisions with other nodes we need to use bit masks. We go over the theory of how they work so we can start using them up in our projects.

Links

Wikipedia: Bit Field

05:45

Space Cannon - Setting CollisionBitMask

Code

We declare 3 bitmasks:

static const uint32_t kCCHaloCategory = 0x1 << 0;
static const uint32_t kCCBallCategory = 0x1 << 1;
static const uint32_t kCCEdgeCategory = 0x1 << 2;

And set the categoryBitMask property on our various physics bodies:

leftEdge.physicsBody.categoryBitMask = kCCEdgeCategory;
rightEdge.physicsBody.categoryBitMask = kCCEdgeCategory;
ball.physicsBody.categoryBitMask = kCCBallCategory;
halo.physicsBody.categoryBitMask = kCCHaloCategory;

By setting the collisionBitMask property on the ball and halo physics bodies, we can control how they react when they collide with other physics bodies:

ball.physicsBody.collisionBitMask = kCCEdgeCategory;
halo.physicsBody.collisionBitMask = kCCEdgeCategory;

Links

Sprite Kit Programming Guide: Working with collisions and contacts

05:31

Space Cannon - Receiving Collision Notifications

Code

We need to take a few steps in order to react to a collision between a ball and a halo. First we set the contactTestBitMask on the halo's physics body:

halo.physicsBody.contactTestBitMask = kCCBallCategory;

Next, in the header file for our scene we declare that we are implementing the SKPhysicsContactDelegate protocol:

@interface CCMyScene : SKScene <SKPhysicsContactDelegate>

We then tell the physics world that our scene object should be the delegate to receive contact notifications:

self.physicsWorld.contactDelegate = self;

And finally we implement the didBeginContact method:

-(void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody;
SKPhysicsBody *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask) {
firstBody = contact.bodyA;
secondBody = contact.bodyB;
} else {
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
if (firstBody.categoryBitMask == kCCHaloCategory && secondBody.categoryBitMask == kCCBallCategory) {
// Collision between halo and ball.
[firstBody.node removeFromParent];
[secondBody.node removeFromParent];
}
}

Links

Sprite Kit Programming Guide: Working with collisions and contacts

06:46
Review: Contact Delegate, Bit Masks

This is a review video of the steps we needed to take to get our didBeginContact method setup so that we could respond to two physics bodies coming into contact with one another. Feel free to skip this video if you feel you've already got a good grasp of how we got this stuff working.

Section 7: Particle Effects and Explosions
07:06

Space Cannon - Configuring Particle Effects

Particle Emitter Editor

In this video we take a look at the particle emitter editor and see how we can use it to design various particle effects to include in our games.

Links

Apples documentation explains in more detail the effects of the vairious properites of a particle emitter: Particle Emitter Editor Guide

04:34

Space Cannon - Adding An Explosion Effect

Code

We create a method to easily generate a new explosion node for us:

-(void)addExplosion:(CGPoint)position
{
NSString *explosionPath = [[NSBundle mainBundle] pathForResource:@"HaloExplosion" ofType:@"sks"];
SKEmitterNode *explosion = [NSKeyedUnarchiver unarchiveObjectWithFile:explosionPath];
explosion.position = position;
[_mainLayer addChild:explosion];
SKAction *removeExplosion = [SKAction sequence:@[[SKAction waitForDuration:1.5],
[SKAction removeFromParent]]];
[explosion runAction:removeExplosion];
}

We then call the method when we detect a collision between a ball and a halo:

[self addExplosion:firstBody.node.position];

Links

Sprite Kit Programming Guide: Emitter Nodes Create Particle Effects

05:45
Creating SKEmitterNodes Manually

We've seen how we can use the particle emitter editor built into Xcode to visually generate particle effects. It is also possible to manually code up a particle emitter. This video demonstrates how we create an instance of an SKEmitterNode using just code.

Code
SKEmitterNode *explosion = [SKEmitterNode node];
explosion.particleTexture = [SKTexture textureWithImageNamed:@"spark"];
explosion.particleLifetime = 1;
explosion.particleBirthRate = 2000;
explosion.numParticlesToEmit = 100;
explosion.emissionAngleRange = 360;
explosion.particleScale = 0.2;
explosion.particleScaleSpeed = - 0.2;
explosion.particleSpeed = 200;
Links

* SKEmitterNode Class Reference

07:00

Space Cannon - Shot Limiting

Our game would be too easy if we allowed the player to infintely shoot as often as they like. In this video we impose an available ammo limit of 5 that increases by 1 each second.

Code

We add a new property to he header file of our scene:

@property (nonatomic) int ammo;

In the init method, we add a sprite node to display how much ammo is available and create an action to increment the ammo each second:

// Setup Ammo.
_ammoDisplay = [SKSpriteNode spriteNodeWithImageNamed:@"Ammo5"];
_ammoDisplay.anchorPoint = CGPointMake(0.5, 0.0);
_ammoDisplay.position = _cannon.position;
[_mainLayer addChild:_ammoDisplay];
self.ammo = 5;
SKAction *incrementAmmo = [SKAction sequence:@[[SKAction waitForDuration:1],
[SKAction runBlock:^{
self.ammo++;
}]]];
[self runAction:[SKAction repeatActionForever:incrementAmmo]];

Overriding the ammo property's setter allows us to easily impose our limit and update the ammo display at the same time:

-(void)setAmmo:(int)ammo
{
if (ammo >= 0 && ammo <= 5) {
_ammo = ammo;
_ammoDisplay.texture = [SKTexture textureWithImageNamed:[NSString stringWithFormat:@"Ammo%%d", ammo]];
}
}

In our shoot method, we limit our shots and decrement the ammo with a simple if statement:

if (self.ammo > 0) {
self.ammo--;
...
}
Section 8: Challenge 2: Alternate Shot Limiting
00:57
Challenge: Alternate Shot Limiting

In the video where we added shot limiting, I talked about the idea that we could instead limit our shots, by only allowing a couple of cannon balls on screen at any one time. See if you can work out how to implement this behavior and make it so that only 2 cannon balls can be on screen at the same time.

05:03
Challenge: Alternate Shot Limiting: Solution

There are a couple of ways we could solve our shot limiting challenge. One way would be to take advantage of our existing ammo property but set it's maximum to 2. We would then need to remove our action that automatically increases our ammo over time and instead increase the ammo each time a cannon ball is removed from our scene. This happens in two places; when a ball collides with a halo, and when a ball moves outside of the screen.

A second way that we could solve the challenge would be to check how many cannon ball nodes are in the scene when we try to shoot. If there are 2 or more, then we don't allow the player to shoot. We could set this up in the shoot method:

Code
-(void)shoot
{
int availableAmmo = 2;
for (SKNode *node in _mainLayer.children) {
if([node.name isEqualToString:@"ball"])
{
availableAmmo--;
}
}
if (availableAmmo > 0) {
...
Section 9: Shields and start a Lifebar
05:53
Space Cannon - Add Shields

Lets give the player a first line of defense by adding a row of shields above their cannon. When a halo hits a shield it should destroy both the shield and he halo.

Code

We delcare a new category for our shields:

static const uint32_t kCCShieldCategory = 0x1 << 3;

In our init method we add some code to setup 6 shields above the cannon:

// Setup shields
for (int i = 0; i < 6; i++) {
SKSpriteNode *shield = [SKSpriteNode spriteNodeWithImageNamed:@"Block"];
shield.position = CGPointMake(35 + (50 *i), 90);
[_mainLayer addChild:shield];
shield.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(42, 9)];
shield.physicsBody.categoryBitMask = kCCShieldCategory;
shield.physicsBody.collisionBitMask = 0;
}

We update the contactTestBitMask property of our halos to include the new shield category:

halo.physicsBody.contactTestBitMask = kCCBallCategory | kCCShieldCategory;

Finally we respond to a contact between a halo and shield in the didBeginContact method:

if (firstBody.categoryBitMask == kCCHaloCategory && secondBody.categoryBitMask == kCCShieldCategory) {
// Collision between halo and shield.
[self addExplosion:firstBody.node.position];
[firstBody.node removeFromParent];
[secondBody.node removeFromParent];
}
05:51
Review - Add Shields

We added quite a bit of code to create our shields. In this video we review all this code in closer detail. If you feel you've got a good understanding on how it all works, then you may want to skip this video.

06:12
Space Cannon - Add Life Bar

In this video we'll create a game over condition by adding a bar above the cannon that if hit by a halo triggers the end of the game.

Code

First we need a new category for the life bar:

static const uint32_t kCCLifeBarCategory = 0x1 << 4;

We create the life bar node in the init method:

SKSpriteNode *lifeBar = [SKSpriteNode spriteNodeWithImageNamed:@"BlueBar"];
lifeBar.position = CGPointMake(self.size.width * 0.5, 70);
lifeBar.physicsBody = [SKPhysicsBody bodyWithEdgeFromPoint:CGPointMake(-lifeBar.size.width * 0.5, 0) toPoint:CGPointMake(lifeBar.size.width * 0.5, 0)];
lifeBar.physicsBody.categoryBitMask = kCCLifeBarCategory;
[_mainLayer addChild:lifeBar];

We update the contactTestBitMask property of the halos to include the new life bar category:

halo.physicsBody.contactTestBitMask = kCCBallCategory | kCCShieldCategory |kCCLifeBarCategory;

We refactor our addExplosion method to take the name of the explosion we wish to add:

-(void)addExplosion:(CGPoint)position withName:(NSString*)name
{
NSString *explosionPath = [[NSBundle mainBundle] pathForResource:name ofType:@"sks"];
SKEmitterNode *explosion = [NSKeyedUnarchiver unarchiveObjectWithFile:explosionPath];
...
}

We can then update our didBeginContactMethod to respond to the halo/life bar contact:

if (firstBody.categoryBitMask == kCCHaloCategory && secondBody.categoryBitMask == kCCLifeBarCategory) {
// Collision between halo and life bar.
[self addExplosion:firstBody.node.position withName:@"HaloExplosion"];
[self addExplosion:secondBody.node.position withName:@"LifeBarExplosion"];
[firstBody.node removeFromParent];
[secondBody.node removeFromParent];
Section 10: Challenge 3: Explosion on Bounce
00:27
Challenge: Add Explosion on Bounce

See if you can create a new type of mini explosion that occurs every time a cannon ball bounces off the side of the screen. You will need to setup a new particle effect file and detect when there is a collision between a cannon ball and an edge. In terms of positioning your explosion, check out the contactPoint property that is part of the SKPhysicsContact object in the didBeginContact method to help get your explosion right next to the edge.

06:16
Challenge: Add Explosion on Bounce: Solution

In this video we run through how we can solve the challenge to display an explosion effect each time a cannon ball bounces off the edge of the screen.

Code

You'll first need to create a new particle effect file with the explosion effect you want for the cannon ball bounce.

We need to detect when there is contact between a ball node and an edge. We set this up by setting the contactTestBitMask property of our ball's physics body:

ball.physicsBody.contactTestBitMask = kCCEdgeCategory;

We can then add a new if statement in the didBeginContact method to respond to the contact between ball and edge. We can use the contactPoint property of the contact object that's passed into the didBeginContact method to position our explosion:

if (firstBody.categoryBitMask == kCCBallCategory && secondBody.categoryBitMask == kCCEdgeCategory) {
[self addExplosion:contact.contactPoint withName:@"BounceExplosion"];
}
Section 11: Game Reset and Scoring
05:25
Game Over and New Game

Code

We refactor our shield and life bar setup code, moving it from the init method into a newGame method:

-(void)newGame
{
self.ammo = 5;
[_mainLayer removeAllChildren];
// Setup shields
for (int i = 0; i < 6; i++) {
SKSpriteNode *shield = [SKSpriteNode spriteNodeWithImageNamed:@"Block"];
shield.name = @"shield";
shield.position = CGPointMake(35 + (50 *i), 90);
[_mainLayer addChild:shield];
shield.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(42, 9)];
shield.physicsBody.categoryBitMask = kCCShieldCategory;
shield.physicsBody.collisionBitMask = 0;
}
SKSpriteNode *lifeBar = [SKSpriteNode spriteNodeWithImageNamed:@"BlueBar"];
lifeBar.position = CGPointMake(self.size.width * 0.5, 70);
lifeBar.physicsBody = [SKPhysicsBody bodyWithEdgeFromPoint:CGPointMake(-lifeBar.size.width * 0.5, 0) toPoint:CGPointMake(lifeBar.size.width * 0.5, 0)];
lifeBar.physicsBody.categoryBitMask = kCCLifeBarCategory;
[_mainLayer addChild:lifeBar];
}

We create a gameOver method that we call when we detect a collision between a halo and the life bar. This method explodes all the halos on screen and removes all the other nodes. We make it wait for 1.5 seconds before automatically triggering a new game:

-(void)gameOver
{
[_mainLayer enumerateChildNodesWithName:@"halo" usingBlock:^(SKNode *node, BOOL *stop) {
[self addExplosion:node.position withName:@"HaloExplosion"];
[node removeFromParent];
}];
[_mainLayer enumerateChildNodesWithName:@"ball" usingBlock:^(SKNode *node, BOOL *stop) {
[node removeFromParent];
}];
[_mainLayer enumerateChildNodesWithName:@"shield" usingBlock:^(SKNode *node, BOOL *stop) {
[node removeFromParent];
}];
[self performSelector:@selector(newGame) withObject:nil afterDelay:1.5];
}

So that we can enumerate through all the halo nodes, we need to make sure we give them a name:

halo.name = @"halo";
05:18
Review: Game Reset Functionality

This is a review video of the code we wrote to setup our game reset functionality. If you feel you've got a good handle of the code then feel free to skip this video as we're not going to cover anything new.

03:51
Add Scoring
Code

We setup a new label to display the current score on screen:

// Setup score display
_scoreLabel = [SKLabelNode labelNodeWithFontNamed:@"DIN Alternate"];
_scoreLabel.position = CGPointMake(15, 10);
_scoreLabel.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeLeft;
_scoreLabel.fontSize = 15;
[self addChild:_scoreLabel];

We define a new property in the scene's header file:

@property (nonatomic) int score;

We override the setter method of this property to update the label:

-(void)setScore:(int)score
{
_score = score;
_scoreLabel.text = [NSString stringWithFormat:@"Score: %d", score];
}

When we detect a collision between a halo and a ball, we increase the score:

if (firstBody.categoryBitMask == kCCHaloCategory && secondBody.categoryBitMask == kCCBallCategory) {
// Collision between halo and ball.
self.score++;
...

We also reset the score when in the newGame method

self.score = 0;
Section 12: Adding Basic Sounds
01:34
Free art resources: OpenGameArt.org

Having good art is obviously a big part of creating games but not all of us are artists. There are a number of web resources where you can find art designed specifically for games. This can be really useful for finding art to use in your game even if you just need something to test out game concepts with and use as placeholder art before getting art designed specifically for your game. Whenever you use art from the web make sure you check out what license it's being released under. Sometimes license types require you to to release your whole project as open source, some might just require you to include an attribution to the original artist.

We are about to add some sounds to our space cannon game and so I've found a couple of sound packages on OpenGameArt.org that have some suitable sounds we can use. 63 Digital Sound Effects by Kenny has some great sci-fi sounds that should suit our needs. We're going to use:

* laser4.mp3 for when our cannon shoots. We need to edit this sound to remove the echo.

* zap1.mp3 for when a halo bounces off an edge

* twoTone1.mp3 for when a ball bounces off an edge. We need to edit this sound to remove the echo.

There is also a great explosion sound, explode.ogg, in the Platformer Sounds package by yd. We'll use this for when a halo explodes. We'll also use a modified version of it for when the life bar explodes.

Assets

These are the originals of the specific sounds we are using from the sound packages 63 Digital Sound Effects by Kenny and Platformer Sounds by yd. Some of them will need to be edited, and they will all need to be converted before we can use them in our game. The process for this is explained over the next couple of videos.

Links

OpenGameArt

04:38
Editing Sounds with Audacity

Audacity is free, open source, cross-platform software for recording and editing sounds. In this video we see how we can use an audio editing program like Audacity to edit sounds to get them ready for our game.

Links

Audacity: Free Audio Editor and Recorder

Audacity Wiki Home Page

04:02
Converting Sounds to CAF Format

Core Audo Format is the audio format preffered by the iPhone, so it's good if we can convert our sound files into this format prior to adding them to our games. It's important to understand that the CAF file format is just a wrapper for the actual audio data and so we have a lot of choice as to how we want the data encoded in the file. In terms of performance it's best if we use an uncompressed data format such as Linear PCM when we encode our files. We need to keep in mind that our file size will be larger when using an uncompressed format compared to when we are using compression. If file size is a concern then IMA4 may be worth considering as it gives 4:1 compression on 16-bit audio files.

Code

To convert a file to CAF using an uncompressed 16-bit data format, open a terminal window, navigate to the folder where the file is stored and then type:

afconvert -f caff -d LEI16 <filename>

To do the same conversion using IMA4 use:

afconvert -f caff -d ima4 <filename>

To save time in the terminal we can create a shell script to take care of the conversion for us. Create a file with the following code and name it convert_wav_to_caff.sh

for f in *.wav; do

afconvert -f caff -d LEI16 $f

done

If we put that file in a directory with the wav files we want to convert we can run the script to convert all the files by typing into the terminal

sh convert_wav_to_caff.sh

Links

Apple Core Audio Format Specification: CAF File OverviewAudio

Tutorial for iOS: File and Data Formats

07:56
Space Cannon - Adding Sounds

In this video we use the playSoundFileNamed action to add sound effects to our game. It is a good idea if we initiate our sound based actions prior to starting a game so that we don't get any slow-down in our gameplay when playing a sound for the first time.

Code

We declare variables to hold our actions to play our sounds:

SKAction *_bounceSound;
SKAction *_deepExplosionSound;
SKAction *_explosionSound;
SKAction *_laserSound;
SKAction *_zapSound;

We initialise our actions in the init method. This ensures our sounds are loaded prior to starting the game:

// Setup sounds

_bounceSound = [SKAction playSoundFileNamed:@"Bounce.caf" waitForCompletion:NO];
_deepExplosionSound = [SKAction playSoundFileNamed:@"DeepExplosion.caf" waitForCompletion:NO];
_explosionSound = [SKAction playSoundFileNamed:@"Explosion.caf" waitForCompletion:NO];
_laserSound = [SKAction playSoundFileNamed:@"Laser.caf" waitForCompletion:NO];
_zapSound = [SKAction playSoundFileNamed:@"Zap.caf" waitForCompletion:NO];

We run our actions wherever we want to play a sound. E.g:

[self runAction:_explosionSound];

We also tell our ball and halo physics bodies to include the edge category as part of their contactTestBitMask:

ball.physicsBody.contactTestBitMask = kCCEdgeCategory;
halo.physicsBody.contactTestBitMask = kCCBallCategory | kCCShieldCategory | kCCLifeBarCategory | kCCEdgeCategory;

We can then play a sound when a halo or ball bounces off an edge:

if (firstBody.categoryBitMask == kCCHaloCategory && secondBody.categoryBitMask == kCCEdgeCategory) {
[self runAction:_zapSound];
}
if (firstBody.categoryBitMask == kCCBallCategory && secondBody.categoryBitMask == kCCEdgeCategory) {
[self runAction:_bounceSound];
}
Section 13: Adding Menus
06:22
Space Cannon - Add Menu
Code

We create a new class to handle our menu. This class inherits from SKNode. We override the init method in this class to add the various aspects of our menu:

- (id)init
{
self = [super init];
if (self) {
SKSpriteNode *title = [SKSpriteNode spriteNodeWithImageNamed:@"Title"];
title.position = CGPointMake(0, 140);
[self addChild:title];
SKSpriteNode *scoreBoard = [SKSpriteNode spriteNodeWithImageNamed:@"ScoreBoard"];
scoreBoard.position = CGPointMake(0, 70);
[self addChild:scoreBoard];
SKSpriteNode *playButton = [SKSpriteNode spriteNodeWithImageNamed:@"PlayButton"];
playButton.position = CGPointMake(0, 0);
[self addChild:playButton];
}
return self;
}

Back in our scene, we import the header file for our menu class:

#import "CCMenu.h"

Declare a variable to hold an instance of our menu:

CCMenu *_menu;

And setup our menu in the init method:

// Setup menu

_menu = [[CCMenu alloc] init];
_menu.position = CGPointMake(self.size.width * 0.5, self.size.height - 220);
[self addChild:_menu];
06:08
Space Cannon - Make Menu Functional
Code

In our menu class, we give the node for the play button a name:

PlayButton.name = @"Play";

In our scene we declare a state variable to make it easy to tell if we are in a game or not:

BOOL _gameOver;

We set this to NO and hide our menu when we start a new game in the newGame method:

_gameOver = NO;
_menu.hidden = YES;

And then do the opposite in our gameOver method:

_gameOver = YES;
_menu.hidden = NO;

We also need to check if we are in a game when we go to shoot our cannon:

for (UITouch *touch in touches) {
if (!_gameOver) {
_didShoot = YES;
}
}

And we add a touches ended event to detect a tap of the play button:

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
if (_gameOver) {
SKNode *n = [_menu nodeAtPoint:[touch locationInNode:_menu]];
if ([n.name isEqualToString:@"Play"]) {
[self newGame];
}
}
}
}
03:19
Review: locationInNode

This video is a quick review of the locationInNode method and the code we wrote to check if our touch event was located within the play button for our menu.

02:49
Fix Delay

In this video we fix up a couple of issues to do with our score label. We see that because we hadn't yet set the text property of our label when we loaded our game, then when we do eventually set the text property, our game slows down while it configures the display of the text. We can fix this up by setting the text property in our init method. We also hide our score label when we're not currently in a game.

Code

We move the call to set our score value into the init method of our scene, so that the text will be set when the game loads. We also set the ammo to 5 so that it doesn't build up slowly while on the menu and we hide the score label when the game is in the menu:

// Set initial values
self.ammo = 5;
self.score = 0;
_gameOver = YES;
_scoreLabel.hidden = YES;

We also need to set the _scoreLabel.hidden property to YES in the gameOver method and to NO in the newGame method.

06:38
Space Cannon - Add Scores to Menu
Code

In our menu class we add 2 label nodes to display the current and best scores on the menu. We set these up in the init method:

_scoreLabel = [SKLabelNode labelNodeWithFontNamed:@"DIN Alternate"];
_scoreLabel.fontSize = 30;
_scoreLabel.position = CGPointMake(-52, 50);
[self addChild:_scoreLabel];
_topScoreLabel = [SKLabelNode labelNodeWithFontNamed:@"DIN Alternate"];
_topScoreLabel.fontSize = 30;
_topScoreLabel.position = CGPointMake(48, 50);
[self addChild:_topScoreLabel];
self.score = 0;
self.topScore = 0;

In the header of our menu class we declare 2 properties to maintain the scores to be displayed:

@property (nonatomic) int score;
@property (nonatomic) int topScore;

We then override the setters on these properties to automatically update the labels:

-(void)setScore:(int)score
{
_score = score;
_scoreLabel.text = [[NSNumber numberWithInt:score] stringValue];
}
-(void)setTopScore:(int)topScore
{
_topScore = topScore;
_topScoreLabel.text = [[NSNumber numberWithInt:topScore] stringValue];
}

In our scene class we need to tell the menu what scores to display. We do this when we encounter a game over:

_menu.score = self.score;
if (self.score > _menu.topScore) {
_menu.topScore = self.score;
}
03:54
Review: Adding Scores to Menus

In this video we review the code we wrote to display our scores as part of our menu. We don't cover anything new, so feel free to skip this video if you feel confident with everything we did when setting this up.

Section 14: Save with NSUserDefaults
03:37
A Look at NSUserDefaults

We need a way to save our best score between sessions of our app. We can use the NSUserDefaults class for this. In this video we'll have a bit of a discussion about this class, what it does, and how we can use it to make sure our scores are saved to disk.

Links

* NSUserDefaults Class Reference

04:31
Space Cannon - Save Scores Using NSUserDefaults

We need to persist the players high score between sessions of the app. In this video we see how we can use NSUserDefaults to save and load the player's top score.

Code

We declare a variable we can use to quickly access the UserDefaults:

NSUserDefaults *_userDefaults;

And declare a const to use as a key for the data we want to save:

static NSString * const kCCKeyTopScore = @"TopScore";

In the init method we update the menu's topScore property with the value saved in UserDefaults.

// Load top score
_userDefaults = [NSUserDefaults standardUserDefaults];
_menu.topScore = [_userDefaults integerForKey:kCCKeyTopScore];

And do a save when we get a new high score. We call syncrhonize to make sure the data is saved:

if (self.score > _menu.topScore) {
_menu.topScore = self.score;
[_userDefaults setInteger:self.score forKey:kCCKeyTopScore];
[_userDefaults synchronize];
}
10:21
Space Cannon - Bug Fixes

In this video we fix up a few bugs we have in our game.

Code

To prevent halos from bouncing off the top of our edges if they spawn too close, we make the edges higher by adding a bit of a buffer:

leftEdge.physicsBody = [SKPhysicsBody bodyWithEdgeFromPoint:CGPointZero toPoint:CGPointMake(0.0, self.size.height + 100)];
rightEdge.physicsBody = [SKPhysicsBody bodyWithEdgeFromPoint:CGPointZero toPoint:CGPointMake(0.0, self.size.height + 100)];

We also prevent halos from destroying more than one shield by setting their categoryBitMask property when a contact occurs:

firstBody.categoryBitMask = 0;

Although I don't show it in this video, that same line of code should be added within all the contact tests with a halo.

In our didSimulatePhysics method, we clean up halos that drop below the bottom of the screen:

[_mainLayer enumerateChildNodesWithName:@"halo" usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.y + node.frame.size.height < 0) {
[node removeFromParent];
}
}];
Section 15: Particle Trail and Action Speeds
05:02
Space Cannon - Increase Halo Spawn Speed

In this video we see how we use the speed property of an action to modify how fast it runs. We set the speed of our action that spawns our halos so that the game gets harder the longer the player survives.

Code

When we run an action on a node we can give it a key so that we can access it again easily:

[self runAction:[SKAction repeatActionForever:spawnHalo] withKey:@"SpawnHalo"];

Actions have a speed property. We can use this to speed up or slow down our actions. In our case we want to increase the speed that halos are spawned each time we spawn a halo. We add this code to the spawnHalo method:

// Increase spawn speed.
SKAction *spawnHaloAction = [self actionForKey:@"SpawnHalo"];
if (spawnHaloAction.speed < 1.5) {
spawnHaloAction.speed += 0.01;
}

Then in the new game method we reset the speed of our action:

[self actionForKey:@"SpawnHalo"].speed = 1.0;
03:01
Space Cannon - Create Particle File for Trail Effect

In this video we setup a new Sprite Kit Particle File to be used to generate a trail of particles flowing out from behind our ball nodes.

03:33
Space Cannon - Add Trail Emitter Node To Balls

In this video we see how we can use the TargetNode property to direct particles that spawn from an emitter to move into the coordinate space of another ndoe. This allows us to move an emitter node without all the particles following it.

Code

We initialise our SKEmitterNode and add it as a child of the ball. We direct the emitter to set its particles to move around in the coordinate system of the main layer.

// Create trail.

NSString *ballTrailPath = [[NSBundle mainBundle] pathForResource:@"BallTrail" ofType:@"sks"];
SKEmitterNode *ballTrail = [NSKeyedUnarchiver unarchiveObjectWithFile:ballTrailPath];
ballTrail.targetNode = _mainLayer;
[ball addChild:ballTrail];
06:51
Space Cannon - Adjust Ball Trails With New Ball Class

We have a problem with our trail emitter node in that all the particles disappear when it's parent node (the ball) is removed from the scene. In this video we look at one possible solution for solving this by creating a new class for our ball nodes that has a reference to a trail emitter node. We can then add the trail emitter node as a child of the main layer rather than the ball, and update the position of the node as the ball moves. We can also turn off the trail emitter and remove it when our cannon ball is removed.

Code

We create a new ball class. In the header we declare a property and a method:

@interface CCBall : SKSpriteNode
@property (nonatomic) SKEmitterNode *trail;
-(void)updateTrail;
@end

We implement the updateTrail method and we override the removeFromParent method so that we can turn off the trail when the ball is removed:

@implementation CCBall
-(void)updateTrail
{
if (self.trail) {
self.trail.position = self.position;
}
}
-(void)removeFromParent
{
if (self.trail) {
self.trail.particleBirthRate = 0.0;
SKAction *removeTrail = [SKAction sequence:@[[SKAction waitForDuration:self.trail.particleLifetime
+ self.trail.particleLifetimeRange],
[SKAction removeFromParent]]];
[self runAction:removeTrail];
}
[super removeFromParent];
}
@end

Back in our scene, we update the declaration of our cannon balls in the shoot method to use our new class:

CCBall *ball = [CCBall spriteNodeWithImageNamed:@"Ball"];

We can then move the emitter node to be a child of the main layer and set a reference to the emitter on the ball:

[_mainLayer addChild:ballTrail];
ball.trail = ballTrail;

In our didSimulatePhysics method, when we enumerate through all the ball nodes, we can call the updateTrail method on each of them to make the emitter follow the ball:

[_mainLayer enumerateChildNodesWithName:@"ball" usingBlock:^(SKNode *node, BOOL *stop) {
if ([node respondsToSelector:@selector(updateTrail)]) {
[node performSelector:@selector(updateTrail) withObject:nil afterDelay:0.0];
}
...
Adjust Ball Trails With New Ball Class.mov
06:51
08:16
Review: Action Speed and Particle Trail Effect

In this video we review some of the code we've been writing over the last few space cannon videos. We take a closer look at the speed property of actions and run through in more detail what we needed to do to set up the particle trail effect for our cannon balls.

Section 16: Bounce Limit and Power Up
03:27
Space Cannon - Set Bounce Limit

In this video we introduce a bounce limit to our balls, taking advantage of the fact that we now have a class for them. Once the ball has bounced 3 times we remove it next time it hits an edge.

Code

We declare a new property in the header of the ball class:

@property (nonatomic) int bounces;

Then when we detect a contact between a ball and an edge we can increment the bounces property and remove the ball if we've bounced too many times:

if ([firstBody.node isKindOfClass:[CCBall class]]) {
((CCBall*)firstBody.node).bounces++;
if (((CCBall*)firstBody.node).bounces > 3) {
[firstBody.node removeFromParent];
}
}
06:02
Space Cannon - Add Point Multiplier Halo

In this video we setup a different type of halo. We give a halo a 1 in 6 chance of using a different graphic that represents an increase in the number of points we get per halo.

Code

When we spawn a halo, we randomly set 1 in 6 halos to be a point multiplier halo. We take advantage of the userData property of the halo node to mark this particular node as a multiplier node:

// Random point multiplier
if (!_gameOver && arc4random_uniform(6) == 0) {
halo.texture = [SKTexture textureWithImageNamed:@"HaloX"];
halo.userData = [[NSMutableDictionary alloc] init];
[halo.userData setValue:@YES forKey:@"Multiplier"];
}

We can then check if the halo is a multiplier halo when we detect a collision between a ball and a halo:

if ([[firstBody.node.userData valueForKey:@"Multiplier"] boolValue]) {
}
07:16
Space Cannon - Add Point Multiplier

===================================

We add a bit more interest to the game with the introduction of a point multiplier. If the player manages to destroy a multiplier halo, their point multiplier increases by 1 and the will score more points for each halo they destroy without missing. If however the player misses, their point multiplier will be reset to 1.

Code

We declare a new property in the header of our scene:

@property (nonatomic) int pointValue;

We then declare a label node to display the current point multiplier:

SKLabelNode *_pointLabel;

We setup this label in the init method:

// Setup point multiplier label
_pointLabel = [SKLabelNode labelNodeWithFontNamed:@"DIN Alternate"];
_pointLabel.position = CGPointMake(15, 30);
_pointLabel.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeLeft;
_pointLabel.fontSize = 15;
[self addChild:_pointLabel];

We override the setter of our pointValue property to update the display label when the value changes:

-(void)setPointValue:(int)pointValue
{
_pointValue = pointValue;
_pointLabel.text = [NSString stringWithFormat:@"Points: x%d", pointValue];
}

Whenever we destroy a halo, we increase the socre by the value of pointValue:

self.score += self.pointValue;

We also increment pointValue if we destroy a multiplier halo:

if ([[firstBody.node.userData valueForKey:@"Multiplier"] boolValue]) {
self.pointValue++;
}

And reset it when a ball bounces too many times, or leaves the screen:

self.pointValue = 1;
Section 17: Challenge 4: Add Bomb Power Up
01:36
Challenge: Add Bomb Power Up

This challenge is to create a bomb power up. This power up should be a new type of halo with a different graphic. The bomb halo should show up whenever there are currently 4 halos on screen and a 5th halo is about to spawn; this 5th halo should be a bomb halo. If the player manages to shoot the bomb, then all the halos that are currently on screen should also blow up (the player doesn't score for the extras though). However, if the bomb manages to get down and hit one of the player's shields, then all of the shields should be removed.

Assets

HaloBomb@2x.png

05:42
Challenge: Add Bomb Power Up Solution

In this video we solve our challenge for creating a bomb power up.

Code

Since our power up is a new type of halo, our code for creating creating one is going to be in the spawnHalo method. Our rule for creating a bomb power up is that there has to be 4 halos currently on screen. We need to get a count of how many halos are on screen. Since we remove our halos when they are not on the screen, we can just loop through all the children of the main layer and count up those that have the name halo:

int haloCount = 0;
for (SKNode *node in _mainLayer.children) {
if ([node.name isEqualToString:@"halo"]) {
haloCount++;
}
}

We can then check if our count is 4 in which case we can set a few halos on the the halo we've just created to turn it into a bomb halo. We update the texture and set the userData property to include a boolean YES value for the key "Bomb" Note that we can correctly check for a count of 4 here because we haven't yet added our new halo to the main layer. That code happens after so when we did our count above, we correctly got back the number of existing halos, not including the one we are creating in this method:

if (haloCount == 4) {
// Create bomb powerup
halo.texture = [SKTexture textureWithImageNamed:@"HaloBomb"];
halo.userData = [[NSMutableDictionary alloc] init];
[halo.userData setValue:@YES forKey:@"Bomb"];
}

When we destroy a halo with a cannon ball, we need to check the userData property of the halo to see if it has the value YES assigned to the key for Bomb. We do this in the didBeginContact method. If we just destroyed a bomb, then we go ahead and remove all the existing halo nodes, adding an explosion each time. We first set the name property of the halo that caused the detection to trigger, to nil, so that we don't add an extra explosion for it:

} else if ([[firstBody.node.userData valueForKey:@"Bomb"] boolValue]) {
firstBody.node.name = nil;
[_mainLayer enumerateChildNodesWithName:@"halo" usingBlock:^(SKNode *node, BOOL *stop) {
[self addExplosion:node.position withName:@"HaloExplosion"];
[node removeFromParent];
}];

Also in our didBeginContact method, this time where we detect a collision between a halo and a shield, we again check for a value of YES for our key, Bomb in the userData property. This time when we encounter a bomb, we instead remove all the shields by looping through them and calling removeFromParent:

if ([[firstBody.node.userData valueForKey:@"Bomb"] boolValue]) {
// Remove all shields.
[_mainLayer enumerateChildNodesWithName:@"shield" usingBlock:^(SKNode *node, BOOL *stop) {
[node removeFromParent];
}];
}
Assets

HaloBomb@2x.png

07:17
Challenge: Add Bomb Power Up Solution

In this video we solve our challenge for creating a bomb power up.

Code

Since our power up is a new type of halo, our code for creating creating one is going to be in the spawnHalo method. Our rule for creating a bomb power up is that there has to be 4 halos currently on screen. We need to get a count of how many halos are on screen. Since we remove our halos when they are not on the screen, we can just loop through all the children of the main layer and count up those that have the name halo:

int haloCount = 0;
for (SKNode *node in _mainLayer.children) {
if ([node.name isEqualToString:@"halo"]) {
haloCount++;
}
}

We can then check if our count is 4 in which case we can set a few halos on the the halo we've just created to turn it into a bomb halo. We update the texture and set the userData property to include a boolean YES value for the key "Bomb" Note that we can correctly check for a count of 4 here because we haven't yet added our new halo to the main layer. That code happens after so when we did our count above, we correctly got back the number of existing halos, not including the one we are creating in this method:

if (haloCount == 4) {
// Create bomb powerup
halo.texture = [SKTexture textureWithImageNamed:@"HaloBomb"];
halo.userData = [[NSMutableDictionary alloc] init];
[halo.userData setValue:@YES forKey:@"Bomb"];
}

When we destroy a halo with a cannon ball, we need to check the userData property of the halo to see if it has the value YES assigned to the key for Bomb. We do this in the didBeginContact method. If we just destroyed a bomb, then we go ahead and remove all the existing halo nodes, adding an explosion each time. We first set the name property of the halo that caused the detection to trigger, to nil, so that we don't add an extra explosion for it:

} else if ([[firstBody.node.userData valueForKey:@"Bomb"] boolValue]) {
firstBody.node.name = nil;
[_mainLayer enumerateChildNodesWithName:@"halo" usingBlock:^(SKNode *node, BOOL *stop) {
[self addExplosion:node.position withName:@"HaloExplosion"];
[node removeFromParent];
}];

Also in our didBeginContact method, this time where we detect a collision between a halo and a shield, we again check for a value of YES for our key, Bomb in the userData property. This time when we encounter a bomb, we instead remove all the shields by looping through them and calling removeFromParent:

if ([[firstBody.node.userData valueForKey:@"Bomb"] boolValue]) {
// Remove all shields.
[_mainLayer enumerateChildNodesWithName:@"shield" usingBlock:^(SKNode *node, BOOL *stop) {
[node removeFromParent];
}];
}
Assets

HaloBomb@2x.png

Section 18: Create Shield Power Up
06:29
Space Cannon - Create Pool for Shield Nodes

In this video we touch on the concept of pooling. We can increase performance of our games by initialising the objects we're going to need throughout a game prior to starting it. We are then less likely to encounter slowdown if we have to create a number of objects at once as we can simply pull existing objects out of a pool and use them.

Code

In our scene class we declare a new NSMutableArray to hold our shields:

NSMutableArray *_shieldPool;

We then move the initialisation of our shileds from our newGame method into the init method and add them to the pool rather than the scene.

// Setup shield pool
_shieldPool = [[NSMutableArray alloc] init];
// Setup shields
for (int i = 0; i < 6; i++) {
SKSpriteNode *shield = [SKSpriteNode spriteNodeWithImageNamed:@"BlocK"];
shield.name = @"shield";
shield.position = CGPointMake(35 + (50 *i), 90);
shield.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(42, 9)];
shield.physicsBody.categoryBitMask = kCCShieldCategory;
shield.physicsBody.collisionBitMask = 0;
[_shieldPool addObject:shield];
}

In our newGame method we can push all the shields into the main layer, making sure we remove them from the pool:

// Add all shields from pool to scene.
while (_shieldPool.count > 0) {
[_mainLayer addChild:[_shieldPool objectAtIndex:0]];
[_shieldPool removeObjectAtIndex:0];
}

Wherever we remove a shield node from its parent we need to push it back into the pool:

[_shieldPool addObject:secondBody.node];
07:29
Space Cannon - Spawn Shield Power Ups

In this video we add a new power up to the game. The idea is that if the player can shoot a shield that floats quickly across the screen, they get one of their missing shields returned.

Code

We create a new method to spawn the shield power ups. We use the angularVelocity property on the physics body to make it spin.

-(void)spawnShieldPowerUp
{
if (_shieldPool.count > 0) {
SKSpriteNode *shieldUp = [SKSpriteNode spriteNodeWithImageNamed:@"BlocK"];
shieldUp.name = @"shieldUp";
shieldUp.position = CGPointMake(self.size.width + shieldUp.size.width, randomInRange(150, self.size.height - 100));
shieldUp.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(42, 9)];
shieldUp.physicsBody.categoryBitMask = kCCShieldUpCategory;
shieldUp.physicsBody.collisionBitMask = 0;
shieldUp.physicsBody.velocity = CGVectorMake(-100, randomInRange(-40, 40));
shieldUp.physicsBody.angularVelocity = M_PI;
shieldUp.physicsBody.linearDamping = 0.0;
shieldUp.physicsBody.angularDamping = 0.0;
[_mainLayer addChild:shieldUp];
}
}

Note we need to declare a new category for our shield power up:

static const uint32_t kCCShieldUpCategory = 0x1 << 5;

We then create an action in the init method to periodically spawn shield power ups:

// Create spawn shield power up action.
SKAction *spawnShieldPowerUp = [SKAction sequence:@[[SKAction waitForDuration:15 withRange:4],
[SKAction performSelector:@selector(spawnShieldPowerUp) onTarget:self]]];
[self runAction:[SKAction repeatActionForever:spawnShieldPowerUp]];
06:16
Space Cannon - Make Shield Power Ups Collectable

In this video we get the shield power ups working so that we can have our shields returned to us in the middle of a game.

Code

We first need to tell sprite kit that we want to know about contacts between a ball node and a shield power up node:

ball.physicsBody.contactTestBitMask = kCCEdgeCategory | kCCShieldUpCategory;

Then we can react to those contacts in the didBeginContactMethod:

if (firstBody.categoryBitMask == kCCBallCategory && secondBody.categoryBitMask == kCCShieldUpCategory) {
// Hit a shield power up.
if (_shieldPool.count > 0 ) {
int randomIndex = arc4random_uniform((int)_shieldPool.count);
[_mainLayer addChild:[_shieldPool objectAtIndex:randomIndex]];
[_shieldPool removeObjectAtIndex:randomIndex];
[self runAction:_shieldUpSound];
}
[firstBody.node removeFromParent];
[secondBody.node removeFromParent];
}

We declare a new action to play a sound when we collect a power up:

SKAction *_shieldUpSound;

We initialise this in the init method:

_shieldUpSound = [SKAction playSoundFileNamed:@"ShieldUp.caf" waitForCompletion:NO];

We also make sure we remove shield power ups that manage to get to the other side of the screen. We do this in the didSimulatePhysics method:

[_mainLayer enumerateChildNodesWithName:@"shieldUp" usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.x + node.frame.size.width < 0) {
[node removeFromParent];
}
}];
Section 19: Challenge 5: Multi Shot Power Up
01:08
Challenge: Multi Shot Power Up

The challenge this time is to create a new type of power up that will put the cannon into a multi shot mode. When in this mode, every time the player fires, the cannon will shoot 5 cannon balls 1 after the other with a delay of 0.1 seconds between each shot. The cannon will also change to green when in this mode. The player will stay in this mode until they run out of ammo and so this means that their ammo should not increase while in multi mode. It also means their ammo should be reset to a full 5 ammo when they first enter the multi shot mode. The power up itself will use a small texture of a green cannon and will float from the left hand side of the screen to the right at the same speed as the shield power up. A new power up should be spawned for every 10th halo that the player destroys.

Assets

* GreenCannon@2x.png

* MultiShotPowerUp@2x.png

04:28
Challenge: Multi Shot Power Up Solution

The multi shot power up challenge is not a super simple one to solve. There are a number of changes we need to make throughout the code to get everything setup. In this video we look at what we need to do to solve our challenge.

Code

We need to have a way to count how many halos have been destroyed so we declare a new instance variable at the top of our game scene to act as a counter:

int _killCount;

Then in our didBeginContact method, in the if statement where we detect a contact between a ball and a halo, we can increase the counter. We can make use of the modulus operator to check if the halo we've just hit is a multiple of 10 in which case we'll call a new method, spawnMultiShotPowerUp:

_killCount++;
if (_killCount % 10 == 0) {
[self spawnMultiShotPowerUp];
}

Before we can setup a new power up we're going to need a category for its physics body. We can declare this up with our other constants in our game scene:

static const uint32_t kCCMultiUpCategory = 0x1 << 6;

We declare a new method, to spawn a multi shot power up. The method creates a new sprite node and assigns it a position to the left side of the screen but with a random vertical position. We setup the physics body of the node so that it will float across the screen from left to right and rotate. We also give it a category:

-(void)spawnMultiShotPowerUp
{
SKSpriteNode *multiUp = [SKSpriteNode spriteNodeWithImageNamed:@"MultiShotPowerUp"];
multiUp.name = @"multiUp";
multiUp.position = CGPointMake(-multiUp.size.width, randomInRange(150, self.size.height - 100));
multiUp.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:12.0];
multiUp.physicsBody.categoryBitMask = kCCMultiUpCategory;
multiUp.physicsBody.collisionBitMask = 0;
multiUp.physicsBody.velocity = CGVectorMake(100, randomInRange(-40, 40));
multiUp.physicsBody.angularVelocity = M_PI;
multiUp.physicsBody.linearDamping = 0.0;
multiUp.physicsBody.angularDamping = 0.0;
[_mainLayer addChild:multiUp];
}

We need to update the contactTestBitMask of our cannon balls so that we are notified of a collision between a ball and our new type of power up:

ball.physicsBody.contactTestBitMask = kCCEdgeCategory | kCCShieldUpCategory | kCCMultiUpCategory;

We do a bit of clean up and in our didSimulatePhysics method by removing any power up nodes that managed to make it across the screen without being hit:

[_mainLayer enumerateChildNodesWithName:@"multiUp" usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.x - node.frame.size.width > self.size.width) {
[node removeFromParent];
}
}];

We also remove any power ups that are on the screen in our gameOver method:

[_mainLayer enumerateChildNodesWithName:@"multiUp" usingBlock:^(SKNode *node, BOOL *stop) {
[node removeFromParent];
}];

Now that we've set up our power up we can start implementing its functionality. We declare a new property in the header file of our game class to indicate when our cannon should shoot multiple shots at a time:

@property (nonatomic) BOOL multiMode;

We override the setter of our multiMode property in the game scene's implementation file so that we can set the image of our cannon based on the value of the multiMode property. When we're in multi mode, the cannon should be green:

-(void)setMultiMode:(BOOL)multiMode
{
_multiMode = multiMode;
if (multiMode) {
_cannon.texture = [SKTexture textureWithImageNamed:@"GreenCannon"];
} else {
_cannon.texture = [SKTexture textureWithImageNamed:@"Cannon"];
}
}

In our didBeginContact method we add a new check for a collision between a ball and a multi shot power up. When this occurs, we remove the 2 nodes, set multiMode to yes, play a power up sound and set our ammo to 5:

if (firstBody.categoryBitMask == kCCBallCategory && secondBody.categoryBitMask == kCCMultiUpCategory) {
self.multiMode = YES;
[self runAction:_shieldUpSound];
self.ammo = 5;
[firstBody.node removeFromParent];
[secondBody.node removeFromParent];
}

We only want to allow the player 5 shots in multi mode before going back to normal. This means we need to stop our ammo from automatically increasing over time. We can set this up in our init method where we are creating the action that increments our ammo. We can wrap our increment statement in an if statement that checks that we're not in multi mode:

SKAction *incrementAmmo = [SKAction sequence:@[[SKAction waitForDuration:1],
[SKAction runBlock:^{
if (!self.multiMode) {
self.ammo++;
}
}]]];

When we do our multi shots, it would be convenient if we can just call shoot for each one of the 5 shots. Our shoot method currently contains code to check the ammo and decrement it. We remove this code and move it to the point where we're actually calling shoot:

-(void)shoot
{
// Create ball node.
CCBall *ball = [CCBall spriteNodeWithImageNamed:@"Ball"];
...

In our didSimulatePhysics method we update our call to shoot by first checking that we have ammo. We also do a check to see if we're in multi mode. If we are then we shoot another 4 times each one delayed by 0.1 seconds from the last. If we've run out of ammo, we turn multi mode off and set the ammo back to 5:

-(void)didSimulatePhysics
{
// Shoot.
if (_didShoot) {
if (self.ammo > 0) {
self.ammo--;
[self shoot];
if (self.multiMode) {
for (int i = 1; i < 5; i++) {
[self performSelector:@selector(shoot) withObject:nil afterDelay:0.1 * i];
}
if (self.ammo == 0) {
self.multiMode = NO;
self.ammo = 5;
}
}
}
_didShoot = NO;
}
...

We need to make sure that we're not in multi mode at the start of a game, so in our new game method we turn multi mode off. We also reset our counter that counts the number of halos we've destroyed:

self.multiMode = NO;
_killCount = 0;
Assets

* GreenCannon@2x.png

* MultiShotPowerUp@2x.png

08:02
Challenge: Multi Shot Power Up Solution

The multi shot power up challenge is not a super simple one to solve. There are a number of changes we need to make throughout the code to get everything setup. In this video we look at what we need to do to solve our challenge.

Code

We need to have a way to count how many halos have been destroyed so we declare a new instance variable at the top of our game scene to act as a counter:

int _killCount;

Then in our didBeginContact method, in the if statement where we detect a contact between a ball and a halo, we can increase the counter. We can make use of the modulus operator to check if the halo we've just hit is a multiple of 10 in which case we'll call a new method, spawnMultiShotPowerUp:

_killCount++;
if (_killCount % 10 == 0) {
[self spawnMultiShotPowerUp];
}

Before we can setup a new power up we're going to need a category for its physics body. We can declare this up with our other constants in our game scene:

static const uint32_t kCCMultiUpCategory = 0x1 << 6;

We declare a new method, to spawn a multi shot power up. The method creates a new sprite node and assigns it a position to the left side of the screen but with a random vertical position. We setup the physics body of the node so that it will float across the screen from left to right and rotate. We also give it a category:

-(void)spawnMultiShotPowerUp
{
SKSpriteNode *multiUp = [SKSpriteNode spriteNodeWithImageNamed:@"MultiShotPowerUp"];
multiUp.name = @"multiUp";
multiUp.position = CGPointMake(-multiUp.size.width, randomInRange(150, self.size.height - 100));
multiUp.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:12.0];
multiUp.physicsBody.categoryBitMask = kCCMultiUpCategory;
multiUp.physicsBody.collisionBitMask = 0;
multiUp.physicsBody.velocity = CGVectorMake(100, randomInRange(-40, 40));
multiUp.physicsBody.angularVelocity = M_PI;
multiUp.physicsBody.linearDamping = 0.0;
multiUp.physicsBody.angularDamping = 0.0;
[_mainLayer addChild:multiUp];
}

We need to update the contactTestBitMask of our cannon balls so that we are notified of a collision between a ball and our new type of power up:

ball.physicsBody.contactTestBitMask = kCCEdgeCategory | kCCShieldUpCategory | kCCMultiUpCategory;

We do a bit of clean up and in our didSimulatePhysics method by removing any power up nodes that managed to make it across the screen without being hit:

[_mainLayer enumerateChildNodesWithName:@"multiUp" usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.x - node.frame.size.width > self.size.width) {
[node removeFromParent];
}
}];

We also remove any power ups that are on the screen in our gameOver method:

[_mainLayer enumerateChildNodesWithName:@"multiUp" usingBlock:^(SKNode *node, BOOL *stop) {
[node removeFromParent];
}];

Now that we've set up our power up we can start implementing its functionality. We declare a new property in the header file of our game class to indicate when our cannon should shoot multiple shots at a time:

@property (nonatomic) BOOL multiMode;

We override the setter of our multiMode property in the game scene's implementation file so that we can set the image of our cannon based on the value of the multiMode property. When we're in multi mode, the cannon should be green:

-(void)setMultiMode:(BOOL)multiMode
{
_multiMode = multiMode;
if (multiMode) {
_cannon.texture = [SKTexture textureWithImageNamed:@"GreenCannon"];
} else {
_cannon.texture = [SKTexture textureWithImageNamed:@"Cannon"];
}
}

In our didBeginContact method we add a new check for a collision between a ball and a multi shot power up. When this occurs, we remove the 2 nodes, set multiMode to yes, play a power up sound and set our ammo to 5:

if (firstBody.categoryBitMask == kCCBallCategory && secondBody.categoryBitMask == kCCMultiUpCategory) {
self.multiMode = YES;
[self runAction:_shieldUpSound];
self.ammo = 5;
[firstBody.node removeFromParent];
[secondBody.node removeFromParent];
}

We only want to allow the player 5 shots in multi mode before going back to normal. This means we need to stop our ammo from automatically increasing over time. We can set this up in our init method where we are creating the action that increments our ammo. We can wrap our increment statement in an if statement that checks that we're not in multi mode:

SKAction *incrementAmmo = [SKAction sequence:@[[SKAction waitForDuration:1],
[SKAction runBlock:^{
if (!self.multiMode) {
self.ammo++;
}
}]]];

When we do our multi shots, it would be convenient if we can just call shoot for each one of the 5 shots. Our shoot method currently contains code to check the ammo and decrement it. We remove this code and move it to the point where we're actually calling shoot:

-(void)shoot
{
// Create ball node.
CCBall *ball = [CCBall spriteNodeWithImageNamed:@"Ball"];
...

In our didSimulatePhysics method we update our call to shoot by first checking that we have ammo. We also do a check to see if we're in multi mode. If we are then we shoot another 4 times each one delayed by 0.1 seconds from the last. If we've run out of ammo, we turn multi mode off and set the ammo back to 5:

-(void)didSimulatePhysics
{
// Shoot.
if (_didShoot) {
if (self.ammo > 0) {
self.ammo--;
[self shoot];
if (self.multiMode) {
for (int i = 1; i < 5; i++) {
[self performSelector:@selector(shoot) withObject:nil afterDelay:0.1 * i];
}
if (self.ammo == 0) {
self.multiMode = NO;
self.ammo = 5;
}
}
}
_didShoot = NO;
}
...

We need to make sure that we're not in multi mode at the start of a game, so in our new game method we turn multi mode off. We also reset our counter that counts the number of halos we've destroyed:

self.multiMode = NO;
_killCount = 0;
Assets

* GreenCannon@2x.png

* MultiShotPowerUp@2x.png

05:59
Challenge: Multi Shot Power Up Solution

The multi shot power up challenge is not a super simple one to solve. There are a number of changes we need to make throughout the code to get everything setup. In this video we look at what we need to do to solve our challenge.

Code

We need to have a way to count how many halos have been destroyed so we declare a new instance variable at the top of our game scene to act as a counter:

int _killCount;

Then in our didBeginContact method, in the if statement where we detect a contact between a ball and a halo, we can increase the counter. We can make use of the modulus operator to check if the halo we've just hit is a multiple of 10 in which case we'll call a new method, spawnMultiShotPowerUp:

_killCount++;
if (_killCount % 10 == 0) {
[self spawnMultiShotPowerUp];
}

Before we can setup a new power up we're going to need a category for its physics body. We can declare this up with our other constants in our game scene:

static const uint32_t kCCMultiUpCategory = 0x1 << 6;

We declare a new method, to spawn a multi shot power up. The method creates a new sprite node and assigns it a position to the left side of the screen but with a random vertical position. We setup the physics body of the node so that it will float across the screen from left to right and rotate. We also give it a category:

-(void)spawnMultiShotPowerUp
{
SKSpriteNode *multiUp = [SKSpriteNode spriteNodeWithImageNamed:@"MultiShotPowerUp"];
multiUp.name = @"multiUp";
multiUp.position = CGPointMake(-multiUp.size.width, randomInRange(150, self.size.height - 100));
multiUp.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:12.0];
multiUp.physicsBody.categoryBitMask = kCCMultiUpCategory;
multiUp.physicsBody.collisionBitMask = 0;
multiUp.physicsBody.velocity = CGVectorMake(100, randomInRange(-40, 40));
multiUp.physicsBody.angularVelocity = M_PI;
multiUp.physicsBody.linearDamping = 0.0;
multiUp.physicsBody.angularDamping = 0.0;
[_mainLayer addChild:multiUp];
}

We need to update the contactTestBitMask of our cannon balls so that we are notified of a collision between a ball and our new type of power up:

ball.physicsBody.contactTestBitMask = kCCEdgeCategory | kCCShieldUpCategory | kCCMultiUpCategory;

We do a bit of clean up and in our didSimulatePhysics method by removing any power up nodes that managed to make it across the screen without being hit:

[_mainLayer enumerateChildNodesWithName:@"multiUp" usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.x - node.frame.size.width > self.size.width) {
[node removeFromParent];
}
}];

We also remove any power ups that are on the screen in our gameOver method:

[_mainLayer enumerateChildNodesWithName:@"multiUp" usingBlock:^(SKNode *node, BOOL *stop) {
[node removeFromParent];
}];

Now that we've set up our power up we can start implementing its functionality. We declare a new property in the header file of our game class to indicate when our cannon should shoot multiple shots at a time:

@property (nonatomic) BOOL multiMode;

We override the setter of our multiMode property in the game scene's implementation file so that we can set the image of our cannon based on the value of the multiMode property. When we're in multi mode, the cannon should be green:

-(void)setMultiMode:(BOOL)multiMode
{
_multiMode = multiMode;
if (multiMode) {
_cannon.texture = [SKTexture textureWithImageNamed:@"GreenCannon"];
} else {
_cannon.texture = [SKTexture textureWithImageNamed:@"Cannon"];
}
}

In our didBeginContact method we add a new check for a collision between a ball and a multi shot power up. When this occurs, we remove the 2 nodes, set multiMode to yes, play a power up sound and set our ammo to 5:

if (firstBody.categoryBitMask == kCCBallCategory && secondBody.categoryBitMask == kCCMultiUpCategory) {
self.multiMode = YES;
[self runAction:_shieldUpSound];
self.ammo = 5;
[firstBody.node removeFromParent];
[secondBody.node removeFromParent];
}

We only want to allow the player 5 shots in multi mode before going back to normal. This means we need to stop our ammo from automatically increasing over time. We can set this up in our init method where we are creating the action that increments our ammo. We can wrap our increment statement in an if statement that checks that we're not in multi mode:

SKAction *incrementAmmo = [SKAction sequence:@[[SKAction waitForDuration:1],
[SKAction runBlock:^{
if (!self.multiMode) {
self.ammo++;
}
}]]];

When we do our multi shots, it would be convenient if we can just call shoot for each one of the 5 shots. Our shoot method currently contains code to check the ammo and decrement it. We remove this code and move it to the point where we're actually calling shoot:

-(void)shoot
{
// Create ball node.
CCBall *ball = [CCBall spriteNodeWithImageNamed:@"Ball"];
...

In our didSimulatePhysics method we update our call to shoot by first checking that we have ammo. We also do a check to see if we're in multi mode. If we are then we shoot another 4 times each one delayed by 0.1 seconds from the last. If we've run out of ammo, we turn multi mode off and set the ammo back to 5:

-(void)didSimulatePhysics
{
// Shoot.
if (_didShoot) {
if (self.ammo > 0) {
self.ammo--;
[self shoot];
if (self.multiMode) {
for (int i = 1; i < 5; i++) {
[self performSelector:@selector(shoot) withObject:nil afterDelay:0.1 * i];
}
if (self.ammo == 0) {
self.multiMode = NO;
self.ammo = 5;
}
}
}
_didShoot = NO;
}
...

We need to make sure that we're not in multi mode at the start of a game, so in our new game method we turn multi mode off. We also reset our counter that counts the number of halos we've destroyed:

self.multiMode = NO;
_killCount = 0;
Assets

* GreenCannon@2x.png

* MultiShotPowerUp@2x.png

08:35
Challenge: Multi Shot Power Up Solution

The multi shot power up challenge is not a super simple one to solve. There are a number of changes we need to make throughout the code to get everything setup. In this video we look at what we need to do to solve our challenge.

Code

We need to have a way to count how many halos have been destroyed so we declare a new instance variable at the top of our game scene to act as a counter:

int _killCount;

Then in our didBeginContact method, in the if statement where we detect a contact between a ball and a halo, we can increase the counter. We can make use of the modulus operator to check if the halo we've just hit is a multiple of 10 in which case we'll call a new method, spawnMultiShotPowerUp:

_killCount++;
if (_killCount % 10 == 0) {
[self spawnMultiShotPowerUp];
}

Before we can setup a new power up we're going to need a category for its physics body. We can declare this up with our other constants in our game scene:

static const uint32_t kCCMultiUpCategory = 0x1 << 6;

We declare a new method, to spawn a multi shot power up. The method creates a new sprite node and assigns it a position to the left side of the screen but with a random vertical position. We setup the physics body of the node so that it will float across the screen from left to right and rotate. We also give it a category:

-(void)spawnMultiShotPowerUp
{
SKSpriteNode *multiUp = [SKSpriteNode spriteNodeWithImageNamed:@"MultiShotPowerUp"];
multiUp.name = @"multiUp";
multiUp.position = CGPointMake(-multiUp.size.width, randomInRange(150, self.size.height - 100));
multiUp.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:12.0];
multiUp.physicsBody.categoryBitMask = kCCMultiUpCategory;
multiUp.physicsBody.collisionBitMask = 0;
multiUp.physicsBody.velocity = CGVectorMake(100, randomInRange(-40, 40));
multiUp.physicsBody.angularVelocity = M_PI;
multiUp.physicsBody.linearDamping = 0.0;
multiUp.physicsBody.angularDamping = 0.0;
[_mainLayer addChild:multiUp];
}

We need to update the contactTestBitMask of our cannon balls so that we are notified of a collision between a ball and our new type of power up:

ball.physicsBody.contactTestBitMask = kCCEdgeCategory | kCCShieldUpCategory | kCCMultiUpCategory;

We do a bit of clean up and in our didSimulatePhysics method by removing any power up nodes that managed to make it across the screen without being hit:

[_mainLayer enumerateChildNodesWithName:@"multiUp" usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.x - node.frame.size.width > self.size.width) {
[node removeFromParent];
}
}];

We also remove any power ups that are on the screen in our gameOver method:

[_mainLayer enumerateChildNodesWithName:@"multiUp" usingBlock:^(SKNode *node, BOOL *stop) {
[node removeFromParent];
}];

Now that we've set up our power up we can start implementing its functionality. We declare a new property in the header file of our game class to indicate when our cannon should shoot multiple shots at a time:

@property (nonatomic) BOOL multiMode;

We override the setter of our multiMode property in the game scene's implementation file so that we can set the image of our cannon based on the value of the multiMode property. When we're in multi mode, the cannon should be green:

-(void)setMultiMode:(BOOL)multiMode
{
_multiMode = multiMode;
if (multiMode) {
_cannon.texture = [SKTexture textureWithImageNamed:@"GreenCannon"];
} else {
_cannon.texture = [SKTexture textureWithImageNamed:@"Cannon"];
}
}

In our didBeginContact method we add a new check for a collision between a ball and a multi shot power up. When this occurs, we remove the 2 nodes, set multiMode to yes, play a power up sound and set our ammo to 5:

if (firstBody.categoryBitMask == kCCBallCategory && secondBody.categoryBitMask == kCCMultiUpCategory) {
self.multiMode = YES;
[self runAction:_shieldUpSound];
self.ammo = 5;
[firstBody.node removeFromParent];
[secondBody.node removeFromParent];
}

We only want to allow the player 5 shots in multi mode before going back to normal. This means we need to stop our ammo from automatically increasing over time. We can set this up in our init method where we are creating the action that increments our ammo. We can wrap our increment statement in an if statement that checks that we're not in multi mode:

SKAction *incrementAmmo = [SKAction sequence:@[[SKAction waitForDuration:1],
[SKAction runBlock:^{
if (!self.multiMode) {
self.ammo++;
}
}]]];

When we do our multi shots, it would be convenient if we can just call shoot for each one of the 5 shots. Our shoot method currently contains code to check the ammo and decrement it. We remove this code and move it to the point where we're actually calling shoot:

-(void)shoot
{
// Create ball node.
CCBall *ball = [CCBall spriteNodeWithImageNamed:@"Ball"];
...

In our didSimulatePhysics method we update our call to shoot by first checking that we have ammo. We also do a check to see if we're in multi mode. If we are then we shoot another 4 times each one delayed by 0.1 seconds from the last. If we've run out of ammo, we turn multi mode off and set the ammo back to 5:

-(void)didSimulatePhysics
{
// Shoot.
if (_didShoot) {
if (self.ammo > 0) {
self.ammo--;
[self shoot];
if (self.multiMode) {
for (int i = 1; i < 5; i++) {
[self performSelector:@selector(shoot) withObject:nil afterDelay:0.1 * i];
}
if (self.ammo == 0) {
self.multiMode = NO;
self.ammo = 5;
}
}
}
_didShoot = NO;
}
...

We need to make sure that we're not in multi mode at the start of a game, so in our new game method we turn multi mode off. We also reset our counter that counts the number of halos we've destroyed:

self.multiMode = NO;
_killCount = 0;
Assets

* GreenCannon@2x.png

* MultiShotPowerUp@2x.png

Students Who Viewed This Course Also Viewed

  • Loading
  • Loading
  • Loading

Instructor Biography

Eliot Arntz, iOS Developer and Teacher

Eliot regularly teaches iOS development classes and workshops at General Assembly and guest lectures for companies and development boot camps around NYC. He also taught the inaugural class for Coalition for Queens which focused on increasing diversity in iOS development. He also coaches students in a one-on-one environment with a focus on transitioning to full time development. Eliot cofounded and organizes the iOS Office Hours meetup NYC.

In his free time he works as a contractor for startups focusing on agile development.

Find me on twitter @EliotArntz - I check my direct messages quite frequently.

Instructor Biography

Mr. John Omar, Designer, developer, teacher at Bitfountain

John was the lead iOS developer at Fast Society and Cameo until he started Bitfountain in 2012. The apps that John has contributed to have appeared in TechCrunch, Mashable and the New York Times. At Bitfountain, John oversees all projects from a technical and strategic perspective. At Bitfountain, John has taught over 120,000 students how to code online.

Instructor Biography

Jed Hastwell, iOS Games Developer and Teacher

Jed worked for many years as a web developer for various companies in Australia and the UK. He co-founded indie games development studio, Whistlefire that recently launched their first game for iOS, Jungle Raider. He currently resides in Berlin where he is working for Toywheel, designing and building mobile games and digital toys for kids.

Ready to start learning?
Take This Course