Creating a Tutorial for a SpriteKit Game

A step-by-step approach

David Piper
Oct 20 · 6 min read
Image by Wokandapix from Pixabay

Currently, my wife and I are working on a small game in SpriteKit. The player controls a fish that needs to dodge obstacles on its way up from the deep ocean. This fish can only swim within four lanes, as you can see in this image:

Freddy the fish can swim in these four lanes and needs to dodge obstacles.

The player can switch lanes by swiping left or right. You would guess that this concept is not hard to grasp, and so did we. But when test users played, some of them didn’t understand what they needed to do. Thus we decided to transform the first level into a tutorial. Since I couldn’t find a tutorial on how to add a tutorial, I’ll describe the approach we took.

Each step of the tutorial level consists of two parts. First, the fish needs to stop at a specific point while the user gets a hint of what they have to do. Second, after the user completes the swipe gesture, the fish needs to perform the required movement.

1. Setup of Tutorial Nodes

We marked points where the fish should stop by adding nodes to the scene. The name of each of these nodes starts with tutorial_node_, followed by the number of the step in the tutorial. These nodes are prepared in the method setupNodes, which is called when the scene is moved to a view.

Setting up the tutorial nodes
  1. After the nodes are added to the scene editor, we need to access them in code. We can do this by getting the surrounding world node from the scene and looping over its children.
  2. Now that we have access to the nodes, we need to prepare them. We make them clear, so they can’t be seen in the game, and we create a SKPhysicsBody for each of them so they can be hit by the fish. Each body has the same size and position as the node itself and gets a categoryBitMask value to mark them as a node involved in the tutorial. Finally, by setting the isDynamic property to false, the nodes are not affected by gravity and will stay where they are.

2. Handling Contact With a Tutorial Node

To detect contact between the player and a tutorial node, we need to implement the didBegin method, which is required by the SKPhysicsContactDelegate protocol.

Handling contact of the player with a tutorial node.
  1. The contact object, which is passed as a parameter, contains both elements involved in the contact as bodyA and bodyB. By combining both in categoryBitMask, we can check which nodes did collide.
  2. In the case of the fish and a tutorial block, we need to show the next part of the tutorial.
  3. First we stop the fish. It will only swim on after the user completes this part of the tutorial.
  4. We extract the tutorial block from the contact by using a little helper function called node(withCategory:). It’s an extension of SKPhysicsContact that checks whether bodyA or bodyB has the tutorial categoryBitMask and returns the correct body. Now we can check the name of the tutorial block. Since we enumerated them in the scene editor, we know at which step of the tutorial the player currently is. In this example, we’ll just explore the first step.
  5. Each time the fish starts contact with a tutorial node, we determine the next movement the fish has to perform. In the first step, the fish needs to swim to the left-most lane. Also we show an animation to visualize which action the user has to do.

You can see that we do this by using the two functions calculateNeededMovement(current:destination:) and showSwipeNeededAnimation(for:). Let’s look at how they work.

The lanes are represented by the following enumeration:

The four swimming lanes you can see in the first image are represented by these values.

A movement consists of a direction in which the fish should move and a number of line switches the fish has to do in the given direction.

The struct Movement encapsulates a direction and a number of steps to go in that direction.

To construct an instance of Movement, we pass the current SwimmingColumn of the fish and the destination to which the fish should move to the method calculateNeededMovement(current:destination:). It switches over both and returns the corresponding Movement object, e.g. if the fish is currently in the left-most lane and needs to move to the middle-left lane, it needs to move one step to the right. This movement is stored in the variable calculatedMovement to be executed once the user swipes.

In this example getting the next movement is as easy as switching over all possible combinations.

Note: An alternative approach would be to postpone calculating the next movement of the player to the point where the user already triggers the resume. This would remove the need to separate calculating and executing the next movement, which may seem like a better way. However, there is still state that needs to be stored, mainly which point of the tutorial was triggered. Also, separating these steps provides some benefits. When the tutorial point is hit, SpriteKit has nothing more to do than show the Animation and thus has enough resources to calculate the next step. This may seem irrelevant in such a small game, but imagine that you have multiple enemies and the player needs to move in the best possible way around them. There may even be an AI involved to find the shortest path to the next spot. This can be perfectly done while the game waits for the player to make the next move. If this calculation happens after the user performs the needed action to resume the game, there may be performance issues.

As you can see in the didBegin(contact:) method, after the next movement is determined, we want to show an animation to the user to explain what they need to do. In other cases, you may also want to show a text giving more information.

Showing the user what to do with this animation.

Here is how we added the animation step by step:

  1. Since we want to remove the animated node later when the user swipes, we store it in a variable.
  2. When setting up the animation, we first make sure that we have received a valid movement. Otherwise, we will just not show any animation. When the user needs to swipe to the left, we want our indicator to start at the right side of the display. Otherwise, it should start at the left side. Thus we create its position with an x offset of -100 or 100, depending on the direction. The position is stored in a variable to reset the node in the animation to its starting point.
  3. Now we create a filled white circle, which will be the animated node to indicate that the user needs to swipe in a specific direction.
  4. Finally we can set up the animation. This animation consists of four different animations performed in a sequence that will be repeated forever. First, we move the indicator in the direction the user needs to swipe. Once finished, the node will fade out, move back to its original position, and fade back in.

3. Handling a Swipe by the User

We can detect the swipe by using an UIGestureRecognizer. We first remove the previously created and stored indicator node to stop the animation. Next, it uses a method called executeMovement which actually performs the movement we previously determined. The last step is to restart the fish.

Handling the swipe of a user after a tutorial node was hit by the fish.

The method executeMovement only moves the fish in the given direction. This is repeated numberOfSteps times. In our game, moving left and right involves an animation to switch lines, changing the sprite of the fish and other things that are left out.

Executing the previously determined movement.

That’s how we structured our tutorial level. In this image you can see a summary of all the steps we’ve gone through to show the user what he has to do:

Actions to add a tutorial step.

I hope you can use this example if you want to add a tutorial level to your game! If you have questions or want to suggest a better approach, please leave a comment.

Better Programming

Advice for programmers.

David Piper

Written by

Working as an iOS and Android dev. Also going to finish my master degree in computer science anytime soon now!

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade