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
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.
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!
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 theUIGestureRecognizer
class. - Line #2: You convert the location’s point from view coordinates to scene ones with the
convertPoint(fromView:)
instance method from theSKScene
class. - Line #3: You create a “move to” action with the
move(to: duration:)
static method from theSKAction
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 theSKNode
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:
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 theSKAction
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 theSKAction
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.
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.
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.