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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
How to Make a Game Like Mega Jump With Sprite Kit and Swift: Part 1/2
40 mins
- Getting Started
- Importing the Art
- Building the Scene
- Adding the Player Node
- Adding Gravity and a Physics Body
- Tap To Start
- Game Objects: Reach for the Stars!
- The Star Class
- Collision Handling and Bit Masks
- Multiple Star Types
- Ping! Adding a Sound Effect
- Game Objects: Platforms
- Where to Go From Here?
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.
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:
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:
- You instantiate your
StarNode
and set its position. - You then assign the star’s graphic using an
SKSpriteNode
. - You’ve given the node a circular physics body – you use it for collision detection with other objects in the game.
- 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.
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 thecontactTestBitMask
and thecollisionBitMask
, 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:
- 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!)
- This defines the physics body’s category bit mask. It belongs to the
CollisionCategoryPlayer
category. - 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! - 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:
- 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. -
SKPhysicsContact
does not guarantee which physics body will be inbodyA
andbodyB
. You know that all collisions in this game will be between the player node and aGameObjectNode
, so this line figures out which one is not the player node. - Once you’ve identified which object is not the player, you call the
GameObjectNode
’scollisionWithPlayer:
method. - 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!
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.