Combining the Power of SpriteKit and SceneKit

Final product image
What You'll Be Creating

Introduction

SpriteKit and SceneKit are iOS frameworks designed to make it easy for developers to create 2D and 3D assets in casual games. In this tutorial, I will show you how to combine content created in both frameworks into a single view to utilize the APIs that Apple has made available.

You will achieve this by adding a simple play/pause button and score-keeping functionalities to a 3D SceneKit scene through the use of a 2D SpriteKit-based interface.

This tutorial requires that you are running at least Xcode 6+ and have previous experience with basic SpriteKit and SceneKit content. If not, then I recommend you read some of our other Tuts+ tutorials about SpriteKit and SceneKit first.

It's also recommended that you have a physical iOS device to use for testing, which requires an active, paid iOS developer account. You will also need to download the starter project from GitHub.

1. Project Setup

When you open the starter project, you will see that, in addition to the default AppDelegate and ViewController classes, you also have two other classes, MainScene and OverlayScene.

The MainScene class is a subclass of SCNScene and provides the 3D content of your app. Likewise, the OverlayScene class is a subclass of SKScene and contains the 2D SpriteKit content in your app.

Feel free to look at the implementations of these classes. It should look familiar if you have some experience with SpriteKit and SceneKit. In your ViewController class's viewDidLoad method, you will also find the code for setting up a basic SCNView instance.

Build and run your app on either the iOS Simulator or your physical device. At this stage, your app contains a blue rotating cube.

Initial 3D scene

It's time to add a SpriteKit scene on top of the 3D content. This is done by setting the overlaySKScene property on any object that conforms to the SCNSceneRenderer protocol, in our example that's the SCNView instance. In ViewController.swift add the following lines to the viewDidLoad method:

When you build and run your app again, you will see that you now have a pause button positioned in the bottom left and a score label in the bottom centre of your app's view.

Overlay SpriteKit scene

You may be wondering why it is better to use this property rather than simply adding an SKView as a subview of the SCNView object. When the overlaySKScene property is used, both the 2D and 3D components of your app use the same OpenGL context to render content onto the screen. This performs significantly better than creating two separate views, which would each have their own OpenGL context and rendering pipeline. Even though the difference is negligible for this simple setup, the performance gained in lager, more complex scenes can be invaluable.

2. SceneKit and SpriteKit Interaction

There are many different ways in which you could transfer information between your MainScene and OverlayScene instances. In this tutorial, you are going to use key-value observing, KVO for short.

Before you implement the ability to pause the cube animation, you first need to add this functionality to the SpriteKit scene. In OverlayScene.swift, add the following method to the OverlayScene class:

The touchesEnded(_:withEvent:) method is invoked when the user lifts its finger(s) from the device's screen. In this method, you check whether the user has touched the pause button and update the scene accordingly. Build and run your app again to check that this piece of the puzzle is working correctly.

Working pause button

We now need to stop the 3D cube from rotating when the pause button was tapped by the user. Go back to ViewController.swift and add the following line to the viewDidLoad method:

Finally, add the following method to MainScene.swift to enable key-value observing:

If you build and run your app again, the cube should stop rotating when the pause button is tapped.

Just as information can be transferred from a SpriteKit scene to a SceneKit scene, you can also send data from SceneKit instances to SpriteKit instances. In this simple app, you are going to add one point to the score every time the cube is tapped by the user. In ViewController.swift, add the following method:

Note that we used the touchesEnded(_:withEvent:) method rather than a UITapGestureRecognizer, because UIGestureRecognizer objects cause the touchesEnded(_:withEvent:) method to be executed very inconsistently. Since you use this method for the pause button, you need to be sure that it will be called every time the user taps the screen.

In the touchesEnded(_:withEvent:) method, we perform a hit test for the final location of a touch in your sceneView. If the location of the touch corresponds with the location of the cube, one point is added to the score of your spriteScene. The text in the scene will automatically update thanks to the property observer of the score property in the OverlayScene class.

Run your app again and tap the cube to verify that the score label is updated whenever the cube is tapped.

Score updates when cube is tapped

Both the pause button and the score label exemplify how you can transfer information between SpriteKit and SceneKit scenes. This information, however, is not limited to numerical and boolean values, it can be whatever data type your project needs it to be.

3. Using SpriteKit Scenes as SceneKit Materials

In addition to being able to overlay a SpriteKit scene on top of a SceneKit scene, you can also use an SKScene instance as a material for SceneKit geometry. This is done by assigning an SKScene object to the contents property of an SCNMaterialProperty object. This lets you easily add an animated material onto any 3D object.

Let's look at an example. In your app, you are going to add a simple color transition animation to the cube instead of the static blue it currently has. In the init method of the MainScene class, replace the following code block:

with this code block:

This code snippet creates a simple SKScene instance with one background node, which is initially blue. We then create three actions for the transition from blue to red and green. We run the actions in a repeating sequence on the background node.

While we've used basic colors in our example, anything that can be done in a SpriteKit scene can be turned into a SceneKit material.

Build and run your app one last time. You will see that the color of the cube transitions from blue to red and green.

Cube colour change

Note that pausing the SceneKit scene does not pause the SpriteKit scene that we've used as a material. To pause the animation of the material, you will need to keep a reference to the SpriteKit scene and pause it explicitly.

Conclusion

Combining SpriteKit and SceneKit content can be very beneficial in a variety of ways. Overlaying a 2D scene on top of a 3D scene allows you to create a dynamic interface and it will result in a performance gain since both scenes use the same OpenGL context and rendering pipeline. You've also learned how to leverage SpriteKit to create animated materials for 3D objects. If you have any comments or questions, leave them in the comments below.

Tags:

Comments

Related Articles