SpriteKit with Swift: Introducing the Level Editor

About: This project uses SpriteKit’s level editor interface to create a sandbox for experimenting with various SpriteKit features, including physics interactions and responding to touches. It is intended to be a surface-level introduction to the level editor so you can get setup and start playing around quickly and easily.

This project was created in Xcode 6.1.1 using the Swift programming language. The full project can be found here.

Initial Setup

Open Xcode and create a new project (⇧ + ⌘ + N) for an iOS Game. Setup your project with the following settings:

  • Product Name: DemoSpritePlayground (or whatever name you want)
  • Language: Swift
  • Game Technology: SpriteKit
  • Devices: iPad

I also limited my project to Landscape-only orientation for simplicity’s sake. You can change these settings by selecting the project (top-level) in the file browser and checking/unchecking the boxes under Deployment Info.

Project Resources

You can get the images/textures used in this project here, or you can use whatever images you want. All that really matters is that you have a background image and some sprite textures to choose from.

If you do choose to download the resources above, include them in your project by unzipping Images.zip and dragging the Images folder into your project directory in Xcode. When prompted, make sure “Copy items if needed” is selected and click Finish:

Copy Items if Needed

You should now see the Images folder in your project directory.

Getting Started

First you’ll want to clear out much of the existing project code. Start by editing the methods in the GameScene.swift file so that they look like this:

override func didMoveToView(view: SKView) {
    /* Setup your scene here */
}

override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    /* Called when a touch begins */
}

override func update(currentTime: CFTimeInterval) {
    /* Called before each frame is rendered */
}

You won’t need to alter AppDelegate.swift or GameViewController.swift for this project.

Level Designer Overview

Now that your project is a blank slate, we’ll get started with Xcode’s extremely useful Level Editor interface. This is also referred to as the “Level Designer” or “Scene Editor,” the latter of which seems only to be the case in projects that use the SceneKit library (as opposed to SpriteKit). Fair warning: I may use these terms interchangeably throughout this tutorial — be aware that other resources you use may refer to it as something else.

Anyway, in your project directory, you will see a file called GameScene.sks. Click it and you’ll see the Level Editor interface. Some of its features will look familiar to you right away if you’ve ever used Xcode Storyboards or the interface builder. The following image and its description are courtesy of this excellent tutorial, which is part of Techotopia’s iOS 8 App Development Essentials:

Please visit the Techotopia page linked above to read this great tutorial!
Please visit the Techotopia page linked above to read this great tutorial!

Image key:

  • (A) Scene Canvas – This is the canvas onto which nodes may be placed, positioned and configured.
  • (B) SKNode Inspector Panel – This panel provides a range of configuration options for the currently selected node in the scene canvas.
  • (C) Object Library – The Object Library panel contains a range of node and effect types that can be dragged and dropped onto the scene. Clicking on the far right button in the Object Library toolbar displays the Media Library containing items such as textures for sprites.
  • (D) Simulate/Edit Button – Toggles between the editor’s simulate and edit modes. Simulate mode provides a useful mechanism for previewing the scene behavior without the need to compile and run the application.
  • (E) Zoom – Buttons to zoom in and out of the scene canvas. You’ll notice right away when using the touchpad or a mouse wheel to zoom that for some reason, zooming and scrolling are inverted in this interface. If it really bothers you, just use the zoom buttons.

Adding a Background via Code

For the purpose of illustrating the difference between creating Sprites programmatically and creating them via the Level Editor, we’re going to set the background in code. Add the following two lines to the didMoveToView method your GameScene.swift file:

let bgSprite = SKSpriteNode(imageNamed: "genericBg.jpg")
self.addChild(bgSprite)

Build and run the project, and you should see the background image as expected. That was rather painless, right?

Note: In this case, we don’t have to reposition or scale the image, since we’re using an oversized image to begin with, but if you’re using an image that is exactly the size of the screen, it may not appear where you expect. This is because addChild adds new sprites at position 0, 0 (bottom left corner) by default. If you want to use an image sized specifically for your screen, you’ll have to change its position.

Adding a Background via the Level Editor

First I’m going to make you delete what you just did (sorry!) and show you another way to accomplish the same thing, with a little more control. Go to GameScene.swift and remove the two lines you just added to the didMoveToView method.

Now click on your GameScene.sks file to see the level editor interface. What I’m about to show you is so stupidly easy I’m almost hesitant to recommend it. All you have to do to add your background sprite to the scene is click the genericBg.jpg file in your project directory and drag it onto the scene. Boom. Done.

Doing More with Less

Why did I make you add the background both ways? Well, if you’ve ever added sprites programmatically before, you know that doing any additional setup (positioning it, giving it a physics body, etc.) requires many more lines of code. With the level editor, you can accomplish most of this simple setup without writing a single line of code.

Select the background sprite you just added and explore the node inspector for a bit. You’ll notice you can set several of the sprite’s properties here:

Xcode Node Inspector

Change the following properties of your background sprite:

  • Name: background
  • Size: 1024 x 770 — or whatever size roughly fits the scene’s boundary.
  • Body Type (under Physics Definition): None

Position the background so that it covers your whole scene.

Add More Sprites

Use the same method outlined above to add some other sprites to your scene. In my example, I’m using the genericBigCircle.png and genericBigSquare.png images. Position them wherever you want on screen, then click the Simulate button. You should see your sprites fall right off the screen! The level editor’s Simulate feature is an excellent way to test physics interactions without having to build and run the app.

Names

Give each sprite you added to the scene a unique name. This will be important when we want to make them respond to touches. I named my sprites “square1” and “circle1” for their respective shapes.

Physics Bodies

For now, we don’t want our sprites moving around unless we tell them to. For each sprite you added, uncheck its Affected By Gravity property (under Physics Definition). You can also change the Body Type if you want. Changing this setting to Alpha mask will result in the sprite having a physical body exactly the size and shape of the sprite itself, which allows for the most precise interactions.

There are many other settings here we could get into, but we’ll leave those for a more in-depth guide in the future.

Note: Another way to stop your sprites from falling off the screen is to add the following line to GameScene.swift in the didMoveToView method:

physicsWorld.gravity = CGVectorMake(0, 0)

Choose whichever method best suits your project, or do it both ways.

Responding to Touches

We’re going to make our sprites into selectable objects so we can drag them around the screen and make them run into one another, for science! All of the code in this section should be inside the GameScene.swift file.

Sprite Types

Something you may want to do is create a Struct for types of sprites. This can be useful if you placed multiple squares and circles on your scene and you want each type of shape to respond to touches or other events in different ways. We’ll need it for sure for this project, so add the following lines just below import SpriteKit:

struct SpriteType {
    static let Circle : String = "circle"
    static let Square : String = "square"
    static let Background : String = "background"
}

Subclass Methods

In addition to the three methods already in this class, you’ll need one you can call when you want to select a sprite by touching it. Add this method to GameScene.swift:

/*
    This method gets the sprite node at the touched location, if one exists.

    Note: In this project, a node will always exist, since the background is a sprite itself.
    This means we'll have to do some special handling to determine the type of sprite selected.
*/
func selectSpriteForTouch(location: CGPoint) {

}

Handling Touch Events

Before we can do anything with selectSpriteForTouch, we need to handle touch events. Add the following code to touchesBegan:

// Record initial location of touch.
let touch = touches.anyObject() as UITouch
let location = touch.locationInNode(self)

// Select the sprite where the touch occurred.
selectSpriteForTouch(location)

We’re doing three things here:

  1. Since touchesBegan takes an NSSet of generic objects, we need to get one object and cast it as a UITouch so we can access information about it.
  2. locationInNode(self) gives us a CGPoint where the touch occurred. We’re telling it to look for a point relative to the whole scene (a.k.a. “self”).
  3. Finally we’re calling selectSpriteForTouch with the location of the touch.

Selecting a Sprite

How do we go about actually telling the game which sprite to select? Does GameScene.swift even know about the sprites in our .sks file? We have no references to the sprites in code, so how do we “find” them?

Fortunately, this is also ridiculously easy. Add the following code to selectSpriteForTouch:

// Get the node at the touched location.
let touchedNode = self.nodeAtPoint(location) as SKSpriteNode
println("Selected sprite with name: (touchedNode.name)")

To ensure this works, run the project and touch some sprites, then check the console output. Make sure you also click the background!

My console output looked like this:

Selected sprite with name: Optional(“background”)
Selected sprite with name: Optional(“circle1”)
Selected sprite with name: Optional(“background”)
Selected sprite with name: Optional(“square1”)
Selected sprite with name: Optional(“background”)
Selected sprite with name: Optional(“background”)
Selected sprite with name: Optional(“square1”)

There are two important things to note here:

  1. The sprites we added with the level editor are Optionals, which will need to be unwrapped if we want to access or change some of their properties.
  2. We are able to select the background, which means our sprite selection just got a little more complicated.

Responding to Different Sprite Types

There are many ways/places to do this, and I can’t promise my way is the best way or even a good way, but since our end goal here is really to give you a sandbox to play in, it will have to suffice.

Storing the Selected Node

First, add a variable to the top of the GameScene class that we’ll use to store the selected node so we can reference it in other methods:

var selectedNode: SKSpriteNode?

Action Depending on SpriteType

Now we’re going to tell the game to act only if the selected node is something other than the background. Add the following code to selectSpriteForLocation:

if touchedNode.name!.hasPrefix(SpriteType.Background) {
    // Reset selectedNode (prevents selectedNode from "snapping" to touched location).
    // Thanks to Robin from the comments section for pointing this out!
    selectedNode = nil
} else {
    // Store the selected node for future use.
    if selectedNode != touchedNode {
        selectedNode = touchedNode
    }
}

It’s important to note that touchedNode.name will require unwrapping in order to use hasPrefix(). In this case it’s safe to explicitly unwrap since we know every touch must be inside the boundaries of some node. Also note that this won’t work if you forgot to name your sprites when you added them to the level editor or if you forgot to add the SpriteType struct to top of the GameScene.swift file.

Moving the Selected Node

Finally, we’ll tell the selected sprite to move. Override touchesMoved:

override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
    // Record location of touch.
    let touch = touches.anyObject() as UITouch
    let touchLocation = touch.locationInNode(self)

    // Define a movement action for the sprite.
    let moveAction = SKAction.moveTo(CGPointMake(touchLocation.x, touchLocation.y), duration: 0.0)

    // Move the sprite (if one was selected).
    if let node = selectedNode {
        node.runAction(moveAction)
    }
}

Overview of this method:

  1. It captures the location of the touch, just like touchesBegan. The difference between the two methods is that this one will fire constantly, as long as the touch keeps moving.
  2. moveAction is a variable that stores an SKAction, which is something that can be enacted upon a sprite using the SKNode class’s runAction method. SKAction.moveTo takes a CGPoint for the location to move to, as well as a duration (how long should moving there take?).
  3. Finally, it executes the action on the selected node, as long as selectedNode is not nil.

Now if you build and run your project, you should be able to touch and drag your sprites around the screen. Try running one into another to see what happens (notice the abysmal FPS — thanks Quicktime!).

All Done! (technically)

If you’ve gotten this far, you technically have all you need to create a sandbox environment for experimenting with sprite creation, selection, and interaction, but check back in the next couple weeks for a forthcoming post involving gravity fields, particle emitters, and other awesome things from the level editor’s object library.

In the mean time, I encourage you to delve deeper into the level editor’s features on your own, especially setting sprite properties in the node inspector panel and adding new nodes from the object library. Also please share anything interesting you discover!

Where to go from here?

Check out some of these resources to learn about more features you can add to your SpriteKit sandbox:

5 thoughts on “SpriteKit with Swift: Introducing the Level Editor

  1. thanks great tutorial
    i added

    if touchedNode.name!.hasPrefix(SpriteType.Background)
    {
    selectedNode = nil

    // Don’t do anything!
    } else {
    which stops the sprite snapping to your finger if you tap the background after touching a sprite

    Like

    1. Thanks for the heads up! Can you elaborate? I suspect this has something to do with casting, but I just started a new job and haven’t had the time or inclination to jump back into Xcode. If it’s something simple I’ll happily update my guides this weekend.

      Like

Leave a comment