MACHINE-GAME.COM

Indie developer studio

SKScene

The root node for all Sprite Kit objects displayed in a view.

Overview

An SKScene object represents a scene of content in SpriteKit. A scene is the root node in a tree of SpriteKit nodes (SKNode). These nodes provide content that the scene animates and renders for display. To display a scene, you present it from an SKView object.

Creating a Scene

A scene is presented by a view. The scene includes properties that define where the scene’s origin is positioned and the size of the scene. If the scene does not match the view’s size, you can also define how the scene is scaled to fit in the view.

A Scene’s Size Defines Its Visible Area

When a scene is first initialized, its size property is configured by the designated initializer. The size of the scene specifies the size of the visible portion of the scene in points. This is only used to specify the visible portion of the scene. Nodes in the tree can be positioned outside of this area; those nodes are still processed by the scene, but are ignored by the renderer.

Using the Anchor Point to Position the Scene’s Coordinate System in the View

By default, a scene’s origin is placed in the lower-left corner of the view, as shown in the figure below. So, a scene initialized with a height of 1024 and a width of 768, has the origin (0,0) in the lower-left corner, and the (1024,768) coordinate in the upper-right corner. The frame property holds (0,0)-(1024,768).

A scene’s position property is ignored by Scene Kit because the scene is always the root node for a node tree. Its default value is zero and you can’t change it. However, you can move the scene’s origin by setting its anchorPoint property. The anchor point is specified in the unit coordinate space and chooses a point in the enclosing view.

Figure 2

Default anchor for a scene is in the lower-left corner of the view
Default anchor for a scene is in the lower-left corner of the view

The default value for the anchor point is zero, which places it at the lower-left corner. The scene’s visible coordinate space is (0,0) to (width,height). The default anchor point is most useful for games that do not scroll a scene’s content.

The second-most common anchor point value is (0.5,0.5), which centers the scene’s origin in the middle of the view as shown in the figure below. The scene’s visible coordinate space is (-width/2,-height/2) to (width/2, height/2). Centering the scene on its anchor point is most useful when you want to easily position nodes relative to the center of the screen, such as in a scrolling game. However, this effect is better achieved using a SKCameraNode.
Figure 3

Moving the anchor point to the center of the view
Moving the anchor point to the center of the view

So, to summarize, the anchorPoint and size properties are used to compute the scene’s frame, which holds the visible portion of the scene.
A Scene’s Contents Are Scaled to Fit the View

After a scene is rendered, its contents are copied into the presenting view. If the view and the scene are the same size, then the content can be directly copied into the view. If the two differ, then the scene is scaled to fit in the view. The scaleMode property determines how the content is scaled.

When you design your game, you should decide on a strategy for handling the scene’s size and scaleMode properties. Here are the most common strategies:

Instantiate the scene with a constant size and never change it. Pick a scaling mode that lets the view scale the scene’s content. This gives the scene a predictable coordinate system and frame. You can then base your art assets and gameplay logic on this coordinate system.

Adjust the size of the scene in your game. Where necessary, adjust your game logic and art assets to match the scene’s size.

Set the scaleMode property to SKSceneScaleMode.resizeFill. SpriteKit automatically resizes the scene so that it always matches the view’s size. Where necessary, adjust your game logic and art assets to match the scene’s size.

Listing 1 shows a typical implementation for when you plan to use a constant-sized scene. This code specifies a method to be executed the first time that the scene is presented. It configures the scene’s properties, including its scaling mode, then adds content. In this example, the scale mode is set to SKSceneScaleMode.aspectFit, which scales the contents equally in both dimensions and ensures that all of the scene’s contents are visible. Where necessary, this mode adds letter-boxing.
Listing 1

Creating a constant-sized scene

func createSceneContent() {
scene.scaleMode = .aspectFit
scene.backgroundColor = .black
// Add additional scene contents here.

}

If you expect a scene’s size to change at runtime, then the initial scene size should be used to determine which art assets to use, as well as any game logic that is dependent on the scene size. Your game should also override the scene’s didChangeSize(_:) method, which is called whenever the scene changes size. When this method is called, you should update the scene’s contents to match the new size.
How a Scene Processes Frames of Animation

The process of animating and rendering the scene is tied to the scene object, SKScene. Scene and action processing runs only when the scene is presented. A presented scene runs a rendering loop that alternates between processing the scene’s node tree and rendering it. This model is similar to the rendering and processing loop used in most games.

SpriteKit will only render the scene when something within it has changed, improving energy efficiency and allowing your game or app to perform other operations in the background.

Figure 1 shows the steps used by a scene to execute the rendering loop.

Figure 1

Frame processing in a scene
Frame processing in a scene

Each time through the rendering loop, the scene’s contents are updated and then rendered. You can’t override the rendering behavior; instead you update the nodes in the scene. However, the scene includes methods you can override to customize scene processing, and you can use actions and physics to alter properties of nodes in the tree. Here are the steps in the rendering loop:

The scene’s update(_:) method is called with the time elapsed so far in the simulation. This is the primary place to implement your own in-game simulation, including input handling, artificial intelligence, game scripting, and other similar game logic. Often, you use this method to make changes to nodes or to run actions on nodes.

The scene processes actions on all the nodes in the tree. It finds any running actions and applies those changes to the tree. In practice, because of custom actions, you can also hook into the action mechanism to call your own code. You cannot directly control the order in which actions are processed or cause the scene to skip actions on certain nodes, except by removing the actions from those nodes or removing the nodes from the tree.

The scene’s didEvaluateActions() method is called after all actions for the frame have been processed.

The scene simulates physics on nodes in the tree that have physics bodies. Adding physics to nodes in a scene is described in SKPhysicsBody, but the end result of simulating physics is that the position and rotation of nodes in the tree may be adjusted by the physics simulation. Your game can also receive callbacks when physics bodies come into contact with each other, see SKPhysicsContactDelegate.

The scene’s didSimulatePhysics() method is called after all physics for the frame has been simulated.

The scene applies any constraints associated with nodes in the scene. Constraints are used to establish relationships in the scene. For example, you can apply a constraint that makes sure a node is always pointed at another node, regardless of how it is moved. By using constraints, you avoid needing to write a lot of custom code in your scene handling.

The scene calls its didApplyConstraints() method.

The scene calls its didFinishUpdate() method. This is your last chance to make changes to the scene.

The scene is rendered.

Working With Scene Delegates

Although subclassing the scene class is a common way to handle scene processing, you can avoid it by using a delegate object instead. All of the scene processing methods described above have related methods in the SKSceneDelegate protocol. If a delegate is provided to the scene and the delegate implements one of these methods, that method is called instead of the one on the scene. For example, an iOS app might use view controller as the delegate.

Post-Processing in Scenes

A scene can process the actions on the scene tree in any order. For this reason, if you have tasks that you need to run each frame and you need precise control over when they run, you should use the didEvaluateActions() and didSimulatePhysics() methods to perform those tasks. Typically, you make changes during post-processing that require the final calculated positions of certain nodes in the tree. Your post-processing can take these positions and perform other useful work on the tree.

Subclassing Notes

Often, your game subclasses a scene to deliver gameplay. Your subclass usually:

Creates the scene’s initial content

Implements game logic that occurs each time a frame is processed

Implements responder methods to handle keyboard, mouse, or touch events

An alternative pattern to subclassing the SKScene class is to use a delegate that implements most of the game logic. For example, in an iOS game, you can make your view controller the delegate for your scene. The view controller already participates in event handling and can perform all of the other necessary duties described above.