## How To Make a Breakout Game with SpriteKit and Swift: Part 2

Michael Briscoe

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..

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!

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.

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.

In Bamboo Breakout there are three game states:

• 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.

### WaitingForTap State

The `WaitingForTap` state is when the game has loaded and is ready to begin. The player is prompted to “Tap to Play”, and the game waits for a touch event before entering the play state.

Start by adding the following code to the end of the `didMove(to:)` method:

 ```let gameMessage = SKSpriteNode(imageNamed: "TapToPlay") gameMessage.name = GameMessageName gameMessage.position = CGPoint(x: frame.midX, y: frame.midY) gameMessage.zPosition = 4 gameMessage.setScale(0.0) addChild(gameMessage)   gameState.enter(WaitingForTap.self)```

This creates a sprite that displays the “Tap to Play” message, later it will also be used to display “Game Over”. You are also telling the state machine to enter the `WaitingForTap` state.

While you are in `didMove(to:)` also remove the line that reads:

 `ball.physicsBody!.applyImpulse(CGVector(dx: 2.0, dy: -2.0)) // REMOVE`

You’ll move this line to the play state a little later in this tutorial.

Now, open the WaitingForTap.swift file located in the Game States group. Replace `didEnter(from:)` and `willExit(to:)` with this code:

 ```override func didEnter(from previousState: GKState?) { let scale = SKAction.scale(to: 1.0, duration: 0.25) scene.childNode(withName: GameMessageName)!.run(scale) }   override func willExit(to nextState: GKState) { if nextState is Playing { let scale = SKAction.scale(to: 0, duration: 0.4) scene.childNode(withName: GameMessageName)!.run(scale) } }```

When the game enters the `WaitingForTap` state, the `didEnter(from:)` method is executed. This function simply scales up the “Tap to Play” sprite, prompting the player to begin.

When the game exits the `WaitingForTap` state, and enters the `Playing` state, then the `willExit(to:)` method is called, and the “Tap to Play” sprite is scaled back to 0.

Do a build and run, and tap to play!

Okay, so nothing happens when you tap the screen. That’s what the next game state is for!

### Playing State

The `Playing` state starts the game, and manages the balls velocity.

First, switch back to the GameScene.swift file and implement this helper method:

 ```func randomFloat(from: CGFloat, to: CGFloat) -> CGFloat { let rand: CGFloat = CGFloat(Float(arc4random()) / 0xFFFFFFFF) return (rand) * (to - from) + from }```

This handy function returns a random float between two passed in numbers. You’ll use it to add some variability to the balls starting direction.

Now, open the Playing.swift file located in the Game States group. First, add this helper method:

 ```func randomDirection() -> CGFloat { let speedFactor: CGFloat = 3.0 if scene.randomFloat(from: 0.0, to: 100.0) >= 50 { return -speedFactor } else { return speedFactor } }```

This code just “flips a coin” and returns either a positive, or negative number. This adds a bit of randomness to the direction of the ball.

Next, add this code to `didEnter(from:)`:

 ```if previousState is WaitingForTap { let ball = scene.childNode(withName: BallCategoryName) as! SKSpriteNode ball.physicsBody!.applyImpulse(CGVector(dx: randomDirection(), dy: randomDirection())) }```

When the game enters the `Playing` state, the ball sprite is retrieved and it’s `applyImpulse(_:)` method is fired, setting it in motion.

Next, add this code to the `update(deltaTime:)` method:

 ```let ball = scene.childNode(withName: BallCategoryName) as! SKSpriteNode let maxSpeed: CGFloat = 400.0   let xSpeed = sqrt(ball.physicsBody!.velocity.dx * ball.physicsBody!.velocity.dx) let ySpeed = sqrt(ball.physicsBody!.velocity.dy * ball.physicsBody!.velocity.dy)   let speed = sqrt(ball.physicsBody!.velocity.dx * ball.physicsBody!.velocity.dx + ball.physicsBody!.velocity.dy * ball.physicsBody!.velocity.dy)   if xSpeed <= 10.0 { ball.physicsBody!.applyImpulse(CGVector(dx: randomDirection(), dy: 0.0)) } if ySpeed <= 10.0 { ball.physicsBody!.applyImpulse(CGVector(dx: 0.0, dy: randomDirection())) }   if speed > maxSpeed { ball.physicsBody!.linearDamping = 0.4 } else { ball.physicsBody!.linearDamping = 0.0 }```

The `update(deltaTime:)` method will be called every frame while the game is in the `Playing` state. You get the ball and check its velocity, essentially the movement speed. If the x or y velocity falls below a certain threshold, the ball could get stuck bouncing straight up and down, or side to side. If this happens another impulse is applied, kicking it back into an angular motion.

Also, the ball’s speed can increase as it’s bouncing around. If itâ€™s too high, you increase the linear damping so that the ball will eventually slow down.

Now that the Playing state is set up, it’s time to add the code to start the game!

Back in GameScene.swift replace `touchesBegan(_:with:)` with this new implementation:

 ```override func touchesBegan(_ touches: Set, with event: UIEvent?) { switch gameState.currentState { case is WaitingForTap: gameState.enter(Playing.self) isFingerOnPaddle = true   case is Playing: let touch = touches.first let touchLocation = touch!.location(in: self)   if let body = physicsWorld.body(at: touchLocation) { if body.node!.name == PaddleCategoryName { isFingerOnPaddle = true } }   default: break } }```

This enables the game to check the game’s current state, and change the state accordingly. Next, you need to override the `update(_:)` method and implement it like so:

 ```override func update(_ currentTime: TimeInterval) { gameState.update(deltaTime: currentTime) }```

The `update(_:)` method is called before each frame is rendered. This is where you call the `Playing` state’s `update(deltaTime:)` method to manage the ball’s velocity.

Give the game a build and run, then tap the screen to see the state machine in action!

### GameOver State

The `GameOver` state occurs when the all the bamboo blocks are crushed, or the ball hits the bottom of the screen.

Open the GameOver.swift file located in the Game States group, and add these lines to `didEnter(from:)`:

 ```if previousState is Playing { let ball = scene.childNode(withName: BallCategoryName) as! SKSpriteNode ball.physicsBody!.linearDamping = 1.0 scene.physicsWorld.gravity = CGVector(dx: 0.0, dy: -9.8) }```

When the game enters the `GameOver` state, linear damping is applied to the ball and gravity is restored, causing the ball to drop to the floor and slow down.

That’s it for the `GameOver` state. Now it’s time to implement the code that determines if the player wins or loses the game!

## You win some, you lose some

Now that you have the state machine all set, you have most of your game play finished. What you need now is a way to win or lose the game.

Start by opening GameScene.swift and adding this helper method:

 ```func isGameWon() -> Bool { var numberOfBricks = 0 self.enumerateChildNodes(withName: BlockCategoryName) { node, stop in numberOfBricks = numberOfBricks + 1 } return numberOfBricks == 0 }```

This method checks to see how many bricks are left in the scene by going through all the sceneâ€™s children. For each child, it checks whether the child name is equal to `BlockCategoryName`. If there are no bricks left, the player has won the game and the method returns `true`.

Now, add this property to the top of the class, just below the `gameState` property:

 ```var gameWon : Bool = false { didSet { let gameOver = childNode(withName: GameMessageName) as! SKSpriteNode let textureName = gameWon ? "YouWon" : "GameOver" let texture = SKTexture(imageNamed: textureName) let actionSequence = SKAction.sequence([SKAction.setTexture(texture), SKAction.scale(to: 1.0, duration: 0.25)])   gameOver.run(actionSequence) } }```

Here you create the `gameWon` variable and attach the `didSet` property observer to it. This allows you to observe changes in the value of a property and react accordingly. In this case, you change the texture of the game message sprite to reflect whether the game is won or lost, then display it on screen.

Note: Property Observers have a parameter that allows you to check the new value of the property (in `willSet`) or its old value (in `didSet`) allowing value changes comparison right when it occurs. These parameters have default names if you do not provide your own, respectively `newValue` and `oldValue`. If you want to know more about this, check the Swift Programming Language documentation here: The Swift Programming Language: Declarations

Next, let’s edit the `didBegin(_:)` method as follows:

First, add this line to the very top of `didBegin(_:)`:

 ```if gameState.currentState is Playing { // Previous code remains here... } // Don't forget to close the 'if' statement at the end of the method.```

This prevents any contact when the game is not in play.

Then replace this line:

 `print("Hit bottom. First contact has been made.")`

With the following:

 ```gameState.enter(GameOver.self) gameWon = false```

Now when the ball hits the bottom of the screen the game is over.

And replace the `// TODO:` with:

 ```if isGameWon() { gameState.enter(GameOver.self) gameWon = true }```

When all the blocks are broken you win!

Finally, add this code to `touchesBegan(_:with:)` just above `default`:

 ```case is GameOver: let newScene = GameScene(fileNamed:"GameScene") newScene!.scaleMode = .aspectFit let reveal = SKTransition.flipHorizontal(withDuration: 0.5) self.view?.presentScene(newScene!, transition: reveal)```

Your game is now complete! Give it a build and run!

## Finishing Touches

Now that Bamboo Breakout is complete, let’s kick it up a notch by adding a little juice! You’ll do this by adding some sound effects whenever the ball makes contact, and when the blocks are broken. You’ll also add a quick blast of music when the game is over. Finally, you’ll add a particle emitter to the ball, so that it leaves a trail as it bounces around the screen!

To save time the project has the various sound files already imported. To start, make sure that GameScene.swift is open, then add the following constants to the top of the class, just after the `gameWon` variable:

 ```let blipSound = SKAction.playSoundFileNamed("pongblip", waitForCompletion: false) let blipPaddleSound = SKAction.playSoundFileNamed("paddleBlip", waitForCompletion: false) let bambooBreakSound = SKAction.playSoundFileNamed("BambooBreak", waitForCompletion: false) let gameWonSound = SKAction.playSoundFileNamed("game-won", waitForCompletion: false) let gameOverSound = SKAction.playSoundFileNamed("game-over", waitForCompletion: false)```

You define a series of SKAction constants, each of which will load and play a sound file. Because you define these actions before you need them, they are preloaded into memory, which prevents the game from stalling when you play the sounds for the first time.

Next, update the line that sets the ball’s `contactTestBitMask` within the `didMove(to:)` method, to the following:

 `ball.physicsBody!.contactTestBitMask = BottomCategory | BlockCategory | BorderCategory | PaddleCategory`

Nothing new here, you’re just adding the `BorderCategory` and `PaddleCategory` to the ball’s `contactTestBitMask` so that you can detect contact with the borders of the screen, and when the ball makes contact with the paddle.

Let’s edit `didBegin(_:)` to include the sound effects by adding the following lines right after the if/else statement where you set up `firstBody` and `secondBody` accordingly:

 ```// 1 if firstBody.categoryBitMask == BallCategory && secondBody.categoryBitMask == BorderCategory { run(blipSound) }   // 2 if firstBody.categoryBitMask == BallCategory && secondBody.categoryBitMask == PaddleCategory { run(blipPaddleSound) }```

This code checks for two new collisions:

1. React to ball bouncing off screen borders by playing `blipSound`.
2. React to paddle contact by playing `blipPaddleSound`.

Of course you want a satisfying crunching sound when the ball breaks the blocks, so add this line to the top of `breakBlock(node:)`:

 `run(bambooBreakSound)`

Lastly, at the top of the class insert the following line within the `didSet` property observer for the `gameWon` variable:

 `run(gameWon ? gameWonSound : gameOverSound)`

### One more thing…

Now let’s add a particle system to the ball, so that it leaves a flaming trail as it bounces around!

Add this code to `didMove(to:)`:

 ```// 1 let trailNode = SKNode() trailNode.zPosition = 1 addChild(trailNode) // 2 let trail = SKEmitterNode(fileNamed: "BallTrail")! // 3 trail.targetNode = trailNode // 4 ball.addChild(trail)```

Let’s review this section by section:

1. Create an `SKNode` to serve as the `targetNode` for the particle system.
2. Create an `SKEmitterNode` from the BallTrail.sks file.
3. Set the `targetNode` to the `trailNode`. This anchors the particles so that they leave a trail, otherwise they would follow the ball.
4. Attach the `SKEmitterNode` to the ball by adding it as a child node.

That’s it – you’re all done! Build and run to see how much more polished your game feels with a little juice:

## Where To Go From Here?

You can download the final project for the Sprite Kit Breakout Game that youâ€™ve made in this tutorial.

This is a simple implementation of Breakout. But now that you have this working, thereâ€™s a lot more you can do. You could add scoring, or you could extend this code to give the blocks hit points, have different types of blocks, and make the ball have to hit some of them (or all of them) a number of times before they are destroyed. You could add blocks which drop bonuses or power-ups, let the paddle shoot lasers toward the blocks, whatever you dream up!

If you want to learn more about Sprite Kit, you should check out our book 2D iOS & tvOS Games by Tutorials:

In this book we’ll teach you everything you need to know to make great games for iOS & tvOS – from physics, to tile maps, to particle systems, and even how to make your games “juicy” with polish and special effects.

I hope you enjoyed this tutorial, and if you have any questions or comments, please join the forum discussion below!

Michael Briscoe

Michael is an Independent Software Developer with over 30 years of programming experience. Learning BASIC on a Commodore 64 way back in 1984, he's been hooked on coding ever since. He enjoys creating simulations and games for all Apple platforms.

â€‹

... 19 total!

... 16 total!

... 30 total!

... 15 total!

... 11 total!

... 10 total!

... 11 total!

... 11 total!

... 11 total!