Sprite Kit Best Practices

At this point, you already have a good idea of what Sprite Kit can do and how it works. You know how to add nodes to scenes and perform actions on those nodes, and these tasks are the building blocks for creating gameplay. What you may be missing is the bigger picture. That is, you need an understanding of how to plan and develop games and tools using Sprite Kit. To get the most out of Sprite Kit, you need to know:

Sprite Kit provides more than just a graphics layer for your game; it also provides features that make it easy to integrate Sprite Kit into your custom game tools. By integrating Sprite Kit into your games tools, you can build your content in the tools and read it directly into your game engine. A data-driven design lets artists, game designers, and game programmers collaborate to build your game’s content.

Organize Game Content into Scenes

Scenes are the fundamental building blocks for creating Sprite Kit content. When you start a new game project, one of your tasks is to define which scenes are needed and when transitions occur between those scenes. Scenes usually represent modes of play or content that appears to the player. Usually, it is easy to see when you need a new scene; if your game needs to replace all of the content onscreen, use a transition to a new scene.

Designing your game’s scenes and the transitions between them is similar to the role of view controllers in a traditional iOS app. In an iOS app, content is implemented by view controllers. Each view controller creates a collection of views to draw that content. Initially, one view controller is presented by the window. Later, when the user interacts with the view controller’s views, it might trigger a transition to another view controller and its content. For example, selecting an item in a table view might bring up a detail view controller to display the contents of the selected item.

Scenes do not have a default behavior, like storyboards do in a traditional iOS app. Instead, you define and implement the behaviors for scenes. These behaviors include:

For example, you could implement a model similar to a segue, where a new scene is always instantiated on a transition. Or you could design your game engine to use scenes that it keeps around persistently. Each approach has its benefits:

Allow Your Scene Designs to Evolve

Typically, a new scene is going to be developed in stages. At the start, you might be working with test apps and experimental ideas to understand how Sprite Kit works. But later, as your game gets more sophisticated, your scenes need to adapt.

In test apps, and some simple games, all of your logic and code goes in the scene subclass. The scene manipulates the node tree and the contents of each node in the tree, running actions or changing other behaviors as necessary. The project is simple enough that all of the code can live in a single class.

The second stage of a project usually happens when the rendering or game logic starts getting longer or more complex. At this stage, you usually start breaking out specific behaviors and implementing them in other classes. For example, if your game includes the concept of a camera, you might create a CameraNode class to encapsulate the camera behavior. You might then create other node classes to encapsulate other behaviors. For example, you might create separate node classes to represent units in your game.

In the most sophisticated projects, artificial intelligence and other concepts become more important. In these designs, you may end up creating classes that work independently of Sprite Kit. Objects of these classes perform work on behalf of the scene, but are not specifically tied to it. These classes are usually extracted from your Sprite Kit subclasses when you realize that many of your methods are implementing game logic without really touching any Sprite Kit content.

Limit the Tree’s Contents to Improve Performance

When Sprite Kit renders a frame, it culls all of the nodes that are not visible on screen. In theory, this means you could simply keep all of your content to the scene, and let Sprite Kit do all the work to manage it. For games with modest rendering requirements, this design would be adequate. But as your game gets larger and more sophisticated, you need to do more work to ensure good performance from Sprite Kit.

Typically, a node needs to be part of the node tree because:

  • It has a reasonably good chance of being rendered in the near future.

  • The node runs actions that are required for accurate gameplay.

  • The node has a physics body that is required for accurate gameplay.

When a node does not meet any of these requirements, it is usually better to remove it from the tree, particularly if it has many children of its own. For example, emitter nodes often provide special effects without impacting gameplay at all, and they emit a large number of particles, which can be costly to render. If you had a large number of emitters in the scene, but offscreen, then the scene potentially may need to process hundreds or thousands of invisible nodes. Better to remove the emitter nodes until they are about to become visible.

Typically, the design of your culling algorithm is based on your gameplay. For example:

  • In a racing game, the player is usually traveling around a track in a consistent direction. Because of this, you can usually predict what content is going to be visible in the near future and preload it. As the player advances through the race track, you can remove nodes the player can no longer see.

  • In an adventure game, the player may be in a scrolling environment which permits movement in arbitrary directions. When the player moves through the world, you might be able to predict which terrain is nearby and which terrain is not. Then, only include the terrain for the local content.

When content is always going to be added and removed at once, consider using an interim node object to collect a set of content. The content can be quite complex, yet still be added to the scene with a single method call.

What Shouldn’t Be in a Scene

When you first design a Sprite Kit game, it may seem like the scene class is doing a lot of the work. Part of the process of tuning your app is deciding whether the scene should perform a task or whether some other object in your game should do it. For example, you might consider moving work to another object when:

  • The content or app logic is shared by multiple scenes.

  • The content or app logic is particularly expensive to set up and only needs to be performed once.

For example, if your game uses the same textures for all its gameplay, you might create a special loading class that runs once at startup. You perform the work of loading the textures once, and then leave them in memory. If a scene object is deleted and recreated to restart gameplay, the textures do not need to be reloaded.

Use Subclassing to Create Your Own Node Behaviors

Designing new games requires you to make subclasses of the SKScene class. However, the other node classes in Sprite Kit are also designed to be subclassed so that you can add custom behavior. For example, you might subclass the SKSpriteNode class to add AI logic specific to your game. Or, you might subclass the SKNode class to create a class that implements a particular drawing layer in your scene. And if you want to directly implement interactivity in a node, you must create a subclass.

When you design a new node class, there are implementation details specific to Sprite Kit that are important to understand. But you also need to consider the role that the new class plays in your game and how objects of the class interact with other objects. You need to create well-defined class interfaces and calling conventions so that objects interoperate without subtle bugs slowing your development process.

Here are important guidelines to follow when creating your own subclasses:

Drawing Your Content

A large part of building a node tree is organizing the graphical content that needs to be drawn. What needs to be drawn first? What needs to be drawn last? How are these things rendered?

Consider the following advice when designing your node tree:

Working with Game Data

At any given time, your game manages a lot of data, including the positions of nodes in the scene. But it also includes static data such as:

Whenever possible, avoid embedding your game data directly in the game code. When the data changes, you are forced to recompile the game, which usually means that a programmer is involved in the design changes. Instead, keep data independent of the code, so that a game designer or artist can make changes directly to the data.

The best place to store game data depends on where that data is used within your game. For data not related to Sprite Kit, a property list stored in your app bundle is a good solution. However, for Sprite Kit data, you have another option. Because all Sprite Kit classes support archiving, you can simply create archives of important Sprite Kit objects and then include these archives in your game. For example, you might:

Here are a few guidelines for working with archives: