Using SpriteKit to Create Engagement and Turn Your Users Into Participants
“Show the readers everything, tell them nothing.”
Some ideas are better explained visually. People often need to see something with their own eyes before it becomes real; to be told a fire is approaching is one thing, to see it approaching is an entirely different experience. Case-in-point: recently The Washington Post published what may be the most impactful piece of journalism in the fight against COVID-19. It was the most-read article in the website’s history. Why is this? The article didn’t tell you how the virus was spreading, it showed you.
The Washington Post created a set of dynamically-generated animations that visualized the effects of social-distancing, localized quarantine, and what happens when you have neither. It was a simple, yet powerful, visualization of how an infectant can spread like wild-fire across a population and the steps we can take to stop it. Just like a static graphic may enhance the words on a page, these visualizations brought their message to life.
This led me to ask the question – how could something like this be built on iOS? What is the effort-level to build something like this and ship it in your next app? Luckily there is a framework suited perfectly for this task: SpriteKit.
|With Social Distancing||Without Social Distancing|
Creating simulations like these is no longer a Herculean-task. Using SpriteKit paired with UIKit, an iOS developer could build a simulation like this in an afternoon.
Throughout this post, I’ve added contextual links to relevant bits of code in the sample app. Get the entire source code for the app shown above here.
SpriteKit is Apple’s under-utilized, in-house game engine. I’ve long-touted it’s ability to add a “wow-factor” to your app (even if it’s not a game). Leveraging your existing Swift and iOS knowledge you can quickly and easily create interactive animations and simulations that would be difficult, if not impossible, when using
CoreGraphics. Running on Apple’s Metal rendering engine, SpriteKit is fast allowing us to set up hundreds of physics bodies and performantly calculating collisions afforded by the GPU’s parallel computing capability. If you look at the core logic of the sample project, in
SimulatorScene.swift, it only amounts to about 350 lines of code; a small level of effort for the striking visual complexity it provides.
The Not-So-Secret Ingredient: Interactivity
Authors use painstaking detail to help readers visualize a scene; to convey some feeling or some message. We, as developers, have it much easier—we have a wider variety of media to convey that message—video, sound, tactility, and vitally, interactivity. The best way to convey a message is to use all of these.
If being shown something is better than being told, then what’s better than being shown? Involving the user and letting the user interact with your information. That’s how I took the Washington Post’s simulator a step-further—I enhanced it with
UIControls to let the user change the parameters of the simulation: the percentage of people practicing social distancing, recovery time, and simulation speed. Allowing users to interact with your app and see immediate feedback makes it memorable. The cause-and-effect nature of interaction can transform your paragraph of information into a powerful interactive teaching tool and an “aha moment.” Interactivity is what makes SpriteKit unique—it was built for this.
Bringing Your Idea to Life
Many of the paradigms you’ve learned developing iOS apps using
UIKit can be transferred over to developing interactive SpriteKit features for your app. All SpriteKit scenes, at their simplest, are built using two things: nodes and actions. Nodes (
SKNode) are the building blocks of your scene. When placing a node in a scene it becomes a child of the scene (or node) it is placed in. Every node has a
parent property, similar to UIView’s
superview properties, respectively. By interacting with this node tree you can update and query the state of your scene.
SpriteKit has a full-fledged physics engine, which means we can set up complex movement and behavior with minimal overhead. The graphical representation of the aforementioned
SKNode is separate from its physical representation—what this means is that every node has a physics-body (
SKPhysicsBody) property that we use to define its physical characteristics (mass, shape, density, dynamism, etc). A physics-body is required to register and react to contact.
SKAction) are how we bring life to a scene—it’s what animates the nodes placed in a scene, but it does even more than that.
SKActions can be used as timers, animators or triggers to other callbacks in your code. They’re tied to the run-loop, so if you pause the scene, or change the
speed property of a scene, any time-related properties (e.g. wait) set on an
SKAction will scale proportionally to that change—something that would be difficult to implement using an
NSTimer. Time-based simulations and interactions are particularly well suited for a SpriteKit implementation due to the flexibility of
SKAction and its mutability relative to the object it is acting upon.
|SKNodes in a Scene||SKNodes with SKAction applied|
Setting the Scene
If nodes are your actors and actions are your script then scenes (
SKScene) are their stage. This is your window into SpriteKit; it’s where you respond to contact and set the rules of your mini-universe. In this simulator, the rules are simple: every node has three possible states—healthy, infected or recovered – those states change based on contact registered through the
SKPhysicsContactDelegate. Once contact is detected, the only condition we’re concerned with is if one person is infected and the other is healthy – if this evaluates to true we change the state of the healthy individual to infected.
One of the cool aspects of using SpriteKit is “setting the stage” and letting scenarios play out on their own dictated only by the rules you set up. We want our simulations to be non-deterministic, which means we need to introduce some randomness. Every node in the simulation starts with a random vector that defines its initial movement. When coming in contact with another node or the boundary of the scene the node’s current vector is reversed…usually. I’ve set up a “wild card movement” boolean so that 12.5% of the time when a node makes contact with something it changes to another random vector instead of reversing its current one; why is this? One, it’s to make the movement more realistic, and two, it’s to prevent nodes from getting stuck in an endless loop between two objects.
Combining SpriteKit with UIControls
Combining a SpriteKit view (
SKView) with familiar
UIKit objects allows users to interact with your creations using the interfaces they’re already familiar with and saves development time that would have been spent spinning-up a solution in SpriteKit; it’s the best of both worlds.
I’ve defined three protocols for communication between the simulator scene and the view controller. By setting up
SimulatorSceneDelegate we can tell our view controller when infection and recovery states have been updated and update labels accordingly.
SocialDistancingDelegate updates our scene based on changes to the social distancing percentage and recovery time sliders. Lastly,
GameStateDelegate updates our scene based on interactions with the pause/start and reset buttons as well as the simulation speed slider.
As proven by the popularity of the Washington Post’s COVID-19 article, information is more potent when you let your audience be an audience; let them see what you’re trying to convey, and even take it a step further—let them interact with it. Words are easy to skim over, but there’s something hard-wired in us as humans about pushing a button, flipping a switch, sliding a slider. The next time you’re brainstorming a cool idea and shrug it off as too time-consuming or too difficult to implement, don’t forget about SpriteKit—add it to your arsenal of tools as a developer and wow your audience.