I’m in the process of developing a library of useful, reusable components that could be dropped as an external library into a SpriteKit project.
They include things like my NodeComponent
and an abstracted way of managing three different kinds of input from either macOS or iOS (single tap/left click, double tap/right click, and pan/mouse drag). It also has a physics component and a render component—things that come up in games of all different types.
The components often have a lot of editable properties that affect how entities behave in the game. Tagging these properties with the @GKInspectable
tag allows you to use these components within the SpriteKit visual editor.
You can customise properties with a range of types from the components panel.
There are advantages to this:
- Developing components with the editor in mind keeps that component focused on what it should be—a big bag of editable state—and stops me adding methods and functionality that would be better suited to a system or the game scene and its delegates.
- It’s fewer lines of code that I have to write—SpriteKit will automatically create the entities and then apply the components to them at runtime.
- It makes the editing environment closer to that of Unity, which should make moving that much easier.
Sadly, this support does not extend to libraries.
Reduced Test Case
I created a new Cocoapods library with an example project. The example project is set up exactly like the Game
template that ships with Xcode.
This library had a single file called ExternalComponent.swift
that looks like this:
import GameplayKit import SpriteKit class ExternalComponent : GKComponent { @GKInspectable var externalValue : Double = 0 }
I then added another component directly to the project that looks like this:
import GameplayKit import SpriteKit class InternalComponent : GKComponent { @GKInspectable var internalValue : Double = 0 }
Both components showed up in the editor and both the properties were editable.
To convert the nodes with components within a visual scene into entities, there’s a special abstract class to load the scene file—GKScene
—which finds all the nodes with components and automatically creates the entities for them.
If you use the Xcode Game template with Use GameplayKit
checked when setting up the project, Xcode automatically uses the GKScene
class to create the entities in the GameViewController
:
override func viewDidLoad() { super.viewDidLoad() // Load 'SceneSelection.sks' as a GKScene. This provides gameplay related content // including entities and graphs. if let scene = GKScene(fileNamed: "SceneSelection") { // Get the SKScene from the loaded SceneSelection if let sceneNode = scene.rootNode as! SceneSelection? { // Copy gameplay related content over to the scene sceneNode.entities = scene.entities sceneNode.graphs = scene.graphs // Set the scale mode to scale to fit the window sceneNode.scaleMode = .aspectFill // Present the scene if let view = self.view as! SKView? { view.presentScene(sceneNode) view.ignoresSiblingOrder = true view.showsFPS = true view.showsNodeCount = true } } else { print("Error with finding root node") } else { print("Error loading the SpriteKit scene") } }
This was where things started failing. It seems that the GKScene(fileNamed:)
method works correctly and returns the scene. However, it would fail when trying to cast the rootNode
property to my custom SKScene subclass.
If I removed the component that belonged to the Cocoapods library, everything worked as expected.
I don’t see any good reason for why it should be like this, especially as Storyboards allow you to use UIKit @IBInspectable
properties in external libraries and frameworks without issue.
Using the SpriteKit visual editor would be an asset when designing adventure games—easily moving around the starting position of NPCs and objects would make iteration that much quicker—but it’s a nice-to-have at the moment.
The advantages of not having to rewrite a lot of standard game code far outweighs the benefits of being able to adjust properties in the components panel of the visual editor.
I do think this is a bug and I have filed a radar. I imagine it’s about a low a priority as something can get for the SpriteKit team, but I still hope Apple gets this fixed.