SpriteKit Project — AbsolutelyBrickingIt — Part 11: Add the first brick
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
:
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:
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:
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