SpriteKit Project — AbsolutelyBrickingIt — Part 5: Navigate to the game scene

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

In this article, we’ll build on the simple menu screen with a basic animation and navigation to the game scene.

Animating the logo

Currently, the code to add the menu logo to the scene looks like:

let logoNode = SKSpriteNode(imageNamed: "logo")
logoNode.position = CGPoint(x: 0.5 * size.width, y: 0.8 * size.height)
addChild(logoNode)

Let’s update this to start at a lower y value and to animate to the above y value, using SKAction's move(to: _, duration: _) method:

let logoNode = SKSpriteNode(imageNamed: "logo")
logoNode.position = CGPoint(x: 0.5 * size.width, y: 0.5 * size.height)
addChild(logoNode)
logoNode.run(.move(to: CGPoint(x: 0.5 * size.width, y: 0.8 * size.height), duration: 1.0))

Running in the simulator now will show the logo moving from the center to the top of the screen when the menu scene is shown:

Logo animation

Detecting when play is touched

In order to detect when a node, such as the play node is touched, we need to keep a reference to the node we’re interested in and we need to compare it against touches on the screen:

class MenuScene: SKScene {

private var playNode: SKSpriteNode?

override func didMove(to view: SKView) {
...
addChild(playNode)
self.playNode = playNode
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let point = touch.location(in: self)

if playNode?.contains(point) == true {
print("Play touched")
}
}
}
}

Now we can see the resulting output in the console when clicking Play in the Simulator:

Console output when play is touched

Opening the game scene when play is touched

We’ll use the coordinator pattern to route the play button touch back to the GameViewController and then present the GameScene from there:

protocol MenuSceneCoordinator: AnyObject {
func menuScenePlayTapped(_ menuScene: MenuScene)
}

class MenuScene: SKScene {

weak var coordinator: MenuSceneCoordinator?

...

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let point = touch.location(in: self)

if playNode?.contains(point) == true {
coordinator?.menuScenePlayTapped(self)
}
}
}
}

If we run the app at this point, we’ll find that the console output is gone when we tap play. Nothing happens because we have yet to set the coordinator to handle the tap:

No console output

We can make the GameViewController the coordinator for the MenuScene to allow it to handle play taps:

class GameViewController: UIViewController {

override func viewDidLoad() {
...
let scene = MenuScene(size: CGSize(width: 375.0, height: 667.0))
scene.coordinator = self
...
}

...
}

extension GameViewController: MenuSceneCoordinator {

func menuScenePlayTapped(_ menuScene: MenuScene) {
print("Play tapped!")
}
}

Now when we tap play, we once again see message in the console. This time, however, the tap has been handled by a coordinator (i.e. the GameViewController):

Console output for play tap routed through the coordinator

Finally, we can update the GameViewController to present the GameScene when a play tap occurs:

extension GameViewController: MenuSceneCoordinator {

func menuScenePlayTapped(_ menuScene: MenuScene) {
guard let view = self.view as? SKView else { return }

let scene = GameScene(size: CGSize(width: 375.0, height: 667.0))
scene.scaleMode = .resizeFill

view.presentScene(scene)
}
}

Run the app in the Simulator and confirm that the play button now leads to the game scene:

Game scene presented after a play button tap

Code

This article corresponds to the merge commit Navigate to game scene (#5) in the GitHub repository AbsolutelyBrickingIt

Next Part

Part 6: Add the about scene

--

--