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

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
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Making Contact

So far, you have a ball that bounces around the screen and a paddle you can move around via touch. While this is really fun to toy around with, to make it a game you need a way for your player to win and lose the game. Losing should happen when the ball touches the bottom of the screen instead of hitting the paddle. But how do you detect this scenario using Sprite Kit?

Sprite Kit can detect the contact between two physics bodies. However, for this to work properly, you need to follow a few steps to set things up a certain way. Here’s a short overview. Each of the steps will be explained in more detail later. So, here you go:

  • Set up physics body bit masks: In your game you might have several different types of physics bodies – for example, you can have the player, enemies, bullets, bonus items etc. To uniquely identify these different types of physics bodies, each physics body can be configured using several bit masks. These include:
    • categoryBitMask: This bit mask identifies the category a body belongs to. You use categories to define a body’s interaction with other bodies. The categoryBitMask is a 32-bit integer, where each bit represents one category. So you can have up to 32 custom categories in your game. This should be enough for most games to set up a separate category for each object type. For more complex games it can be useful to remember that each body can be in several categories. So through smart design of the categories you could even overcome the limitation of 32 categories.
    • contactTestBitMask: Setting a bit in this bitmask causes Sprite Kit to notify the contact delegate when the body touches another body assigned to that particular category. By default, all bits are cleared – you are not notified about any contacts between objects. For best performance you should only set bits in the contacts mask for interactions you are really interested in.
    • collisionBitMask: Here, you can define which bodies can collide with this physics body. You can use this, for example, to avoid collision calculations for a very heavy body when it collides with a much lighter body as this would only make negligible changes to the heavy body’s velocity. But you can also use it to allow two bodies to pass right through each other.
  • Set and implement the contact delegate: The contact delegate is a property of SKPhysicsWorld. It will be notified when two bodies with the proper contactTestBitMasks begin and end colliding.

But why are they useful? Well, they allow you to get state information out of a binary number and give you the ability to change a specific bit in a binary number to set a specific state. You can do this with the binary operators AND and OR, like so:

Bitmask power :]

This allows you to store a lot of information in a really compact way using just one variable and still be able to access and manipulate the stored information.

Note: Bit masks?!? In case you’ve never worked with bit masks, don’t panic! At first glance they might look complicated, but they are really useful.
So what is a bitmask? A bitmask is a multi-digit binary number. Something like this: 1011 1000. Not so complicated at all.

But why are they useful? Well, they allow you to get state information out of a binary number and give you the ability to change a specific bit in a binary number to set a specific state. You can do this with the binary operators AND and OR, like so:

Bitmask power :)

Bitmask power :]

This allows you to store a lot of information in a really compact way using just one variable and still be able to access and manipulate the stored information.

Bitmask power :)

Bitmask power :]

3, 2, 1, Contact: Code

First, create constants for the different categories. Do this by adding the following lines below the other constants for the category names in GameScene.swift:

let BallCategory   : UInt32 = 0x1 << 0
let BottomCategory : UInt32 = 0x1 << 1
let BlockCategory  : UInt32 = 0x1 << 2
let PaddleCategory : UInt32 = 0x1 << 3
let BorderCategory : UInt32 = 0x1 << 4

The above defines five categories. You do this by setting the last bit to 1 and all other bits to zero. Then using the << operator you shift this bit to the left. As a result, each of the category constants has only one bit set to 1 and the position of the 1 in the binary number is unique across the four categories.

For now you only need the category for the bottom of the screen and the ball, but you should set up the others anyway as you will need them later on as you expand the gameplay.

Once you have the constants in place, create a physics body that stretches across the bottom of the screen. Try to do this by yourself since this uses principles you've already learned when creating the barriers around the screen edges.

[spoiler title="Create an edge-based body that spans the bottom of the screen"]
Add the following to didMove(to:) in GameScene.swift:

let bottomRect = CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.size.width, height: 1)
let bottom = SKNode()
bottom.physicsBody = SKPhysicsBody(edgeLoopFrom: bottomRect)
addChild(bottom)

[/spoiler]

Now, let’s get to the meat of establishing contact. First, set up the categoryBitMasks for the game objects by adding the following code to didMove(to:):

let paddle = childNode(withName: PaddleCategoryName) as! SKSpriteNode
    
bottom.physicsBody!.categoryBitMask = BottomCategory
ball.physicsBody!.categoryBitMask = BallCategory
paddle.physicsBody!.categoryBitMask = PaddleCategory
borderBody.categoryBitMask = BorderCategory

This code simply assigns the constants you created earlier to the corresponding physics body’s categoryBitMask.

Now, set up the contactTestBitMask by adding this (again to didMove(to:)):

ball.physicsBody!.contactTestBitMask = BottomCategory

For now you only want to be notified when the ball makes contact with the bottom of the screen.

Next, make GameScene the delegate for all physics contact.

Change this line:

class GameScene: SKScene {

To this line:

class GameScene: SKScene, SKPhysicsContactDelegate {

This makes it official: GameScene is now an SKPhysicsContactDelegate (as it conforms to the SKPhysicsContactDelegate protocol) and will receive collision notifications for all configured physics bodies. Hurrah!

Now you need to set GameScene as the delegate in the physicsWorld. So, add this to didMove(to:) right below physicsWorld.gravity = CGVector(dx: 0.0, dy: 0.0):

physicsWorld.contactDelegate = self

Setting up the SKPhysicsContactDelegate

Setting up the SKPhysicsContactDelegate

Setting up the SKPhysicsContactDelegate

Finally, you need to implement didBegin(_:) to handle the collisions. Add the following method to GameScene.swift:

func didBegin(_ contact: SKPhysicsContact) {
  // 1
  var firstBody: SKPhysicsBody
  var secondBody: SKPhysicsBody
  // 2
  if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
    firstBody = contact.bodyA
    secondBody = contact.bodyB
  } else {
    firstBody = contact.bodyB
    secondBody = contact.bodyA
  }
  // 3
  if firstBody.categoryBitMask == BallCategory && secondBody.categoryBitMask == BottomCategory {
    print("Hit bottom. First contact has been made.")
  }
}

Let's walk through the method step by step:

  1. Create two local variables to hold the two physics bodies involved in the collision.
  2. Check the two bodies that collided to see which has the lower categoryBitmask. You then store them into the local variables, so that the body with the lower category is always stored in firstBody. This will save you quite some effort when reacting to contacts between specific categories.
  3. Profit from the sorting that you did just before. You only need to check whether firstBody is in the BallCategory and whether secondBody is in the BottomCategory to figure out that the ball has touched the bottom of the screen, as you already know that secondBody could not possibly be in the BallCategory if firstBody is in the BottomCategory (because BottomCategory has a higher bit mask than BallCategory). For now react with a simple log message.

It’s time to try out your code again. Build and run your game and if you’ve done everything correctly, you should see the log message in the console every time the ball misses the paddle and hits the bottom of the screen. Like this:

First contact has been made :)

First contact has been made :)

First contact has been made :)

Allright! Now the hard part is done - all that's left is adding the bricks and gameplay logic, which you'll do in part 2.

Michael Briscoe

Contributors

Michael Briscoe

Author

Over 300 content creators. Join our team.