A simple iOS Sprite Kit game using Swift

This tutorial will take you through the process of creating a simple Magic 8 ball style game

Setting up the project

  1. Create a new application in Xcode using the SpriteKit Game template, and name it Magic8Ball. Ensure that the Language option is set to Swift, the Game Technology set to SpriteKit.
  2. Download the assets for the Magic 8 ball application. Once you have extracted the assets from the zip file, add them to your project, either by dragging them, or using the File menu and selecting Add Files to Magic8Ball. Ensure that you check the 'Copy items into destination group's folder (if needed)' box. This step is especially important, as if left unchecked, if you try to transfer your project to another computer (e.g. come assignment submission) assets will be missing and you app may not run.
  3. The default project includes some demo files. Delete the 'GameScene.sks', 'Actions.sks' and 'GameScene.swift' files, choosing the "Move to Trash" option when prompted.
  4. Add a new file to the project, choosing the 'Cocoa Touch' option name it 'BallScene' and make it a subclass of SKScene (Ensure the language is set as 'Swift'). This will be the 'screen' for your game.
  5. If the first line of the new file reads import UIKit, change it to import SpriteKit
  6. By default the application will load the GameViewController file, which in turn loads the scene of the game (BallScene). By default it loads a scene from file. Replace the contents of GameViewController.swift with the following code:
    import SpriteKit
    class GameViewController: UIViewController {
        override func viewDidLoad() {
            super.viewDidLoad()
           
            let scene = BallScene(size: view.bounds.size)
                // Configure the view.
                let skView = self.view as! SKView
                skView.showsFPS = true
                skView.showsNodeCount = true
            
                skView.ignoresSiblingOrder = true
                
                /* Set the scale mode to scale to fit the window */
                scene.scaleMode = .aspectFill
                
                skView.presentScene(scene)
        }
    }

Adding the sprites

  1. Go to the BallScene.swift file, between the brackets after the line that starts class, add the code shown in the snippet below:
    override init(size: CGSize) {
        super.init(size: size)
            
        backgroundColor = SKColor.black
        let backgroundImage = SKSpriteNode(imageNamed: "ball.png")
        backgroundImage.position = CGPoint(x: frame.midX, y: frame.midY)
        backgroundImage.name = "ball"
        addChild(backgroundImage)        
    }
    This code adds the ball to the centre of the screen. Note that by default, the anchor point of sprites is their centre, so the centre of the ball image is positioned to the mid x and y positions of the at the centre position of the scene.
  2. Xcode should be showing an red error, which if clicked on, displays a message similar to the following: "'required' initializer 'init(coder:)' must be provided by subclass of 'SKScene'". Under the error there should be an option to 'Fix-it'. Choose this option which will automatically add the following code to the BallScene class:
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
  3. Run the application, you should see a screen with a magic 8 ball at the centre.
  4. The next stage is to add a text label to display the message from the magic 8 ball. We will add the label to the ball (rather than the scene) so if we happened to move the ball later, the label would move with it. Add the following code after the line in which we added the ball to the scene:
    //create a message node
    let message = SKLabelNode(fontNamed: "Chalkduster")
            
    //set the text and size
    message.text = "Maybe"
    message.fontSize = 20
            
    //position it in the center of its parent (the ball)
    message.position = CGPoint(x: 0, y: 0)
            
    //add the message to the ball
    backgroundImage.addChild(message)
  5. Run the project again, and you should see a message in the middle of the magic 8 ball.

Changing the message

It would be useful to change the message, so the magic 8 ball works more like a real one. To do this we need to be able to access the message label, and change its message at random. (We also need to be able to find the ball, as the message label is a 'child' of the ball)

  1. Add another line of code to the section you just added, as follows:
    message.name = "message"
  2. Add a similar line of code at the appropriate place earlier in the code to set the backgroundImage's name property to 'ball'.
  3. The next stage is to handle when the user touches the screen. Immediately after the {, start typing the word 'touchesBegan'. Xcode will suggest a method to enter, and pressing the tab key once the relevant suggestion is visible will input it. It should appear as follows:
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {}
    This method is called every time the user touches the screen in some way, the next step is to add code inside this method to locate the label and change its message based on a random number.
  4. Between the braces add the following code (replacing the 'code' placeholder):
    //find the label node
    let ball = childNode(withName: "ball")
    let message = ball?.childNode(withName: "message") as! SKLabelNode
            
    //generate a random number between 0 and 1
    let randomNumber = Int.random(in: 0...1) //for versions of Swift <4.2 use: let randomNumber = Int(arc4random_uniform(2))
            
    //if the number is a 1, set the message to Yes, if not set it to No
    if (randomNumber == 1) {
        message.text = "Yes"
    }else{
        message.text = "No"
    }
  5. Run the game again, this time when you tap the screen the first time, it will change to a 'Yes' or 'No'. After that it will only change when the random number results in a different message.

Further Development

Allowing more potential answers

Instead of using if and else to make decisions, we can choose a particular option based on a particular situation. In programming this is known as switch case.

Below is an example of how we can change the text of our label based on the random number

switch randomNumber {
    case 0:
        message.text = "Sure Thing"
        break
    case 1:
        message.text = "Probably Not"
        break
    default:
        break
}

In this example we are making a decision based on the randomNumber, saying that in the case it is a 0, set the label to read 'Sure Thing', and then 'break' which means to stop running this bit of code. However, in the case that the randomNumber is a 1, it will set the text to read 'Probably not'. we also have an option to set if none of the cases are met - which in this case does nothing.

At present our randomNumber is only 0 or 1, because we are getting the remainder when we divide a number by 2, modify the code so that we can have more options (I would suggest 5 or 6) and instead of using if and else to make decisions, use the switch case example, but extend it to provide more answers. If you get stuck ask your tutor for help.

Animating the message

Animating the message is fairly simple, we make it invisible by setting it's alpha value to 0:

message.alpha=0
then once we've set the new message we create and apply a fade in animation as shown below:
let fadeIn = SKAction.fadeIn(withDuration: 1.0)
message.run(fadeIn)