
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:
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];
Apple's introduction to Sprite Kit: Sprite Kit Programming Guide: About Sprite Kit
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.
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.
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.
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
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.
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
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.
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.
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
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.
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
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.
We can convert degrees to radians using: radians = degrees * (π / 180)
We can convert radians to degrees using: degrees = radians * (180 / π)
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
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
Shoot Method Review
This is a review video of the code we've set up for the shoot method of our Space Cannon game.
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];
}
}];
}
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.
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
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
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.
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:
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:
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
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.
* 8Ball@2x.png
* BeachBall@2x.png
* SoccerBall@2x.png
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.
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];
}
}
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.
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];
}
}
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.
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];
}
}
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.
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
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.
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
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
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
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.
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.
Apples documentation explains in more detail the effects of the vairious properites of a particle emitter: Particle Emitter Editor Guide
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
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.
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;
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--;
...
}
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.
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:
-(void)shoot
{
int availableAmmo = 2;
for (SKNode *node in _mainLayer.children) {
if([node.name isEqualToString:@"ball"])
{
availableAmmo--;
}
}
if (availableAmmo > 0) {
...
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.
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];
}
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.
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.
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];
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.
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.
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"];
}
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:
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!