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.

Leave a rating/review
Save for later
Share

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.

  1. Some useful constants like the number of blocks you want and their width.
  2. 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.
  3. 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!

Bamboo Blocks

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.

Note: Emitter nodes are a special type of nodes that display particle systems created in the Scene Editor. To check it out for yourself and see how it's configured, open BrokenPlatform.sks, which is a particle system I've created for you for this tutorial. To learn more about particle systems, check out our book 2D iOS & tvOS Games by Tutorials, which has a detailed chapter on the subject.

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.

Breaking Bamboo

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.

State Machine Flow

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.

Michael Briscoe

Contributors

Michael Briscoe

Author

Over 300 content creators. Join our team.