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 2 of 4 of this article. Click here to view the first page.

Caging the ball

No escape for the ball.

Cage the ball!

Open GameScene.swift and add the following line of code to the end of didMove(to:) to create an invisible barrier around the screen:

// 1
let borderBody = SKPhysicsBody(edgeLoopFrom: self.frame)
// 2
borderBody.friction = 0
// 3
self.physicsBody = borderBody

Let’s review this line by line:

  1. You create an edge-based body. In contrast to the volume-based body you added to the ball, an edge-based body does not have mass or volume, and is unaffected by forces or impulses.
  2. Set the friction to 0 so that the ball will not be slowed down when colliding with the border barrier. Instead, you want to have a perfect reflection, where the ball leaves along the same angle that it hit the barrier.
  3. You can set a physics body for every node. Here, you attach it to the scene. Note: The coordinates of the SKPhysicsBody are relative to the position of the node.

Running your project, you should now see your ball fall as before but it now bounces on the bottom-edge of the cage you just added. Because you removed the friction from the contact with the cage and the environment, and set the restitution to be perfectly elastic, the ball will bounce like that indefinitely.

BouncingBall

To finish up with the ball, let’s remove the gravity and apply a single impulse so that it will bounce around the screen forever.

Forever bouncing

It’s time to get the ball rolling (bouncing, actually). Add the following code to didMove(to:) right after the previous lines in GameScene.swift:

physicsWorld.gravity = CGVector(dx: 0.0, dy: 0.0)
    
let ball = childNode(withName: BallCategoryName) as! SKSpriteNode
ball.physicsBody!.applyImpulse(CGVector(dx: 2.0, dy: -2.0))

This new code first removes all gravity from the scene, then gets the ball from the scene’s child nodes using the name you set in the Visual Editor and applies an impulse. An impulse applies an immediate force to a physics body to get it moving in a particular direction (in this case, diagonally down to the right). Once the ball is set in motion, it will simply bounce around the screen because of the barrier you just added!

Now it’s time to try it out! When you compile and run the project, you should see a ball continuously bouncing around the screen – cool!

BouncingBallEdges

Adding the Paddle

It wouldn’t be a Breakout game without a paddle, now would it?

Go to GameScene.sks and build the paddle (and its companion physics body) via the Visual Editor just like you did for the ball by placing a Color Sprite at the bottom middle of the scene and setting its properties to:

  1. Name = paddle
  2. Texture = paddle.png
  3. Position = 284,30
  4. Z Position = 3
  5. Body Type > Bounding rectangle
  6. Uncheck Dynamic
  7. Friction: 0
  8. Restitution: 1

Most of this is similar to what you used when creating the ball. However, this time you use a Bounding rectangle to form the physics body as it will better match the rectangular paddle.

By turning off Dynamic here you are setting the paddle to be static. This ensures that the paddle does not react to forces and impulses. You will soon see why this is important.

If you build and run, you’ll see your paddle in the scene, and the ball will bounce off it (if you can wait long enough for it to get there):

The most boring game ever :/

So far so good – but now let’s allow the player to move the paddle!

Moving the Paddle

Time to get moving! Moving the paddle is going to require detecting touches. You can detect touches in GameScene by implementing the following touch handling methods:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)

But, before that, you need to add a property. Go to GameScene.swift and add the following property to the class:

var isFingerOnPaddle = false

This defines a property that stores whether the player tapped the paddle or not. You will need it to implement the dragging of the paddle.

Now, go ahead and add the code to implement touchesBegan(_:with:) to GameScene.swift as follows:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
  let touch = touches.first
  let touchLocation = touch!.location(in: self)
    
  if let body = physicsWorld.body(at: touchLocation) {
    if body.node!.name == PaddleCategoryName {
      print("Began touch on paddle")
      isFingerOnPaddle = true
    }
  }
}

The above code gets the touch and uses it to find the location on the scene where the touch occurred. Next, it uses body(at:) to find the physics body associated with the node (if any) at that location.

Finally, it checks whether there was a node at the tap location and if yes, whether that node is the paddle. This is where the object names you set up earlier come into play – you can check for a specific object by checking its name. If the object at the tap location is a paddle, then a log message is sent to the console and isFingerOnPaddle is set to true.

Now you can build and run the project again. When you tap the paddle, you should see a log message in the console.
BreakoutDebugger1

Now, go ahead and add the code for touchesMoved(_:with:):

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
  // 1
  if isFingerOnPaddle {
    // 2
    let touch = touches.first
    let touchLocation = touch!.location(in: self)
    let previousLocation = touch!.previousLocation(in: self)
    // 3
    let paddle = childNode(withName: PaddleCategoryName) as! SKSpriteNode
    // 4
    var paddleX = paddle.position.x + (touchLocation.x - previousLocation.x)
    // 5
    paddleX = max(paddleX, paddle.size.width/2)
    paddleX = min(paddleX, size.width - paddle.size.width/2)
    // 6
    paddle.position = CGPoint(x: paddleX, y: paddle.position.y)
  }
}

This is where most of the paddle movement logic comes in.

  1. Check whether the player is touching the paddle.
  2. If yes, then you need to update the position of the paddle depending on how the player moves their finger. To do this, you get the touch location and previous touch location.
  3. Get the SKSpriteNode for the paddle.
  4. Take the current position and add the difference between the new and the previous touch locations.
  5. Before repositioning the paddle, limit the position so that the paddle will not go off the screen to the left or right.
  6. Set the position of the paddle to the position you just calculated.

The only thing left in touch handling is to do some cleanup in touchesEnded(_:with:) as follows:

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
  isFingerOnPaddle = false
}

Here, you set the isFingerOnPaddle property to false. This ensures that when the player takes their finger off the screen and then taps it again, the paddle does not jump around to the previous touch location.

Perfect! When you build and run the project now, the ball will bounce around the screen and you can influence its movement using the paddle.

Paddle Bounce

w00t – it’s feeling fun already!

Michael Briscoe

Contributors

Michael Briscoe

Author

Over 300 content creators. Join our team.