How to Make a Game Like Mega Jump With Sprite Kit and Swift: Part 2/2

In this final part of the tutorial series, you’ll finish your game like Mega Jump, and add the level design, accelerometer support, and HUD using Swift and Sprite Kit! By Michael Briscoe.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Parallaxalization

No, that’s not a word! ;]

To give your game the parallax effect, you’re going to move the background, midground and foreground nodes at different rates as the player moves up and down the scene. Sprite Kit calls update() on your scene every frame, so that’s the place to implement this logic to produce smooth animation.

Open GameScene.swift and add the following method:

override func update(currentTime: NSTimeInterval) {
  // Calculate player y offset
  if player.position.y > 200.0 {
    backgroundNode.position = CGPoint(x: 0.0, y: -((player.position.y - 200.0)/10))
    midgroundNode.position = CGPoint(x: 0.0, y: -((player.position.y - 200.0)/4))
    foregroundNode.position = CGPoint(x: 0.0, y: -(player.position.y - 200.0))
  }
}

You check to make sure the player node has moved up the screen at least 200 points, because otherwise you don’t want to move the background. If so, you move the three nodes down at different speeds to produce a parallax effect:

  • You move the foreground node at the same rate as the player node, effectively keeping the player from moving any higher on the screen.
  • You move the midground node at 25% of the player node’s speed so that it appears to be farther away from the viewer.
  • You move the background node at 10% of the player node’s speed so that it appears even farther away.

Build and run, and tap to start the game. You’ll see the layers all move with the player, and the different speeds of the background and midground nodes will produce a very pleasing parallax effect.

20-Parallax

Great work! But don’t rest on your laurels yet. To get any higher in this world, you need to get your tilt on.

Moving With the Accelerometer

It’s now time to consider the accelerometer. You’ve got movement along the vertical axis working well, but what about movement along the horizontal axis? Just like in Mega Jump, the player will steer their Uber Jumper using the accelerometer.

Note: To test accelerometer, you need to run your game on a device. The iPhone Simulator does not simulate accelerometer inputs.

The accelerometer inputs are part of the Core Motion library, so at the top of GameScene.swift, add the following import:

import CoreMotion

Then add the following properties to the GameScene class:

// Motion manager for accelerometer
let motionManager = CMMotionManager()
    
// Acceleration value from accelerometer
var xAcceleration: CGFloat = 0.0

You’re going to use motionManager to access the device’s accelerometer data, and you’ll store the most recently calculated acceleration value in xAcceleration, which you’ll use later when you set the player node’s velocity along the x-axis.

To instantiate your CMMotionManager, add the following code to init(size:), just after the line that adds tapToStartNode to the HUD:

// CoreMotion
// 1
motionManager.accelerometerUpdateInterval = 0.2
// 2
motionManager.startAccelerometerUpdatesToQueue(NSOperationQueue.currentQueue(), withHandler: {
  (accelerometerData: CMAccelerometerData!, error: NSError!) in
  // 3
  let acceleration = accelerometerData.acceleration
  // 4
  self.xAcceleration = (CGFloat(acceleration.x) * 0.75) + (self.xAcceleration * 0.25)
})

There’s a lot going on here, so take a deeper dive:

  1. accelerometerUpdateInterval defines the number of seconds between updates from the accelerometer. A value of 0.2 produces a smooth update rate for accelerometer changes.
  2. You enable the accelerometer and provide a block of code to execute upon every accelerometer update.
  3. Inside the block, you get the acceleration details from the latest accelerometer data passed into the block.
  4. Here you calculate the player node’s x-axis acceleration. You could use the x-value directly from the accelerometer data, but you’ll get much smoother movement using a value derived from three quarters of the accelerometer’s x-axis acceleration (say that three times fast!) and one quarter of the current x-axis acceleration.

Now that you have an x-axis acceleration value, you need to use that value to set the player node’s velocity along the x-axis.

As you are directly manipulating the velocity of the player node, it’s important to let Sprite Kit handle the physics first. Sprite Kit provides a method for you to implement to get that timing correct called didSimulatePhysics. Sprite Kit will also call this method once per frame, after the physics have been calculated and performed.

Add the following method to GameScene.swift:

override func didSimulatePhysics() {
  // 1
  // Set velocity based on x-axis acceleration
  player.physicsBody?.velocity = CGVector(dx: xAcceleration * 400.0, dy: player.physicsBody!.velocity.dy)
  // 2
  // Check x bounds
  if player.position.x < -20.0 {
    player.position = CGPoint(x: self.size.width + 20.0, y: player.position.y)
  } else if (player.position.x > self.size.width + 20.0) {
    player.position = CGPoint(x: -20.0, y: player.position.y)
  }
}

A couple of things are happening here:

  1. You change the x-axis portion of the player node’s velocity using the xAcceleration value. You multiply it by 400.0 because the accelerometer scale does not match the physics world’s scale and so increasing it produces a more satisfying result. You leave the velocity’s y-axis value alone because the accelerometer has no effect on it.
  2. In Mega Jump, when the player leaves the screen from the left or right, they come back onscreen from the other side. You replicate that behavior here by checking the bounds of the screen and leaving a 20-point border at the edges.

Build and run on your device, and use the accelerometer to steer the player sprite as high as you can!

21-AccelerometerRun

The Scoring System

Your complete Uber Jump will have three pieces of information pertinent to the player:

  • The Current Score. The score will start at zero. The higher the player gets, the more points you’ll award toward the score. You’ll also add points for each star the player collects.
  • The High Score. Every run through the game will result in a final score. Uber Jump will save the highest of these in the app’s user defaults so that the player always knows the score to beat.
  • The Stars. While the current score will reset at the beginning of each game, the player’s stars will accumulate from game to game. In a future version of Uber Jump, you may decide to make stars the in-game currency with which users can buy upgrades and boosts. You won’t be doing that as part of this tutorial, but you’ll have the currency set up in case you’d like to do it on your own.

You’re going to store the current score, the high score and the number of the collected stars in a singleton class called GameState. This class will also be responsible for saving the high score and number of stars to the device so that the values persist between game launches.

Create a new iOS/Source/Swift File called GameState. Add the following class definition and properties to GameState.swift:

class GameState {
  var score: Int
  var highScore: Int
  var stars: Int
    
  class var sharedInstance: GameState {
    struct Singleton {
      static let instance = GameState()
    }
        
    return Singleton.instance
  }
}

These three properties will provide access to the current score, the high score and the star count. The class variable sharedInstance will provide access to the singleton instance of GameState.

You need to provide an initialization method for GameState that sets the current score to zero and loads any existing high score and star count from the user defaults.

Add the following init method to GameState.swift:

init() {
  // Init
  score = 0
  highScore = 0
  stars = 0
        
  // Load game state
  let defaults = NSUserDefaults.standardUserDefaults()
        
  highScore = defaults.integerForKey("highScore")
  stars = defaults.integerForKey("stars")
}

NSUserDefaults is a simple way to persist small bits of data on the device. It’s intended for user preferences, but in this example it works well to store the high score and star count. In a real app, you would want to use something more secure than NSUserDefaults as someone could easily tamper with the data stored there and give themselves more stars than they earned!

Note:For more information on storing game data, check out our tutorial on How to Save your Game Data.

To store these values, you need a method in GameState. Add the following method to GameState.swift:

func saveState() {
  // Update highScore if the current score is greater
  highScore = max(score, highScore)
        
  // Store in user defaults
  let defaults = NSUserDefaults.standardUserDefaults()
  defaults.setInteger(highScore, forKey: "highScore")
  defaults.setInteger(stars, forKey: "stars")
  NSUserDefaults.standardUserDefaults().synchronize()
}

You now have a GameState class that synchronizes with storage on the device. What good is a score if nobody knows what it is? You’ve got to show me the money!

Michael Briscoe

Contributors

Michael Briscoe

Author

Over 300 content creators. Join our team.