How To Make a Breakout Game with SpriteKit and Swift: Part 2
Learn how to make a breakout game for iOS with Sprite Kit and Swift via a fun hands-on tutorial. By Michael Briscoe.
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
How To Make a Breakout Game with SpriteKit and Swift: Part 2
25 mins
Update 9/22/16: This tutorial has been updated for Xcode 8 and Swift 3 by Michael Briscoe. Original post by Tutorial Team member Barbara Reichart.
Update 9/22/16: This tutorial has been updated for Xcode 8 and Swift 3 by Michael Briscoe. Original post by Tutorial Team member Barbara Reichart.
Welcome back to our How To Create a Breakout Game with Sprite Kit and Swift tutorial series!
In the first part of the series, you added a moving paddle and ball to the game.
In this second and final part of the series, you’ll add the bricks to the game along with the rest of the gameplay logic.
This tutorial begins where the previous tutorial left off. If you don’t have it already, here’s the example project up to this point.
Bamboo Blocks
Now that you’ve got the ball bouncing around and making contact, let’s add some bamboo blocks to break. This is a breakout game after all!
Go to GameScene.swift and add the following code to didMove(to:)
to introduce the blocks to the scene:
// 1
let numberOfBlocks = 8
let blockWidth = SKSpriteNode(imageNamed: "block").size.width
let totalBlocksWidth = blockWidth * CGFloat(numberOfBlocks)
// 2
let xOffset = (frame.width - totalBlocksWidth) / 2
// 3
for i in 0..<numberOfBlocks {
let block = SKSpriteNode(imageNamed: "block.png")
block.position = CGPoint(x: xOffset + CGFloat(CGFloat(i) + 0.5) * blockWidth,
y: frame.height * 0.8)
block.physicsBody = SKPhysicsBody(rectangleOf: block.frame.size)
block.physicsBody!.allowsRotation = false
block.physicsBody!.friction = 0.0
block.physicsBody!.affectedByGravity = false
block.physicsBody!.isDynamic = false
block.name = BlockCategoryName
block.physicsBody!.categoryBitMask = BlockCategory
block.zPosition = 2
addChild(block)
}
This code creates eight blocks that are centered on the screen.
- Some useful constants like the number of blocks you want and their width.
- Here you calculate the x offset. This is the distance between the left border of the screen and the first block. You calculate it by subtracting the width of all the blocks from the screen width and then dividing it by two.
- Create the blocks, configure each with the proper physics properties, and position each one using blockWidth, and xOffset.
Build and run your game and check it out!
The blocks are now in place. But in order to listen to collisions between the ball and blocks, you must update the contactTestBitMask
of the ball. Still in GameScene.swift, edit this already existing line of code in didMove(to:)
to add an extra category to it:
ball.physicsBody!.contactTestBitMask = BottomCategory | BlockCategory
The above executes a bitwise OR
operation on BottomCategory
and BlockCategory
. The result is that the bits for those two particular categories are set to one while all other bits are still zero. Now, collisions between ball and floor as well as ball and blocks will be sent to to the delegate.
Breaking Bamboo
Now that you're all set to detect collisions between the ball and blocks, let's add a helper method to GameScene.swift to remove the blocks from the scene:
func breakBlock(node: SKNode) {
let particles = SKEmitterNode(fileNamed: "BrokenPlatform")!
particles.position = node.position
particles.zPosition = 3
addChild(particles)
particles.run(SKAction.sequence([SKAction.wait(forDuration: 1.0),
SKAction.removeFromParent()]))
node.removeFromParent()
}
This method takes an SKNode
. First, it creates an instance of SKEmitterNode
from the BrokenPlatform.sks file, then sets it's position to the same position as the node. The emitter node's zPosition
is set to 3, so that the particles appear above the remaining blocks. After the particles are added to the scene, the node (bamboo block) is removed.
The only thing left to do is to handle the delegate notifications accordingly. Add the following to the end of didBegin(_:)
:
if firstBody.categoryBitMask == BallCategory && secondBody.categoryBitMask == BlockCategory {
breakBlock(node: secondBody.node!)
//TODO: check if the game has been won
}
The above lines check whether the collision is between the ball and a block. If this is the case, you pass the node to the breakBlock(node:)
method and the block is removed from the scene with a particle animation flourish!
Build and run. Blocks should now break apart when the ball hits them.
Adding Gameplay
Now that you have all the elements of your breakout game set up, it's time for the player to experience the the thrill of victory, or the agony of defeat.
Understanding State Machines
Most gameplay logic is governed by the current state of the game. For example, if the game is in the "main menu" state, the player shouldn't move, but if the game is in the "play" state, it should.
A lot of simple games manage the state by using Boolean variables within the update loop. By using a state machine, you can better organize your code as your game becomes more complex.
A state machine manages a group of states, with a single current state and a set of rules for transitioning between states. As the state of the game changes, the state machine will run methods upon exiting the previous state and entering the next. These methods can be used to control gameplay from within each state. After a successful state change, the machine will then execute the current state's update loop.
Apple introduced the GameplayKit framework in iOS 9, which has built-in support for state machines and makes working with them easy. GameplayKit is beyond the scope of this tutorial, but for now, you'll use two of its many classes: the GKStateMachine
and GKState
classes.
Adding States
In Bamboo Breakout there are three game states:
- WaitingForTap: The game has loaded and is ready to play.
- Playing: The game is actively playing.
- GameOver: The game is over with either a win or loss.
To save time these three GKState
classes have already been added to your project (check out the Game States group if you're curious). To create the state machine, first add the following import statement at the top of the GameScene.swift file:
import GameplayKit
Next, insert this class variable just below var isFingerOnPaddle = false
:
lazy var gameState: GKStateMachine = GKStateMachine(states: [
WaitingForTap(scene: self),
Playing(scene: self),
GameOver(scene: self)])
By defining this variable, you've effectively created the state machine for Bamboo Breakout. Notice that you're initializing GKStateMachine
with an array of GKState
subclasses.