In 2013, Apple released SpriteKit, its 2D game engine, in order to compete with all the two-dimensional frameworks that were existing in the market and retain their developers in its own technological ecosystem. Since then, SpriteKit has become one of the most powerful tools that are used to develop 2D games for iOS. In this chapter, we will have a look at the elements that are a part of game development, and we'll study how to use them with SpriteKit.
In this chapter, we will explore the following topics:
Understanding game engines
Creating and understanding a new SpriteKit project
Understanding the
SKNode
classStudying the
SKScene
classHow to add a sprite and background to a scene
I remember the time when I developed my first game using the BASIC programming language on my old Amstrad CPC. In those times, every game was hardware-specific, which means that you had to take into account every machine's low-level characteristics.
A game engine is a collection of software instructions that eases the process of game development by providing abstraction between the hardware and software layers. This way, you don't need to waste your efforts when performing important tasks, such as handling user inputs, playing sound and video, rendering images, or simulating physics.
As mentioned previously, SpriteKit is an engine developed by Apple to create games, and it's one of the most powerful tools that are used to build native 2D games for both iOS and Max OS X.
The tool needed to develop SpriteKit games is Apple's Integrated Development Environment (IDE) Xcode, which can be found free of charge on Apple's App Store at https://itunes.apple.com/en/app/xcode/id497799835?l=en&mt=12. We are going to work with version 7.0, which is the latest at the time of writing the book, and iOS 9.
Creating a new project with Xcode is a straightforward task, but I would like to take advantage of it to help you understand how a default Xcode project looks like.
To create a new SpriteKit project, we need to open Xcode and then navigate to File | New | Project…. On the left-hand side, you will need to click on the iOS | Application Template; you will see what's shown in the following screenshot:

At this point, you will need to perform the following steps:
Select the Game template and click on Next.
Set InsideTheHat as the template name. Ensure that the Swift option is chosen in the Language menu. Select SpriteKit as the Game Technology, and Universal is chosen on the Device Family menu. Leave the default configuration (checked) for Include Unit Tests and Include UI Tests before clicking on the Next button.
Choose a place to save your project in and click on Create.
The first thing that you should look at is the left section, which is called Project Navigator and contains the folders, and files that will be a part of our game; this section is shown in the following screenshot:

The Project Navigator shows a tree of files and folders that represents a hierarchy that doesn't correspond with how these files are located on your hard drive. It means that, if you move some file, on the Project Navigator, it won't affect their position in Finder. However, if you move some file in Finder, the reference kept by Xcode will be broken and it won't be able to make use of it.
The yellow containers on the project navigator are called Groups in Xcode, and they are equivalent to folders in a filesystem; as folders, the groups' responsibility is to organize all the files (images, classes, and so on) of an Xcode project.
One of the most important groups is the one called Project. It contains classes and resource files. As you can see in the preceding screenshot, a default project contains three Swift classes, namely AppDelegate
, GameScene
, and GameViewController
, that will contain the core of the game.
Tip
Swift is Apple's programming language that was created by Apple for iOS, Mac OS X, watchOS, and Linux development. It was first released in June 2014.
There are a couple of storyboard files, namely Main
and LaunchScreen
, that are responsible for showing the game screen and launch image respectively. You will also see an image asset
file, which will contain the images used on the game, and a plist
file with the project configuration.
In addition to this, there is a file called GameScene.sks
that should look pretty new to you. This file is used to build the screen in a static way, which is similar to a storyboard, that is created with Interface Builder.
Getting back to Xcode, there is another important section in the Project; it is the window at the center, which shows the configuration of the Project, as shown in the following screenshot:

In this panel, you will see three different sections, namely Identity, Deployment Info, and App Icons and Launch Images. Let's take a look at the second one first, where you can configure the following:
Deployment Target: This is the iOS version that is used to run the game. By default,
9.0
is chosen.Devices: This is the family of devices (iPhone, iPad, or both), on which we will be able to run the game. In our case, it shows Universal, which is the property that we specified when creating the project.
Main Interface: This is the main storyboard file that is used to run the project.
Device Orientation: This determines the different orientations that our game will be able to support. As we are going to develop a vertical game, unselect the Landscape Left and Landscape Right checkboxes, leaving just Portrait checked off.
Status Bar Style: This helps us determine how we want the status bar to be shown.
The third section contains the following configuration:
App Icons Source: This comprises the asset catalog for app icons.
Launch Images Source: This comprises the asset catalog for the launch image.
Launch Screen File: This determines the screen shown while loading the game. If you want to avoid the launch screen that shows the copyright (such as the one that you can use to show your company's logo), choose Main.Storyboard in the drop-down menu.
After performing the aforementioned modifications, the project's properties will meet our requirements. So, let's run the project.
To execute the project, you just need to click on the Run icon at the top left of the Xcode screen, and it will run the project on an iPhone 6 on the iOS Simulator; the result is shown in the following screenshot:

Well, with a little effort, we have created our first Hello, World project using SpriteKit. Now, it's time to look at the code and understand why the preceding screenshot is the result of these files.
In this section, you are not supposed to understand everything. The aim of this section is to understand the responsibility of each class in a default SpriteKit
project.
The entry point of our game is the AppDelegate
class, which is the same as that of all iOS applications. Let's take a look at its content:
import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { // Override point for customization after application launch. return true }
Tip
Downloading the example code.
You can download the example code files from your account at http://www.packtpub.com for all the Packt Publishing books you have purchased. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.
I've just pasted the top block of the file because it is the important one. As you can see, we imported the UIKit
framework that will provide the window and the view architecture needed to build an application. It also provides our project with the event-handling infrastructure that is needed to respond to user input and the app model needed to drive the main
run loop and interact with the system.
The next line contains an odd-looking instruction, which is @UIApplicationMain
. This tells Xcode which is the main
file of the project.
Then, you will see that the AppDelegate
class inherits from UIResponder
and UIApplicationDelegate
, which is what happens with all iOS applications. We declared a UIWindow
optional variable to avoid runtime errors in case of nil content.
Note
As per Apple's documentation ( https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/OptionalChaining.html), optional chaining is a Swift process that is used to call properties and methods on an optional that might currently be nil
. If the optional contains a value, the call succeeds, and if the optional is nil
, the call returns nil
.
Finally, you will see that the only method implemented is application(application:,
launchOptions:)
. This is the point where we can apply some instructions that we want the game to execute as soon as it's launched.
There is nothing more to remark on this class. So let's take a look at the class that will be called just after AppDelegate: GameViewController
.
To understand why this class is called, as soon as the main screen is launched, we need to keep in mind that the project is configured to show Main.storyboard
as the main interface. In the Project Explorer, select the File and look at the Utilities panel on the right-hand side of screen, and choose the Identity Inspector to have a look at its configuration, as shown in the following screenshot:

This means that the interface is linked to the GameViewController
class. It's time to open the class and discover what it contains:
import UIKit import SpriteKit class GameViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() if let scene = GameScene(fileNamed:"GameScene") { // Configure the view. let skView = self.view as! SKView skView.showsFPS = true skView.showsNodeCount = true /* Sprite Kit applies additional optimizations to improve rendering performance */ skView.ignoresSiblingOrder = true /* Set the scale mode to scale to fit the window */ scene.scaleMode = .AspectFill skView.presentScene(scene) } }
As you can see at the top of the file, the view controller is a subclass of the UIViewController
class, which is commonly seen in many iOS applications. However, the difference is that here, we imported the SpriteKit
framework (apart from UIKit
), to provide game characteristics to the project.
This class overrides the viewDidLoad
method, where we create a scene by using a file called GameScene
. This file corresponds to GameScene.sks
. If it succeeds, we create a view (an instance of the SKView
class), setting the showsFPS
and showsNodeCount
attributes to True
.
This is the reason why we can see these labels at the bottom right of the game's screen; they show the amount of draw calls (node count), and frame rate respectively.
Note
The frame rate value measures how smooth our game will be. In iOS, the maximum frame rate is 60 Hz.
The number of draw calls and the frame rate are values that you need to take care of, as they will let us know if our game will run smoothly.
We will have a look at the last view's configuration (ignoresSiblingOrder
), and the scaleMode
property later in the chapter, as we just want to have an overview the project. Once the view
is configured, we can load the scene
by calling the presentScene
method.
Next, in the file, you will see four more methods. Take a look at the following two methods:
override func shouldAutorotate() -> Bool { return true } override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask { if UIDevice.currentDevice().userInterfaceIdiom == .Phone { return .AllButUpsideDown } else { return .All } }
This code means that the user can rotate the device, and the screen will adapt itself automatically to the new orientation with one restriction, due to the AllButUpsideDown
property: the game's screen won't rotate when we hold the iPhone or iPod devices upside down.
Have a look at the following method:
override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Release any cached data, images, etc that aren't in use. }
This method should look familiar to you if you have developed an iOS application previously. It's raised by the system when the amount of available memory is low. It allows us to release some memory to avoid an application crash.
Finally, we have the following method that has to do with the way the game is shown:
override func prefersStatusBarHidden() -> Bool { return true }
This method keeps the status bar hidden, as our application is a game and we want to use the full screen to show it.
We have previously seen that this class creates a scene by calling the constructor method in the GameScene
class. Therefore, it's time to open the file:
import SpriteKit class GameScene: SKScene { override func didMoveToView(view: SKView) { /* Setup your scene here */ let myLabel = SKLabelNode(fontNamed:"Chalkduster") myLabel.text = "Hello, World!"; myLabel.fontSize = 65; myLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame)); self.addChild(myLabel) }
As you can see, this class also imports the SpriteKit
framework, but the most important thing about this is the class that it is inheriting SKScene
. We will study it in detail further in this chapter, but for now, you need to understand that an instance of SKScene
or its subclass is the object that will represent a scene of content in a SpriteKit game.
The didMoveToView
method means that its code will be executed as soon as the scene is presented by a view
. This is the perfect place to initialize a scene and, as we can see, in the default project, we are creating a new label using a font called Chalkduster
and configuring some of its properties to set the size
, text
, and desired position
. Adding the label to the scene is as easy as executing the addChild
method.
The next method in the class looks like this:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { /* Called when a touch begins */ for touch in touches { let location = touch.locationInNode(self) let sprite = SKSpriteNode(imageNamed:"Spaceship") sprite.xScale = 0.5 sprite.yScale = 0.5 sprite.position = location let action = SKAction.rotateByAngle(CGFloat(M_PI), duration:1) sprite.runAction(SKAction.repeatActionForever(action)) self.addChild(sprite) } }
This method is called when the user touches somewhere on the screen. It is also one of the methods that we can override to handle touches. There are three more methods, namely touchesMoved
, touchesEnded
, and touchesCancelled
, which will be covered in detail in Chapter 2, What Makes a Game a Game? As soon as user touches on the screen and this whole process gets completed, it gets the location of the touch, creates an SKSpriteNode
instance using the Spaceship
texture, sets its size
to half of the texture's original size, and places it on the touch position. You will find the image that is used to create the spaceship in the Assets.xcassets
folder of the Project Navigator.
Then, it applies a rotation to the spaceship by creating an action
method, and calling the rotateByAngle
method, which accepts an angle
value as an input parameter, and running this action
on the spaceship. Finally, it adds the ship to the scene.
Note
The SKSpriteNode
instance is one of the most used classes in SpriteKit game development as it provides a visual representation and a physical shape to the objects in view
.
The last method looks like this:
override func update(currentTime: CFTimeInterval) { /* Called before each frame is rendered */ }
This is one of the most important methods when developing games with SpriteKit, as it is called just before each frame is rendered and it is the place where we can perform important operations and actions.
If you run the project again and touch anywhere on the screen, you will see something that is similar to what's shown in the following screenshot:

As expected, a spaceship has been created and it has begun to rotate in a counterclockwise direction. Another important thing to note at this point is the number of nodes, which has increased and corresponds to the draw of the scene, the text label, the spaceship, and the background.
Now that we had an overview of the initial project code, it's time to go deeper into some of the classes that we saw earlier. We have seen that the default project creates instances of SKScene
, SKLabelNode
, and SKSpriteNode
, which are subclasses of SKNode
, one of the most important classes of the SpriteKit framework. You will understand why if you keep reading.
When developing a scene, we sometimes build what is called a scene hierarchy. This scene graph is a hierarchy of the nodes that are available on it.
We call them nodes because they inherit from the SKNode
class. For more information, visit https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKNode_Ref, which is the main SpriteKit
class that renders visual elements.
The following diagram corresponds to the scene graph of the project. You can see that there is a parent SKScene
node with two children that correspond to the SKSpriteNode
and SKLabelNode
that we have added to the game:

The SKNode
class is a subclass of UIResponder
. This means that all the SKNode
instances and every subclass of SKNode
will be able to handle touches and other kind of events such as motion
events.
If you look at the SKNode
class hierarchy in the following diagram, you will realize the importance of this class, as it is the parent of several useful classes:

In this section, we are going to have a look at the most important properties available in the SKNode
class in detail.
An important property of nodes is their position
, as we are going to manipulate this several times during the game's development. This property corresponds to the position of the node in the parent's coordinate system. Therefore, we need to take it into account when adding new elements to a scene
. Its default value is (0.0, 0.0)
.
Another useful property is the frame
of a node, which makes a reference to the rectangle defined by its texture (the visual content). This property size
can be modified by applying a scaling factor on both the width and height by applying a value between 0
and 1
to the xScale
and yScale
attributes. The frame can also be rotated by modifying the zRotation
property, which will apply a counterclockwise rotation if the value is greater than 0
.
Note
As a node can be used to organize the content by storing other nodes, the scale
and rotation
modifiers will affect both the node and its descendants.
If we want to take into account a node's descendants when getting its frame, there is a function called calculateAccumulatedFrame()
that retrieves the rectangle containing the content of the parent and children while taking into account the scale
and rotation
factors.
We can, for instance, determine whether this whole frame is intersected by another node's frame thanks to this method.
This property determines the height of the node related to its parent. Its value is 0.0
by default, but we can set positive or negative values so that, the bigger the zPosition
value, the closer the node will be to the user. This way, we will have full control over how the children are rendered.
Sometimes, we will need to keep a node invisible while it is in a scene. We can do this by setting the hidden
property to true
. It only affects the way the node and its descendants are rendered, as they will still be able to perform actions and collide with other nodes in the scene.
A property that provides a similar effect is the alpha
property of the node. It applies a modifier between 0.0
and 1.0
to the alpha
component of each pixel and allows us to make the node transparent.
If a scene contains several nodes, we may need to identify them in order to handle collisions. In such cases, it is a good approach to provide a value to each node's name
property. We can use this property to give the same name to a group of nodes in order to differentiate them from the player's node and make collision detection tasks easy.
If we want to find a node by its unique name, we can make use of the childNodeWithName
method. On the other hand, if we have used a name to identify a collection of nodes, we can call enumerateChildNodesWithName:usingBlock
, which will search a node's children and execute a block of code once for each child that is found.
We have seen previously that an SKNode
instance can be used to contain other nodes in order to organize the scene content. The following are a few examples:
You may want to group several nodes that need to be treated as a unique object to represent an army of alien ships, and you don't want any of the ships to be the root. Grouping them as
children
of a node will allow you to move them, while always keeping the line-up.In a game, it is common to have a background, several characters, objects to collide, texts, and many more elements. You can create different layers to separate each of these different kind of elements by creating basic nodes and inserting them in the desired order into the scene.
In the preceding screenshot, you can see how we used three different layers, one for the background, another one for the ninja character, and the last one for the score.
By following the afore mentioned approaches, you will be able to add or remove entire groups of objects by deleting a single node. This will make the scene management more efficient. You can also configure the properties of several nodes by applying the configuration to the root
node. You can even take advantage of it when running actions or handling physics contacts.
The SKScene
class is a subclass of SKNode
that has some specific properties and methods to handle the way content is drawn in an SKView
object (the screen).
Each node provides content that will be animated and rendered by the scene in a process called game loop. It looks like the following screenshot that was taken from https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG:

According to the preceding screenshot, each frame in a SpriteKit game is calculated in the following order:
Firstly, the
scene
calls theupdate
method. Here, we can specify the code that we want to execute just before the scene actions are evaluated.Then, the
scene
executes the actions on itschildren
nodes.Once the actions have been executed, the
scene
triggers itsdidEvaluateActions
method. We should include in this method the code that we want to execute as soon as the actions have been evaluated.Now, it's time for the physics to be evaluated. SpriteKit provides an easy way to simulate physics in a node such as
gravity
,collisions
, andfriction
, but we are not going to cover it in this book. You just need to know that there is a step in the game loop where thescene
executes every physics simulation on the physic bodies in the scene.After the physics is simulated, the scene triggers its
didSimulatePhysics
method. We should include in this method the code that we want to execute as soon as the physics is simulated.Then, the scene applies the constraints associated to its
children
nodes. These constraints are an array of instances of the SKC
onstraint class, which basically are restrictions applied to a node that can be related to another node in the scene. For example, we can create constraints to set a node'szRotation
method so that it always points at another node or position in the scene, or keeps a node inside a specified rectangle or within a specified distance of another node.Once the constraints have been applied, the scene triggers its
didApplyConstraints
method, which we should take advantage of to include the code that we want to execute as soon as the physics has been simulated.Then, the scene calls the
didFinishUpdate
method, which is the last method that is called before the scene is rendered.Finally, the scene renders all of its children nodes and updates the view.
In this section, we are going to study in detail the most important properties that are available in the SKScene
class.
An SKScene
instance provides some properties that can become interesting when creating a scene. For example, the scaleMode
property allows us to specify the way a scene is mapped to the view that presents it, which can be one of the following four values defined in the SKSceneScaleMode
enumeration:
Fill
: Each axis of the scene (x and y) is scaled independently. This way, each axis in the scene exactly maps to the length of the same axis in the view.AspectFill
: This is thescale
mode that is used by the default project. In this case, we will choose a scaling factor, that will be the larger scaling factor between the two dimensions, and each axis of the scene will be scaled by the same factor. This way, the entire area of the view will be filled, but it's possible that some parts of the scene may be cropped.AspectFit
: In this case, we will choose a scaling factor that will be the smaller scaling factor between the two dimensions, and each axis of the scene will be scaled by the same factor. This way, the entire scene will be visible, but letterboxing may be required in the view.ResizeFill
: This value will automatically resize the scene so that its dimensions match those of the view.
This property makes reference to the origin point of the scene. By default, its value is (0,0)
, which means that the scene will be pinned to the bottom left point of the view, as shown in the following screenshot. When we add the first sprite to the scene, we'll see how important it is:

This property specifies the part of the scene's coordinate space that is visible in the view. When this property is changed, the didChangeSize
method is triggered. An important aspect that needs to be highlighted is that this property will also be modified if we set the ResizeFill
value in the scaleMode
property.
In this game, we will take control of a little rabbit that is trying to escape from the top hat of a magician, where it is trapped. To achieve its objective, our main character will need to run through magic doors until it gets the ace of diamonds that will let the rabbit escape.
In this chapter, we are going to see how to create the main character's sprite and add it to the scene. On the other hand, we will learn how to set a background for the game. In the preceding pages, we have seen a lot of properties and methods that will help us reach our current goal.
Let's start by cleaning off the unnecessary files and content in the project. We are going to generate the screens programmatically so that you can delete the sks
file:
Right-click on the
GameScene.sks
file.Choose Delete.
Ensure that you click on the Move to Trash button.
Next, adapt the GameViewController
class in order to avoid initializing the scene from the file that we have just removed. Replace the viewDidLoad
method from this class with the following block of code:
override func viewDidLoad() { super.viewDidLoad() let scene = GameScene(size: view.bounds.size) // Configure the view. let skView = self.view as! SKView skView.showsFPS = true skView.showsNodeCount = true /* Sprite Kit applies additional optimizations to improve rendering performance */ skView.ignoresSiblingOrder = true /* Set the scale mode to scale to fit the window */ scene.scaleMode = .AspectFill skView.presentScene(scene) }
We have just modified the old line, which looks like this:
if let scene = GameScene(fileNamed:"GameScene") {
We replaced the preceding line of code with the following code:
let scene = GameScene(size: view.bounds.size)
This way, we initialized the scene using the init(size:)
method of the SKScene
class, to which we pass a size
value as an input parameter in the form of view.bounds.size
. We are using the bounds property of the SKView
class, which corresponds to a rectangle that occupies the whole size of the screen.
Now, it's time to clean the GameScene
class. Therefore, open it and replace the didMoveToView
method with the following piece of code:
override func didMoveToView(view: SKView) { }
Replace the touchesBegan
method with the following code:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { }
The project is now ready to be updated with our brand-new code, but you can run it just to ensure that we haven't broken anything.
The SKSpriteNode
class https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKSpriteNode_Ref is the one that we are going to use in order to load the sprites that will be a part of our game.
The SKSpriteNode
class is a subclass of SKNode
, and it's used to represent visual elements called sprites
on the screen by using images. As you are going to need an image to create a sprite (an instance of SKSpriteNode
class), perform the following steps to add it to the project:
Unzip the
7338_01_Resources.zip
file in the desired location.In Xcode, right-click on the InsideTheHat group on the Navigator tab, select New Group, and call it Art.
Right-click on the Art group and select Add Files to InsideTheHat….
A dialog box will open, where you need to select the
rabbit.png
image in the7338_01_Resources
folder that you just unzipped.Ensure that Copy items if needed is selected and click on Add.
Now that the image has been added, we will need a variable to manage the main character. Therefore, on GameScene.swift
, add the following line just after class
GameScene:SKScene
{
:
private var rabbit: SKSpriteNode!
Note that we have declared the variable as a var
because its value will change throughout the game's life.
Now that the sprite variable has been declared, modify the didMoveToView
method by:
override func didMoveToView(view: SKView) { self.initializeMainCharacter() } func initializeMainCharacter() { // Creating the rabbit sprite using an image file and adding it to the scene rabbit = SKSpriteNode(imageNamed: "rabbit") addChild(rabbit) }
As soon as the scene is loaded, we call a new method named self.initializeMainCharacter
that we created just to keep the code as clean as we can. We will use the self
object because we are referencing a method in the current class. If you look at the method, you will see that it initializes the sprite with the init(imageNamed:)
method, which takes the image that we have just added to the project to provide the sprite's visual content.
Tip
Note that you don't need to specify the extension of the filename, as it will load a .png
, .jpg
, .jpeg
, .tiff
, .tif
, .gif
, .bmp
, .BMPf
, .ico
, .cur
, or .xbm
file.
Thanks to this init
method, the sprite's size
property (and its frame
) is automatically set to the dimensions of the image and the color to white (1.0,
1.0,
1.0)
.
Once the sprite has been initialized, we add it to the scene by using the addChild
method, which adds a new child to the specified container (GameScene
in this case), and this is how we add new nodes to the scene.
If you run the game now, you will see something similar to what's shown in the following screenshot:

The sprite has been placed at the bottom left corner of the screen (the (0,0)
coordinate), which corresponds to the scene's anchor point. You may be wondering why the rabbit is not fully visible.

The answer is that the default value of the anchorPoint
on an SKSpriteNode
is the center of the texture at (0.5,
0.5)
, while the anchorPoint
of the scene is at (0,0)
. As soon as the sprite is added to the scene, their anchor points get aligned.
For our game, we want the rabbit to be placed at the center of the screen and near at the bottom of the screen. Therefore, add the following lines of code to initializeMainCharacter
just before addChild(rabbit)
:
// Positioning the rabbit centered rabbit.position = CGPoint(x:(view!.bounds.size.width/2), y: rabbit.size.height)
With the preceding line of code, we created a CGPoint
class, which is a commonly used class that is utilized to represent a point in a two-dimensional coordinate system that accepts a CGFloat
value for both the x and y axes. At the bottom-center of the screen, we are setting this point as the position of the rabbit.
Also note how we are getting the center of the screen's width. We get the width
property from the size
method of the bounds
property of the current view (passed as an input argument when the scene is loaded), which is the rectangle that contains all the visual elements. As we want our sprite to be centered on the x axis, we just need to divide it by 2, and we have the desired value.
Tip
As there are several devices that support iOS and each of them has its own specific resolution and screen sizes, it's very important to always work with relative positions. This way, you don't need to worry about an element's position.
If you look at the code, you will realize that we are setting the sprite's position before adding it to the scene, but you can place it just after the addChild
method, and the result won't vary. Now, if you run the game, the rabbit will be placed in the correct position, as shown in the following screenshot:

At this moment, there is nothing that represents that the rabbit is trying to escape from somewhere. Therefore, we need to add context to the game, which is the same as adding a background.
We need to create a road for our rabbit to run on and also for it to find the exit of the top hat. In this case, we will follow almost the same steps than we did to add the rabbit, but with a few differences.
Add the background image in a way that is similar to how we added the rabbit's image:
On the Project Navigator, select the Art group.
Right-click and select Add Files to "InsideTheHat"….
Look for the
background.png
file in the7338_01_Resources
folder that you unzipped. Select it and click on Add.
Then, add the following lines to GameScene
before the addChild(rabbit)
line:
// Creating and adding the background to the scene let background = SKSpriteNode(imageNamed: «background») background.anchorPoint = .zero addChild(background)
You already know the first line; we are creating a sprite using the background image that we have just added to the project. Then, we set its anchorPoint
to .zero
, which is a shortcut for CGPoint(x:0,
y:0)
. The image covers the whole screen, and finally we add the background to the scene.
Run the game. Now, the rabbit should appear standing on a lonely road, as shown in the following screenshot:

You may be wondering why the rabbit sometimes appears and sometimes it doesn't. The reason is that it is behind the background, even though it has been added after the background image.
The reason for this behavior lies in the skView.ignoresSiblingOrder
=
true
line in the GameViewController
class.
The ignoresSiblingOrder
property indicates whether the relationship between the parent nodes and children affects the order of the nodes in the scene. By default, its value is false
. This means that SpriteKit will render the children in the same order they appear in the children
array, one node at a time.
Setting this property to true
will not take into account the position of the nodes in the tree, but their zPosition
property groups all the nodes at the same zPosition
property in a single draw. Therefore, the reason behind setting the ignoresSiblingOrder
property to true
is the fact that it will improve the rendering performance.
In the game, the property has been set to true
and the nodes have no zPosition
specified (0.0
by default). This will render all the children on the same time in an arbitrary way. That's why, the rabbit may sometimes be visible and sometime not.
As we want the game to be very efficient, we will keep the ignoresSiblingOrder
property as is. So, we will need to give the zPosition
value to some nodes. Open GameScene
and add the following line just before addChild(background)
:
background.zPosition = -1
This way, we set the background behind the default zPosition
value so that the rest of the nodes that we will add will always be visible. Let's run the game again and check whether the rabbit is now visible. The output is shown in the following screenshot:

As mentioned previously, iOS games can be executed on devices with different resolutions and screen sizes. This is the reason why it's important to keep in mind the following table, which shows the different families of resolutions and their required file names:
iPhone 6 Plus |
iPhone 6 |
iPhone 4s, iPhone 5 |
iPad Retina |
iPad | |
---|---|---|---|---|---|
Devices |
iPhone 6 Plus, iPhone 6s Plus |
iPhone 6, 6s |
iPhone 4s iPhone 5, 5C, 5S iPod Touch 5G |
iPad Air, Air 2, iPad mini Retina |
iPad, iPad 2, iPad mini |
Resolution |
1242 x 2208 |
750 x 1334 |
640 x 960 640 x 1136 |
1536 x 2048 |
768 x 1024 |
File name |
file@3x.png |
file@2x~iphone.png |
file@2x.png |
file@2x~ipad.png |
file@1x.png |
Note that we are showing the devices supported by iOS 9, which is the version that we are using for development purposes.
The above table corresponds to the five resolution families that are available at the time of writing this chapter. In the table, you will see the different devices of each family, their resolutions, and the names that you will need to specify for each of them.
The filenames are composed of a prefix (the filename) and a suffix that can be @3x
, @2x~iphone
, @2x
, @2x~ipad
, or @1x
(in this case, the suffix can be omitted), depending on the devices that you want the game to be available on.
Providing the needed files will not only result in a better resolution, but also will avoid the programmatic upscaling or downscaling of the image, thus improving the game's performance. Upscaling an image will result in smudgy-looking images but, on the other hand, downscaling images will allow you to reuse a high-resolution image for lower resolution devices. However, this approach is not recommended due to the waste of memory that a non-retina display could lead to.
From now on, when we add a new image to the game, we will need to include the corresponding @2x
, @2x~iphone
, @2x~ipad
and @3x
files, if available. Let's add the required images by performing the following steps:
In the project navigator, right-click on Art and select Add Files to "InsideTheHat"….
You'll find
rabbit@3x.png
,rabbit@2x~ipad.png
,rabbit@2x.png
,background@3x.png
,background@2x~ipad.png
, andbackground@2x.png
in the7338_01_Resources
folder. Select these four files and click on Add.
You can now run the game on other devices and check whether the resolution is maintained in all of them.
Another way of including new image files in a project is by taking advantage of the assets catalog that we mentioned at the beginning of the chapter. If you take a look at this folder, you will see something that is similar to the following screenshot:

Here, you can create a New Image Set by clicking on the + button and filling the 1x, 2x, and 3x slots with the corresponding images.
We started the chapter by looking at a default SpriteKit project, creating a new project, and learning how it is configured and what files it consists of.
You learned what a node is and how to create one with the SKNode
class, which is the parent of several important classes that take part in a game. Also, we showed the structure of a scene graph, with a parent SKScene
node and several children.
I explained some of the key properties and methods of SKNode
and SKScene
that will take part in the development of our game. You also had a look at the different steps a game loop requires to render all the contents on the screen.
Then, you learned how to create a scene and add a sprite and a background properly, taking into account their anchor point and their zposition
value to ensure that the background lies behind the rest of nodes.
In the last section of this chapter, we explored the characteristics that we need to keep in mind when developing a game for both iPhone and iPad devices, such as screen resolutions and image filenames.
Now that we know how to create a project and load sprites efficiently, let's take a step forward in order to make them interactive and the game playable.