iOS Programming · · 6 min read

An Introduction to SpriteKit Part 2: Actions, Sequences and Gesture Recognizers

An Introduction to SpriteKit Part 2: Actions, Sequences and Gesture Recognizers

In the first part of the tutorial series, you have learned all you need to know about scenes, nodes, labels and points in SpriteKit by centering a label on the screen. In this part, you will move the label when you tap the screen using new SpriteKit concepts:

  • Actions
  • Sequences
  • Gesture recognisers
Note: This tutorial assumes that you have already done the first part and you are working on the sample project which you have created there. If you haven’t, download the starter project that covers all the key concepts presented so far and go on from there.

Adding a Gesture Recogniser to the Scene

You are going to add a gesture recogniser to the scene so that the label moves whenever you tap anywhere on the screen. Add the following block of code to the didMove(to:) method in the GameScene.swift file right after you add the label to the scene:

let recognizer = UITapGestureRecognizer(target: self, action: #selector(tap))
view.addGestureRecognizer(recognizer)

You create a tap gesture recognizer as an instance of the UITapGestureRecognizer class and add it to the scene’s view. The gesture recognizer’s target is the scene itself and its action is a selector associated with the tap(recognizer:) method that you will add to the GameScene class later on.

Note: Xcode displays an error at this point: Use of unresolved identifier “tap” because you haven’t implemented the tap(recognizer:) method yet. We will fix that soon.

Now add the tap(recognizer:) method’s prototype to the GameScene class:

func tap(recognizer: UIGestureRecognizer) {

}

The previous error is fixed, but nothing happens when you actually tap the screen. Time for your first action!

Note: You can read more about how selectors work in Swift in my article here.

Creating an SKAction

An SKAction object is an action that is executed by a node in the scene. Basically, you are allowed to use an action to change a node’s properties, such as its position, rotation, or scale. Therefore to move the label, add the following block of code inside the tap(recognizer:) method:

let viewLocation = recognizer.location(in: view) 
let sceneLocation = convertPoint(fromView: viewLocation) 
let moveToAction = SKAction.move(to: sceneLocation, duration: 1)
label.run(moveToAction) 

There’s quite a lot going on here, so let’s break it into steps:

  • Line #1: You determine the tap’s location in the scene’s view coordinates with the location(in:) instance method from the UIGestureRecognizer class.
  • Line #2: You convert the location’s point from view coordinates to scene ones with the convertPoint(fromView:) instance method from the SKScene class.
  • Line #3: You create a “move to” action with the move(to: duration:) static method from the SKAction class. The action defines a one second animation that moves a certain node from its current position to the point where you tapped the screen.
  • Line #4: You make the label run the action with the run(_:) instance method from the SKNode class.

The movement action isn’t associated with any node by default, so you can use it on any other nodes in your scene as well. We will keep things simple and stick to only one for now.

Now run the project and tap anywhere on the screen. The label moves to its corresponding location:

spritekit-label-move

You can also move a node by a certain amount from its current position – replace lines 3 and 4 from above with the following block of code:

let moveByAction = SKAction.moveBy(x: sceneLocation.x - label.position.x, y: sceneLocation.y - label.position.y, duration: 1)
label.run(moveByAction)

You first create a “move by” action with the moveBy(x: y: duration:) static method from the SKAction class. The action declares a one second animation which moves a node by the difference between the tap’s location and its current position on each axis. You then associate the label with its corresponding action just like before.

Build your project and tap anywhere on the screen – the label moves to its corresponding location as in the previous case.

Working with Sequence

The “move by” action isn’t as straightforward as its “move to” counterpart, but it has one major advantage: it can be reversed. To do that, we will need to build the first sequence of actions!

Delete the line of code that runs the “move by” action from the tap(recognizer:) method and add the following block of code to it right after you define the action itself. Your tap(recognizer:) method should look like this:

func tap(recognizer: UIGestureRecognizer) {
    let viewLocation = recognizer.location(in: view)
    let sceneLocation = convertPoint(fromView: viewLocation)
    let moveByAction = SKAction.moveBy(x: sceneLocation.x - label.position.x, y: sceneLocation.y - label.position.y, duration: 1)
    
    // Reversed action
    let moveByReversedAction = moveByAction.reversed()
    let moveByActions = [moveByAction, moveByReversedAction]
    let moveSequence = SKAction.sequence(moveByActions)
    
    label.run(moveSequence)
}

This is what’s going on over here, step by step:

  • Line #7: You reverse the “move by” action with the reversed() instance method from the SKAction class: it does the exact opposite as its counterpart now.
  • Line #8: You create an array of actions which contains the action itself and its reversed one – you will use it at the next step in order to define the sequence of actions.
  • Line #9: You declare a sequence of actions with the sequence(_:) static method from the SKAction class – its only argument is the actions array created at the previous step.
  • Line #11: You run the sequence on the label as before: the actions run one after the other in sequence.

You can reverse a sequence as well – both the order of its corresponding actions and the actions themselves are reversed in this case.

Now run the project and tap anywhere on the screen. The label first moves to the tap location then just goes straight back in place to its original position.

spritekit-reversed-action

Note: You may think of the reverse action as the inverse function and the previous sequence as function composition in Maths.

Building a Sequence Loop

Sequences are powerful by themselves, but they only repeat their corresponding actions once. Let’s take everything to the next level with sequence loops!

You can repeat a given sequence, either for a certain number of times or forever. Delete the line of code that runs the movement sequence and replace it with the following block of code:

func tap(recognizer: UIGestureRecognizer) {
    let viewLocation = recognizer.location(in: view)
    let sceneLocation = convertPoint(fromView: viewLocation)
    let moveByAction = SKAction.moveBy(x: sceneLocation.x - label.position.x, y: sceneLocation.y - label.position.y, duration: 1)
    
    // Reversed action
    let moveByReversedAction = moveByAction.reversed()
    let moveByActions = [moveByAction, moveByReversedAction]
    let moveSequence = SKAction.sequence(moveByActions)
    let moveRepeatSequence = SKAction.repeat(moveSequence, count: 3)
    
    label.run(moveRepeatSequence)
}

You create a repeating sequence with the repeat(_: count:) static method from the SKAction class. It repeats the previously defined movement sequence for five times and you run it on the label the same way as you would do with any other sequence.

Sequences are just actions under the hood, so you can always repeat an action instead of a sequence of actions if you need to do so. Both repeated sequences and actions may be reversed – the reversed sequence or action is repeated in this case.

Now build the project and tap anywhere on the screen. The label moves backwards and forwards between the tap location and its initial position for three times.

spritekit-sequence

Repeating a sequence many times is cool and all, but repeating it forever is way cooler, so go ahead and delete the whole block of code that you have just added and replace it with the following:

let moveRepeatForeverSequence = SKAction.repeatForever(moveSequence)
label.run(moveRepeatForeverSequence)

You create an endlessly repeating sequence with the repeatForever(_:) static method from the SKAction class. It repeats the previously declared sequence forever and you run it on the label like you would do with an ordinary repeating sequence.

Run your project and tap anywhere on the screen. The label moves back and forth without stopping at all like crazy – how cool is that! 🙂

Conclusion

That’s all about movement. In the next parts of the tutorial series, you will learn all you need to know about scaling and rotating the label whenever you tap the screen. Stay tuned and in the meantime if you have any questions or issues, please let me know!

For reference, you can download the sample project on GitHub.

Read next