I recetly decided to try to create some sort of a simple game. Nothing too crazy for my first game project. A simple 2D game with pixelart graphics and tiled environment seemed like a right fit.
Good question with pretty simple answer — I'm already familiar with the platform!
Plus Apple's SpriteKit framework is very approachable in my opinion. If you have some experience with developing iOS apps, SpriteKit should be pretty easy to grasp.
Because SpriteKit's tile editor sucks. The scene editor itself is not bad at all, game development can definitely benefit from drag and drop interfaces, but I found the tilemap workflow slow and painful. This set me off to a hunt for a better solution.
Tiled is lightweight, easy to use and it's free (although I encourage you to donate some coffee money to the developers). With Tiled, I can create new levels really easily. Importing new tilesets is quick and the layer system is a welcomed feature.
Creating a tilemap in Tiled editor
…is super easy. Upload a tileset, Tiled will slice it for you, create new map and start drawing.
Be aware that you cannot use Tiled's “mirror” feature, where you flip a tile horizontally / vertically. This is because we will be exporting the tilemap to .cvs files later and the information gets lost. If you want to have flipped tiles, you have to add it to your tileset. Aseprite can do this for you very easily and for a reasonable price.
Another advice would be to split your tilemap into layers according to their rendering order. For example, I grouped together all cosmetics (bones on the ground), ground tiles have their own layer and chests do too. Walls I split into background and foreground wall layers. You can do this in your code using some sort of a massive switch statement — but I think this will be the overall better path to take.
Export into CSV
In Tiled, go to File → Export As and choose CSV files. This will export all your layers into their separate .csv file and name them accordingly.
This is how we're going to get your tilemap into a Swift app.
- Import csv files and tilemap into project
- Slice tilemap image
- Parse CSV files and assemble them into nodes
Import the files!
This is as easy as dragging and dropping them into your project.
Import CSV files into some sort of “Level” folder, where you keep all your files related to that specific level. Drop the tileset into your .xcassets. This is probably the easiest way to access it as an image from code.
Slice the tileset image. 🔪
Not only cannot SpriteKit do this itself, he won't let anyone else do it for him! The scene editor will not accept any tiles that haven't been manually imported to its Tileset.sks file.
That means we rely entirely on code to do this, since the scene editor is not cooperating. 😒
I created a method that will greatly help us with this:
This will slice up the tileset image into array of UIImage tiles for us. My tile size is 16x16, so I use it a little bit like this:
let tiles = UIImage(named: "tileset_name")!.slice(tileSize: 16)
If you wish to know more about how UIImage and CGImage work, check out this stackoverflow question about slicing UIImage.
Parse the CSV files and convert them into nodes.
As you can see, the following method takes a file name and a closure. The closure is for you to determine what and where the node should be.
The coordinates are for you to determine where the node should end up. The last
tileIndex parameter is the number with which is the tile represented in the CSV file. If you remember our
tiles array of tile images from earlier, the
tileIndex corresponds to the index of the texture for the node.
Usage of the method might look something like this:
You can see I multiplied my coordinates by 16, because my tiles are 16x16px. This might be different for you.
This is why layers matter.
You will have to do this parsing for every layer you export. This might sound like a chore, but actually is pretty nifty. The closure you pass into the method gives you great freedom as of what do you want to do with the node. For example if you want to give SKPhysicsBody to every tile in certain layer, or create custom chest node for a layer that only contains chests.
Thanks for reading!
I hope you found some help and advice in this article. Or even better — that you got an idea how to improve this way of doing things. If so, I would seriously love to hear it!
Happy coding! 🖥