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

In part one of this two-part tutorial series, you’ll start creating a game like Mega Jump, and implement the overall scene, physics, and sound effects using Swift and Sprite Kit! By Michael Briscoe.

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

Game Objects: Reach for the Stars!

To give your player something to do—as well as some upward momentum—it’s “high” time for you to add stars to your game. Stars have a key role in Uber Jump: They are what the player needs to grab to move higher into the level.

Initially, you will add one star to the game and implement it fully. Then in part two of this tutorial, you’ll complete the level.

In Uber Jump, as in Mega Jump, when the player sprite goes a certain distance higher than a star, platform or any other game object, the game will remove that object from the scene. Since star and platform nodes share this functionality, it makes sense to create a subclass of SKNode for all game objects.

Create a new Cocoa Touch Class called GameObjectNode and make it a subclass of SKNode. Be sure that the Langauge: is set to Swift.

NewClassGameObjectNode

GameObjectNode will provide the following functionality:

  • It will remove the game object from the scene if the player node has passed it by more than a set distance.
  • It will handle collisions between the player node and the object. This method will return a Bool that informs the scene whether the collision with the game object has resulted in a need to update the HUD—for example, if the player has scored points.

Replace the code in GameObjectNode.swift with the following:

import SpriteKit

class GameObjectNode: SKNode {
  func collisionWithPlayer(player: SKNode) -> Bool {
    return false
  }
    
  func checkNodeRemoval(playerY: CGFloat) {
    if playerY > self.position.y + 300.0 {
      self.removeFromParent()
    }
  }
}

You’ll call collisionWithPlayer whenever the player node collides with this object, and you’ll call checkNodeRemoval every frame to give the node a chance to remove itself.

In the GameObjectNode class, collisionWithPlayer is simply a stub. You will define the full method in each of your game object subclasses.

checkNodeRemoval checks to see if the player node has traveled more than 300 points beyond this node. If so, then the method removes the node from its parent node and thus, removes it from the scene.

The Star Class

Now that you have a base class for the interactive game nodes, you can create a subclass for your stars. To keep things simple, you’ll add all your GameObjectNode subclasses within the GameObjectNode.swift file. Add the following code after your GameObjectNode class:

class StarNode: GameObjectNode {    
  override func collisionWithPlayer(player: SKNode) -> Bool {
    // Boost the player up
    player.physicsBody?.velocity = CGVector(dx: player.physicsBody!.velocity.dx, dy: 400.0)
        
    // Remove this Star
    self.removeFromParent()
        
    // The HUD needs updating to show the new stars and score
    return true
  }
}

Collision with a star boosts the player node up the y-axis. You may be thinking, “Why am I not using a force or impulse in the physics engine to do this?”

If you were to apply the star boost as a force or impulse, it wouldn’t always have the same effect. For example, if the player node were moving down the screen when it collided with the star, then the force would have a much weaker effect on the player than if the player were already moving up the screen.

The following diagram shows a very simplified visualization of this:

10-ForcesAndBounce

A solution to this problem is to change the player node’s velocity directly. The player node’s velocity is obviously made up of an x-axis speed and a y-axis speed.

The x-axis speed needs to stay the same, since it is only affected by the accelerometer, which you’ll implement later. In the method above, you set the y-axis velocity to 400 on collision — a fixed amount so that the collision has the same effect no matter what the player node is doing when it collides with the star.

Open GameScene.swift and add the following method:

func createStarAtPosition(position: CGPoint) -> StarNode {
  // 1
  let node = StarNode()
  let thePosition = CGPoint(x: position.x * scaleFactor, y: position.y)
  node.position = thePosition
  node.name = "NODE_STAR"
        
  // 2
  var sprite: SKSpriteNode
  sprite = SKSpriteNode(imageNamed: "Star")
  node.addChild(sprite)
        
  // 3
  node.physicsBody = SKPhysicsBody(circleOfRadius: sprite.size.width / 2)
        
  // 4
  node.physicsBody?.dynamic = false
        
  return node
}

The code above should all look familiar by now:

  1. You instantiate your StarNode and set its position.
  2. You then assign the star’s graphic using an SKSpriteNode.
  3. You’ve given the node a circular physics body – you use it for collision detection with other objects in the game.
  4. Finally, you make the physics body static, because you don’t want gravity or any other physics simulation to influence the stars.

Now add the following code to init(size:), just before where you create the player node. You want the stars to be behind the player in the foreground node and so you need to add them before you add the player node.

// Add a star
let star = createStarAtPosition(CGPoint(x: 160, y: 220))
foregroundNode.addChild(star)

Build and run the game. Tap to start and watch the player sprite collide with the star.

11-FirstStar

The Uber Jumper bonks its head on the star. That wasn’t the plan! Why do you think that happened? Take a guess.

[spoiler title=”Solution”]The physics engine handles the collision between the player and star nodes. The player node’s physics body hits the star node’s physics body, which is static and thus immovable. The star node blocks the player node.[/spoiler]

Collision Handling and Bit Masks

To handle collisions between the player and star nodes, you need to capture the collision event and call the GameObjectNode method collisionWithPlayer.

This is a good time to take a look at collision bit masks in Sprite Kit.

When setting up collision information for your physics bodies, there are three bit mask properties you can use to define the way the physics body interacts with other physics bodies in your game:

  • categoryBitMask defines the collision categories to which a physics body belongs.
  • collisionBitMask identifies the collision categories with which this physics body should collide. Here, “collide” means to bump into each other as physical objects would. For example, in a third-person shooter you may want your player sprite to collide with enemy sprites, but pass through other player sprites.
  • contactTestBitMask tells Sprite Kit that you would like to be informed when this physics body makes contact with a physics body belonging to one of the categories you specify. For example, in your game you want Sprite Kit to tell you when the player sprite touches a star or a platform. Using the correct combination of settings for the contactTestBitMask and the collisionBitMask, you can tell Sprite Kit to let objects pass through each other but still notify you when that happens so you can trigger events.

First, define your categories. Open GameObjectNode.swift and add the following struct above the class definitions:

struct CollisionCategoryBitmask {
  static let Player: UInt32 = 0x00
  static let Star: UInt32 = 0x01
  static let Platform: UInt32 = 0x02
}

Go back to GameScene.swift. To set up the player node’s collision behavior, add the following code to the bottom of createPlayer, just before the return statement:

// 1
playerNode.physicsBody?.usesPreciseCollisionDetection = true
// 2
playerNode.physicsBody?.categoryBitMask = CollisionCategoryBitmask.Player
// 3
playerNode.physicsBody?.collisionBitMask = 0
// 4
playerNode.physicsBody?.contactTestBitMask = CollisionCategoryBitmask.Star | CollisionCategoryBitmask.Platform

Let’s take a closer look at this code block:

  1. Since this is a fast-moving game, you ask Sprite Kit to use precise collision detection for the player node’s physics body. After all, the gameplay for Uber Jump is all about the player node’s collisions, so you’d like it to be as accurate as possible! (it costs few more cpu cycles, but the fun is considerably more!)
  2. This defines the physics body’s category bit mask. It belongs to the CollisionCategoryPlayer category.
  3. By setting collisionBitMask to zero, you’re telling Sprite Kit that you don’t want its physics engine to simulate any collisions for the player node. That’s because you’re going to handle those collisions yourself!
  4. Here you tell Sprite Kit that you want to be informed when the player node touches any stars or platforms.

Now set up the star node. Add the following code to the bottom of createStarAtPosition:, just before the return statement:

node.physicsBody?.categoryBitMask = CollisionCategoryBitmask.Star
node.physicsBody?.collisionBitMask = 0

This is similar to your setup for the player node. You assign the star’s category and clear its collisionBitMask so it won’t collide with anything. In this case, though, you don’t set its contactTestBitMask, which means Sprite Kit won’t notify you when something touches the star. You already instructed Sprite Kit to send notifications when the player node touches the star, which is the only contact with the star that you care about, so there’s no need to send notifications from the star’s side.

Sprite Kit sends notifications for the node contacts you’ve registered by calling didBeginContact on its SKPhysicsContactDelegate. Set the scene itself as the delegate for the physics world by adding the SKPhysicsContactDelegate protocol to the GameScene class definition. It should now look like this:

class GameScene: SKScene, SKPhysicsContactDelegate {
  // Layered Nodes
  ...

Now register the scene to receive contact notifications by adding the following line to init(size:), just after the line that sets the gravity:

// Set contact delegate
physicsWorld.contactDelegate = self

Finally, add the following method to GameScene.swift to handle collision events:

func didBeginContact(contact: SKPhysicsContact) {
  // 1
  var updateHUD = false
        
  // 2
  let whichNode = (contact.bodyA.node != player) ? contact.bodyA.node : contact.bodyB.node
  let other = whichNode as! GameObjectNode
        
  // 3
  updateHUD = other.collisionWithPlayer(player)
        
  // Update the HUD if necessary
  if updateHUD {
    // 4 TODO: Update HUD in Part 2
  }
}

Let’s take a closer look at this code:

  1. You initialize the updateHUD flag, which you’ll use at the end of the method to determine whether or not to update the HUD for collisions that result in points.
  2. SKPhysicsContact does not guarantee which physics body will be in bodyA and bodyB. You know that all collisions in this game will be between the player node and a GameObjectNode, so this line figures out which one is not the player node.
  3. Once you’ve identified which object is not the player, you call the GameObjectNode’s collisionWithPlayer: method.
  4. This is where you will update the HUD, if required. You’ll implement the HUD in Part Two, so there’s nothing but a comment here for now.

Build and run the game. Tap to start. Upon collision, the star provides the player sprite with a sizable boost and then removes itself from the scene. Good work!

12-FirstStarCollision

That was a lot of heavy lifting there! Take a well-deserved break before moving on. In the next section, you’re going to add a new star type—the uber star—as well as a cool sound effect.

Note: If you want to know more about detecting contacts and collisions in Sprite Kit feel free to check out “iOS Games by Tutorials“, which contains 3 solid chapters on Sprite Kit physics.

Note: If you want to know more about detecting contacts and collisions in Sprite Kit feel free to check out “iOS Games by Tutorials“, which contains 3 solid chapters on Sprite Kit physics.

Michael Briscoe

Contributors

Michael Briscoe

Author

Over 300 content creators. Join our team.