Sprite Kit for Kids with Swift
Learn how to make a simple iPhone game using Sprite Kit – by a 13-year old game developer! By Ajay Venkat.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Making your Enemies Move
First, let’s tweak the enemies so when they spawn they are fully offscreen (rather than being halfway visible on the screen). This will make the player not realize they’re popping in from nowhere!
To fix this, update the line in spawnEnemy()
that sets the enemy sprite’s position to the following:
enemy.position = CGPoint(x: frame.size.width + enemy.size.width/2,
y: frame.size.height * random(min: 0, max: 1))
Now, let’s make your game a bit more interesting by making your enemies move from one side of your screen to the other by using more actions.
Add this line to the end of spawnEnemy()
:
enemy.runAction(
SKAction.moveByX(-size.width - enemy.size.width, y: 0.0,
duration: NSTimeInterval(random(min: 1, max: 2))))
Let me explain this code to you :
- It runs an
SKAction
on the enemy sprite. - This
SKAction
contains amoveByX()
method which tells the enemy to move on the X axis by a certain amount. Here you set it to move the entire length of the screen to the left (-size.width) and also the full length of the sprite (-enemy.size.width). - The
SKAction
has a duration parameter which indicates how long it should take for the sprite to move that amount. Here, you set it to move a random value between 1-2 seconds, so some enemies will be faster than others.
Build and run your app to see if the enemies are moving to the end of your screen and disappearing completely. It should look something like this:
Now you need to make your player move with physics!
Moving Sprites with Physics
In this game, you are going to make it so that the monkey falls to the bottom of the screen, unless you tap the screen – in which case the monkey will jump up.
You could do this by moving the monkey with a SKAction
, just like you did for the enemies, but it’s easier to let Sprite Kit’s built-in physics engine move the monkey instead.
Let’s try this out. Still in GameScene.swift, and add the following code to the end of didMoveToView(_:)
:
player.physicsBody = SKPhysicsBody(circleOfRadius:player.frame.size.width * 0.3)
player.physicsBody?.allowsRotation = false
The first line creates a physics body for the monkey. This tells the physics engine to start controlling the monkey’s movement, making him “fall” over time according to gravity and other forces.
Note the shape of the physics body is a circle, that roughly matches up to the monkey. You don’t need physics shapes to match exactly, just whatever works for your game. You also specify that the physics body shouldn’t rotate.
Build and run, and watch as your monkey falls down and off the screen. Cool, huh?
Physics Impulses
It’s no fair if the monkey falls right off the screen, so you’re going to use physics to make the monkey jump.
To do this, add this new method after spawnEnemy()
:
func jumpPlayer() {
// 1
let impulse = CGVector(dx: 0, dy: 75)
// 2
player.physicsBody?.applyImpulse(impulse)
}
Let’s go over this line by line:
- First you need to specify the amount to make the monkey jump, by creating a
CGVector
with an impulse amount. I came up with these values by trial and error. - Then you use
applyImpulse()
, passing in the impulse you provided earlier. This will convert the impulse into both linear and angular velocity so it will move through space and also rotate, that’s why you locked the rotation earlier.
This code has not been called yet so you cannot make your player jump yet. To actually make your player jump you need to override a method that is called when the player taps the screen. Copy this code under jumpPlayer()
:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
jumpPlayer()
}
This simply calls the method you wrote whenever the user taps.
Almost done – add this code to the end of didMoveToView(_:)
:
// 1
let collisionFrame = CGRectInset(frame, 0, -self.size.height * 0.2)
// 2
physicsBody = SKPhysicsBody(edgeLoopFromRect: collisionFrame)
This code creates a special physics body on the edge of the screen so the monkey doesn’t fly or fall of into space. Let’s review this line by line:
- First you create a rectangle that specifies the bounds of the monkey’s movement.
CGRectInset()
allows you to grow or shrink a rectangle; you use it to grow the frame of the screen by 20% above and below the area, to let the monkey go slightly offscreen but not a crazy amount. - You then set the physics body of the scene itself. Last time you created a physics body that was a circle shape; here you make it an edge loop, which is a fancy way of saying “the edges of a rectangle”.
Build and run your project, and you should see the following:
Holy bouncing monkey!
Collision Detection
Now you can jump to avoid enemies… but if you crash into them, nothing happens.
You need to add collision detection into your game. To do this, here’s what you need to do:
- Create a physics body for each sprite: Right now, you’ve created a physics body for your space monkey, but you haven’t created one for the enemies – so you’ll need to create physics bodies for them too.
- Set category and contacts for each physics body: Next you need to set a category on each physics body, which tells Sprite Kit which group the sprite is in. This way, you can have one category for the player and one category for the enemy. You can also configure your physics body to register “contacts” with certain other groups of physics bodies.
- Set Contact Delegate: Using the Contact Delegate you can set a contact delegate on the world to be told when two physics bodies contact with each other. Then you find out which category the physics body is in, and if it is the Enemy and the Monkey. Game Over!
Remember how you added the physics body to your monkey? Well now you need to add it to your enemy sprite so they can actually collide.
Start by adding this enumeration to the very top of GameScene.swift
.
enum BodyType: UInt32 {
case player = 1
case enemy = 2
case ground = 4
}
All you do here is create categories for each sprite. The ground number is not for a sprite but is instead for the frame of the app so if the monkey collides with the frame it bounces of the frame so the monkey will not keep falling!
Next, mark GameScene as implementing the SKPhysicsContactDelegate
protocol:
class GameScene: SKScene, SKPhysicsContactDelegate {
You can think of a protocol as a promise that your class will implement certain methods. Here you’re promising that you’ll be implementing a method that handles when two physics bodies contact each other.
After this make the contactDelegate
of the physics world equal to self. Add this code to the end of didMoveToView(_:)
:
physicsWorld.contactDelegate = self
This tells the physics world to call a method on your class when two physics bodies contact each other (the one you will write soon).
Now add this code to the end of spawnEnemy()
:
// 1
enemy.physicsBody = SKPhysicsBody(circleOfRadius: enemy.size.width/4)
// 2
enemy.physicsBody?.dynamic = false
// 3
enemy.physicsBody?.affectedByGravity = false
// 4
enemy.physicsBody?.allowsRotation = false
// 5
enemy.physicsBody?.categoryBitMask = BodyType.enemy.rawValue
// 6
enemy.physicsBody?.contactTestBitMask = BodyType.player.rawValue
// 7
enemy.physicsBody?.collisionBitMask = 0
Let me explain this code a little.
- Creates a physics body for the enemy. Physics bodies don’t have to be an exact match to the shape of the sprite; just an estimate. Here you use a circle shape, setting the radius to 1/4’s of the sprite’s width to make the collision a bit forgiving.
- Turns
dynamic
off. This allows the physics to control the sprite. - Stops sprite from being affected by gravity. This is self explanatory, basically the enemy sprite will not be affected by gravity produced by the physics.
- Stops sprite from rotating. This code will stop the object from rotating when collided by other physics bodies.
- Sets the category bit mask to be the enemy category you defined earlier.
- This instructs Sprite Kit to notify you when an enemy contacts a player.
- The
collisionBitMask
means, if you for example set thecollisionBitMask
to the player, the player and the enemy will bounce of each other. You don’t want anything to bounce of anything so you set it to 0.
Now add this to the end of didMoveToView(_:)
:
physicsBody?.categoryBitMask = BodyType.ground.rawValue
player.physicsBody?.categoryBitMask = BodyType.player.rawValue
player.physicsBody?.contactTestBitMask = BodyType.enemy.rawValue
player.physicsBody?.collisionBitMask = BodyType.ground.rawValue
This sets up the categories and collision bit masks for the player and ground so they collide with each other, and sets the player to register a “contact” with enemies.
Now for the most important part. Actually doing something about the collision. For this, you need to implement the method you “promised” to implement earlier to handle contacts:
func didBeginContact(contact: SKPhysicsContact) {
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch(contactMask) {
case BodyType.player.rawValue | BodyType.enemy.rawValue:
let secondNode = contact.bodyB.node
secondNode?.removeFromParent()
let firstNode = contact.bodyA.node
firstNode?.removeFromParent()
default:
return
}
}
Since you set the scene as the contactDelegate
of the physics world earlier, this method will be called whenever two physics bodies collide.
This method combines the two bitmasks into a single contact mask, and checks to see if they are the combination of the player and the enemy. If so, it removes the player and the enemy from the scene.
Build and run, and you’ll see the following:
That is AWESOME!!!
In the next section you have to quite a lot of work, you have to change basically everything. Well thats going to be fun. You will mainly add a game over screen.