Reasons not to use SpriteKit for your game

Rant-Warning: This post distills 1.5 years of pure frustration into a couple of pages. Be warned: May contain explicit language.

When Apple announced SpriteKit in 2013 I got pretty excited: Apple offered an alternative to Cocos2D and, to some extend, for engines like Unity and Unreal. Native to the iOS platform and, what I believed, heavily optimized for it. It seemed to be a very light weight, minimalistic framework for mobile projects which wouldn't require you to learn a new IDE or language. And if Apple itself created it specifically with focus on mobile gaming, it should outperform a bulky and highly complex engine like Unity in terms of battery life quite easily... At least that's what I was expecting. I quickly started picking it up and experimented with it. Within a few minutes I experienced my first spaceship passing a randomly generated meteor belt. Exhaust particles were populating my screen and collision detection was working out of the box. All created within one afternoon right after picking it up: I was hooked.

After working with it for more than a year and experiencing IMMENSE frustration I would like to inform other developers about the downfalls and write the post I wish I would have read back then.

You will find the introduction seductively easy if you have had any experience working on iOS applications before. Everything seems to be smooth and works just the way you would expect. It feels familiar like meeting an good, old friend. You don't expect it to be that friend who is going to pull down your pants in front of everybody on graduation day.

Sadly that's exactly what kind of guy SpriteKit is: When you are far enough into your project to realize its pitfalls and amount of horrible limitations, it is too late already. You will have sunk several days or even weeks into your project at that point. I write this to warn you about this.

Let's start with the most obvious one: SpriteKit is iOS exclusive. It's great approachability let's you look over this limitation, because it is going to be super easy, comfortable and the game will be done super quickly anyway. Let me tell you that there is no reason at all to limit yourself to one platform AND also be using what I consider the worst tool in the business, by far.

The first steps are performed incredibly easily and you have a bunch of fun seeing your objects colliding, sliding and spinning around. After these first experiments you will start establishing the backbone of your game and create the basic data control structures. And then, when it is time to do the first real visuals, you probably already sunk hours into your project and it already hurts to start all over in a real engine.

So before you start a project, consider this list:

1. An iOS update can and will completely change the behaviour of your game.
If you don't have a second (modern) iPhone lying around, you are probably going to be very careful about installing the iOS beta releases. Yet these are the only way to see if your game is going to run the same way as before on the next iOS version: Simulator performance is horrible and unusable. For example the change from iOS 8 to 9 completely broke my game. This did not only happen to me, but also to a bunch of other developers. (See links below) If you consider that Apple has been running public beta releases over the last year, you should be concerned. If my game would have been released at that point in time, I would have received 1-star reviews without a realistic way for me to react to this. This should be a big no-go already.

Severe framerate issues
Thanks to the this broken update and the massive framerate issues, I was able to experience all of SpriteKit's wonky physics. While the game was rendering smoothly in iOS 8, after an OS update the fps in the game dropped from a stable 60 into the painful sub 20 region. Once you reach low(er) fps values, everything within SpriteKit seems to be falling apart regarding collisions. Tunneling becomes a HUGE problem. Especially on screen edges, where the collidable edge of the rectangle is 1px wide. Even switching to precise collision detection does not change this - quite contrary it makes it worse because the framerate will plummet even further.

[By the way: If you are a game developer I highly recommend this talk about framerate independency by Jonathan Blow.]

Some Shaders stopped working:
Imagine waking up one morning and most of your painstakingly created shaders stop working and fall apart.

Particle emitter's behaviour change
Also the SKEmitterNode is broken since then. It is pretty much impossible now to generate a smooth trail using them. You think rendering a new particle each frame while decreasing all past particles in size will grant you a smooth trail? I thought so too until I met this pain inducing class: The particle birthrate behaves uncomfortably unreliable: Even with 500 particles a second smooth trails are not possible for medium to fast movement velocities. I ended up changing the theme of my game from a simplistic "trail" approach to using "smoke" as much as possible, since it was the only effect that looked neat in this broken state, if you spend enough time on it. I basically had to change the art-style of my game to compensate for a classes buggy behaviour.

2. Fundamental game objects are completely broken
If you are considering SpriteKit for your game it's probably to save yourself some work because you are working alone or in a tiny team and / or on a small budget. With a small budget your game will also most likely rely on simple game objects like rectangles, circles, etc...

SKShapeNode
As an iOS developer I am pretty fond of CAShapeLayers and the way you can animate them using key-frame-animations and basically reshape them on the fly. When you see SKShapeNode for the first time, it seems to be very similar. Don't get excited though... Sadly, pretty much the only similarity between those two is that you can initialize them with a path.

SKShapeNode's change of path is not animatable contrary to CAShapeLayer. You will see that using them as game objects is horrible anyway though:

After some time into your game project you will wonder where the fps drops originate from. After some research you will stumble upon this excellent little rant (I feel you, bro <3).

So you are forced to a) convert your shape nodes into textures (yes, every time you change something you would have to generate a new texture) or b) you can control these performance issues by writing your own little node-recycling pool. whenever you need a shape node you ask your pool for a new one. If there is an old one, recycle it, otherwise hand out a new one. Every time you want to delete a node, hand it back to your recycler. I chose option b, which led to some very funky behaviour down the road. Rendering artefacts, missing physics-bodies, etc... I was able to fix them, yet it was super annoying to debug.

SKLabelNode
Searching for a simple way of displaying text in your game? You believe Apple got you covered? Well, you will assume so until you realize that you cannot apply shaders and most effects to these labels. You have to generate a texture from them to do so, which renders the text immutable. So anytime you want to change the text, you have to create a texture from it, remove the old texture and replace it with a new one. How convenient and energy efficient!

You desire to write a multilane label with a description? Bummer: No freaking multiline support. Even "\n" will be interpreted as space and you are stuck with developing your own multiline implementation.

Sound
When using sounds within a SpriteKit project, the official way was to use SKActions which did not allow you to preload a sound. So anytime you played a sound, a small, yet noticeable frame drop occurred. Since iOS 9 you can use SKSoundNode which I have not tested, because I had already switched to using OpenAL at that point in time.

Shadows / Lighting
And Mr. Developer spoke: "Let there be light," and there was light - yet the maximum shadow count was 6. The moment a light source hits more than 6 nodes casting shadows, the shadow casting becomes unreliable. I could completely understand the decision to not support dozens of shadows for reasons of energy preservation and document it. This is not the case though: Once you succeed the 6 shadow mark your shadows are going to flicker or disappear randomly.

You are, once again, forced to develop your own subsystem keeping count and managing lighting- / shadow nodes. Instead of one sun you're probably end up with 3-4 lightsources in the same spot being responsible for different shadows. Yes, this does not only sound energy inefficient... It is.

Also you will painfully realize: It is not possible for SKShapeNode to render a shadow. It does not matter how simple of a shape it is: This behaviour is not supported.

And yet again you are stuck with developing your own solution for a problem you believed was solved by SpriteKit. I solved it by subclassing SKShapeNode and adding a method that generates a black texture from its path. Then add this texture with an alpha value of 0.001 on top of the original node. Overriding the redraw method to force a redraw every time the path was changed. I consider this a hacky solution which is likely to break at some point in the future, that's why I have not published it (yet). I may upload it to Github if you ask for it at some point though.

If you plan to cast realistic shadows where shadows are not piercing through existing walls, you are completely out of luck by the way. A Stackoverflow User posted this regarding this topic.

Let me quote Apple's official statement on this topic:

"Apple Developer Relations 14-Dec-2015 01:35 PM
There are no plans to address this.
We are now closing this report."

So am I: Closing this report. The only benefit that SpriteKit has in store for you is that as an iOS developer you do not have to learn a new IDE or new tools in general. Sadly, you will spend pretty much the same time searching for work-arounds and cheap hacks to get the simplest things to work. I highly recommend you to either write your own engine or use an existing one like Unity or Unreal that has a lot more features, better support, better performance and also enables you to build the same game for multiple platforms.

Future of SpriteKit
I see SpriteKit's future exclusively as being a joyful and motivating way to learn Swift. It is a great opportunity for students to familiarize themselves with Apple's programming language, IDE and some very basic game design concepts. But that's it. Do not use this for anything else than prototyping.