I quickly ran into a snag upon firing up a new Xcode SpriteKit project and attaching a few test SKSpriteNodes to my SKScene. None of my dummy sprites were showing up at the size or location I anticipated! After a bit of web searching I realized that SKScenes are automatically scaled based on the screen size of the device. I can see how this is pretty handy, but it’s the kind of thing I like to make sure I get right early in the dev process. Here’s how I created a consistent positioning and sizing system I can trust.
Scaling Modes Summary
Please take the following opinions with a grain of salt, as I’m only a few days into developing with SpriteKit. These are my first impressions:
- SKSceneScaleMode.Fill – Scales scene to match the screen perfectly. This does not preserve the aspect ratio of your game elements (they will skew). I’m not sure when I would use this setting, despite it being the default. Check out how this works in the screenshot; the tiles are perfect squares.
- SKSceneScaleMode.AspectFill – This scaling option is similar to CSS’s background-size: cover. The entire screen will be covered by your scene, but some of your content may be cropped. On first glance, this seems useful for sidescrolling games like Jetpack Joyride, where one dimension never pans at all. This is probably the predominant choice for any game, with use of a ‘camera’ world node (more on this in a bit).
- SKSceneScaleMode.AspectFit – Here we let the scene be scaled so the entire scene is visible. Think CSS’s background-size: contain. You’ll get letterboxing if the aspect ratio of the scene isn’t the same as the device screen. This seems like a poor choice for most games.
- SKSceneScaleMode.ResizeFill – This mode performs no scaling at all, and simply sizes the scene to the same dimensions as the device screen. This is useful if you want strict control over your presentation, though you’ll have to do a lot more work to keep your experience consistent on different iOS devices.
The Official Word on Scaling Strategy
So the official documentation gives three strategies for scaleMode. They seem to suggest setting a SKScene size at startup, allowing the SKScene to be scaled to the screen size, and then using the scene’s coordinates as your “truth”. In general, this seems like a pretty good practice for consistent results on Apple devices.
Creating a World
In the case of my fledgling game, I want to take control of my own responsiveness in order to keep the game consistently fair on any device. If I let my tiles scale, certain aspect ratios might benefit by being able to see more of the scene at once. I’d rather resize my square tiles to be rectangles and keep the amount viewable consistent.
I’m familiar with using a ‘camera’ class from other game frameworks like MonoGame, and I want to apply the same concept here. I set my SKScene scaleMode to ResizeFill, which prevents any scaling and causes the scene’s dimensions to mimic the screen’s. I create a SKSpriteNode (named world) and attach it to my SKScene. Every sprite after this will be attached to my world node. The world node is now the relative coordinate for all other sprites, and I simply move the world around to move the ‘camera’. Not quite as elegant as a true camera, but it’ll work.
We’re on Our Own with ResizeFill
I have to do a bit more work this way in order to stay consistent across devices, as I’m not letting scaleModes handle the load. I’m adjusting my game elements size and position manually based on ratios to the screen dimensions. Each tile will always take up 10% of the screen’s height and width. That works well for this particular game’s requirements, but in the future I’ll probably look to AspectFill.