SpriteKit Project — AbsolutelyBrickingIt — Part 11: Add the first brick

atomicswiftdev
App Dev Pro Tips
Published in
3 min readAug 22, 2024

In this article, we’re going to add a brick to the game scene.

Add the brick node to the game scene

Let’s add a method to add a brick node to the game scene, similar to the other add node methods:

private extension GameScene {
...
func addBrickNode(position: CGPoint) {
let brickNode = SKSpriteNode(imageNamed: "brick")
brickNode.name = "brick"
brickNode.position = position
brickNode.physicsBody = SKPhysicsBody(rectangleOf: brickNode.size)
brickNode.physicsBody?.categoryBitMask = brickCategory
brickNode.physicsBody?.isDynamic = false
addChild(brickNode)
}
}

This time we specify a position as a parameter to the method to allow us to vary the position at which to place the brick. We’ve also added a new physics category:

class GameScene: SKScene {
...
private let outOfBoundsCategory: UInt32 = 0x1 << 3
private let brickCategory: UInt32 = 0x1 << 4
...
}

We can now call addBrickNode(position:_) from didMove(to view:_) to add a brick node:

class GameScene: SKScene {
...
override func didMove(to view: SKView) {
...
addOutOfBoundsNode(size: hWallSize, position: outOfBoundsPosition)
addBrickNode(position: CGPoint(x: 0.2 * size.width, y: 0.6 * size.height))
...
}
...
}

Running the project, we can now see the brick in the game scene when we tap Play:

Brick is visible in the game scene

When the ball hits the brick, however, nothing happens. It simply passes through.

Update the collision and contacts for the ball

We need to specify that the ball collides with the brick, meaning it bounces off the brick. We also need to specify that we want to be informed when such a contact occurs.

So we need to update the relevant masks for the ball’s physics body:

private extension GameScene {
...
func addBallNode() {
...
ballNode.physicsBody?.collisionBitMask = paddleCategory | wallCategory | brickCategory
ballNode.physicsBody?.contactTestBitMask = outOfBoundsCategory | brickCategory
...
}
}

With this done, we can update our custom ball(node: _, collidedWith other:_) to handle the case when other is a brick. For now we'll simply remove the brick from the scene when this happens:

extension GameScene: SKPhysicsContactDelegate {
...
private func ball(node: SKNode, collidedWith other: SKNode) {
if other.name == "outOfBounds" {
coordinator?.gameSceneGameEnded(self)
} else if other.name == "brick" {
other.removeFromParent()
}
}
}

Running the project, we see that the brick gets removed when the ball makes contact with it:

When the ball hits the brick it gets removed from the scene

Update the game scene coordinator

At this point, we can update the game scene coordinator to better reflect the 2 possible outcomes, winning and losing:

protocol GameSceneCoordinator: AnyObject {
func gameSceneGameLost(_ scene: GameScene)
func gameSceneGameWon(_ scene: GameScene)
}

Those methods will be called from within the custom ball(node: _, collidedWith other:_) method:

extension GameScene: SKPhysicsContactDelegate {
...
private func ball(node: SKNode, collidedWith other: SKNode) {
if other.name == "outOfBounds" {
coordinator?.gameSceneGameLost(self)
} else if other.name == "brick" {
other.removeFromParent()
coordinator?.gameSceneGameWon(self)
}
}
}

The final required step is to update the coordinator implementation in GameViewController to match these changes:

extension GameViewController: GameSceneCoordinator {

func gameSceneGameLost(_ scene: GameScene) {
present(scene: gameOverScene)
}

func gameSceneGameWon(_ scene: GameScene) {
print("Congratulations! You Win!")
}
}

For now, we will simply output a message to the console when the “game won” method gets called:

Game won message shown in the console

In the next article, we’ll add a game complete scene to show when the game has been completed!

Code

This article corresponds to the merge commit Add first brick (#11) in the GitHub repository AbsolutelyBrickingIt

Next Part

Part 12: Add game complete scene

--

--