Quick Tip: Endless scrolling with SpriteKit and SWIFT (Part 1 of 2)

Endless scrolling with background tiles


Welcome to my tutorial series about scrolling:
  • Part 1: Endless scrolling with background tiles
  • Part 2: Natural endless scrolling with easing
This video gives an impression what I’ll show today:

1. About the algorithm and the background tiles


Creating the image tiles for the scrolling parts of the background:

First we need a background image:

SpriteKit Scrolling Tutorial 1
Let’s mirror this image and add the new one at the right side:


SpriteKit Scrolling Tutorial 2

Copy the original image and append it at the right side. In our app we will have three sprite nodes, one for each image tile:

SpriteKit Scrolling Tutorial 3

Scrolling will start at the left side:

SpriteKit Scrolling Tutorial 4

Let’s add a static background image:

SpriteKit Scrolling Tutorial 5
To scroll right, move the background tiles in the left direction:
SpriteKit Scrolling Tutorial 6
If the end at the right side is reached:
SpriteKit Scrolling Tutorial 7

Move the background tiles back to the start position:

SpriteKit Scrolling Tutorial 8

The SpriteKits object hierarchy will be created this way:

  • scene (SKScene)
    • backgroundNode (SKSpriteNode)
    • worldNode (SKNode)
      • leftTileNode (SKSpriteNode)
      • middleTileNode (SKSpriteNode)
      • rightTileNode (SKSpriteNode)
    • spriteNode (SKSpriteNode)
The worldNode will be moved to implement the scrolling.

2. Create the SWIFT project:


Create a new Sprite Kit project:

SpriteKit Scrolling Tutorial 9
SpriteKit Scrolling Tutorial 10

Open Asset Catalogue and add three images (Background, LeftTile, rightTile)

SpriteKit Scrolling Tutorial 11

Open GameScene.swift:

SpriteKit Scrolling Tutorial 12

Replace the complete code with this snippet:

(For explanation check the comments inside the code snippet)  

//  GameScene.swift
//  EndlessScrollingDemo
//  Created by STEFAN on 13/11/15.
//  Copyright (c) 2015 Stefan. All rights reserved.
import SpriteKit
class GameScene: SKScene {
    // Declare the globaly needed sprite kit nodes
    var worldNode: SKNode?
    var spriteNode: SKSpriteNode?
    // store the width of the NodeTiles
    var nodeTileWidth: CGFloat = 0.0
    // store the start position of the movement
    var xOrgPosition: CGFloat = 0
    override func didMoveToView(view: SKView) {
        // Setup static background
        let backgroundNode = SKSpriteNode(imageNamed: „Background“)
        backgroundNode.size = CGSize(width: self.frame.width, height: self.frame.height)
        backgroundNode.anchorPoint = CGPoint(x: 0, y: 0)
        backgroundNode.zPosition = -10
        // Setup world
        worldNode = SKNode()
        // Setup dynamic background tiles
        // Image of left and right node must be identical
        let leftNode = SKSpriteNode(imageNamed: „LeftTile“)
        let middleNode = SKSpriteNode(imageNamed: „RightTile“)
        let rightNode = SKSpriteNode(imageNamed: „LeftTile“)
        nodeTileWidth = leftNode.frame.size.width
        leftNode.anchorPoint = CGPoint(x: 0, y: 0)
        leftNode.position = CGPoint(x: 0, y: 0)
        middleNode.anchorPoint = CGPoint(x: 0, y: 0)
        middleNode.position = CGPoint(x: nodeTileWidth, y: 0)
        rightNode.anchorPoint = CGPoint(x: 0, y: 0)
        rightNode.position = CGPoint(x: nodeTileWidth * 2, y: 0)
        // Add tiles to worldNode. worldNode is used to realize the scrolling
        // Setup sprite
        spriteNode = SKSpriteNode(imageNamed: „Spaceship“)
        spriteNode?.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
        spriteNode?.xScale = 0.1
        spriteNode?.yScale = 0.1
        spriteNode?.zPosition = 10
    // Implement the scrolling, triggered by swipe gestures
    override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
        for touch in touches {
            // Touch position
            let xTouchPosition = touch.locationInNode(self).x
            if xOrgPosition != 0.0 {
                // calculate the new position
                let xNewPosition = worldNode!.position.x + (xOrgPosition – xTouchPosition)
                // Check if right end is reached
                if xNewPosition <= -(2 * nodeTileWidth) {
                    worldNode!.position = CGPoint(x: 0, y: 0)
                    print(„Right end reached“)
                // Check if left end is reached
                } else if xNewPosition >= 0 {
                    worldNode!.position = CGPoint(x: -(2 * nodeTileWidth), y: 0)
                    print(„Left end reached“)
                } else {
                    worldNode!.position = CGPoint(x: xNewPosition, y: 0)
                // Rotate sprite depending on direction
                if xTouchPosition < xOrgPosition {
                    spriteNode?.zRotation = CGFloat(M_PI/2.0)
                } else {
                    spriteNode?.zRotation = –CGFloat(M_PI/2.0)
            // Store the current touch position to calculate the delta in the next iteration
            xOrgPosition = xTouchPosition
    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        // Reset value for the next swipe gesture
        xOrgPosition = 0
    override func update(currentTime: CFTimeInterval) {
        /* Called before each frame is rendered */
You can download the complete sample from my Github repository.

I’ll show an improved version with a smoother scrolling in part 2. You can support me by downloading my Apps from the Apple AppStore:

AppStore Stefan Josten

That’s all for today.


Quick Tip: Integrate iAd banners in your iOS App with two lines of code
Game Development
Game Center Integration
Apple TV
Quick Tip: How to use the Apple TV remote to control a sprite in your TVOS Apps