Pixel Art Map Tiles in SpriteKit

I decided to use SKTileMapNode to render the mountain that must be climbed. There will be tiles for stone, sky, ice, snow, flowers, etc.

Tile Map

As I mentioned in my previous post, Mountain Dash renders 1x pixel art at 2x (which means 4 × 4 or 9 × 9 pixels on screen for each pixel in the original assets). I chose to do tiles at 16 × 16 pixels, so I need each tile to render at 32 × 32 points. It is entirely possible to do this using the Tile Set Editor and Tile Map Editor in Xcode, but I ran into two problems:

Every time you assign an image in the Tile Set Editor, it defaults to the image’s actual size (16 × 16) and must be manually changed to the desired size (32 × 32)

Occasionally Xcode for whatever reason decides to blur the tiles when scaling them up. The only solution with the Tile Set Editor is to remove and then re-add all the images (and manually retype all the sizes yet again).

Blurry Tiles

I already knew that I could fix the blur in code by setting filteringMode = .nearest for each texture, but while the Tile Set Editor lets you define textures, it doesn’t let you set filteringMode.

All of the above was way too much hassle and as it turns out unnecessary. Instead of scaling up my maps and my sprite by 2x, I could use all my assets at their native sizes and just use an SKCameraNode and set cameraNode.scale = 0.5 to scale in 2x. I need to use SKCameraNode anyway to scroll my map (you just change the position of the camera), so this was perfect. Also it’s helpful for debugging to use the camera’s scale to either zoom in (see pixel art details) or zoom out (see more of the map on screen at once). (The above screenshot was taken at 4x or scale = 0.25.) I may even add a pinch-to-zoom gesture to allow the player to do the same. The scale on SKCameraNode was unintuitive at first because it is the inverse of what I expected; I’m used to writing scale = 2 to double a view’s size.

Even with everything at 1x, I’ve still had the Tile Set Editor barf on me once and give me blurry tiles, so I’ve decided to permanently fix this by looping through all the textures in the tile set and setting the filteringMode. I wonder if there’s a better way of handling this…

let tileSet = map.tileSet
for tileGroup in tileSet.tileGroups {
for tileRule in tileGroup.rules {
for tileDefinition in tileRule.tileDefinitions {
for texture in tileDefinition.textures {
texture.filteringMode = .nearest
}
}
}
}