Unity3D: Pixel Perfect Tilemap Display

In Dojo Storm, we are using a tilemap to display the world, and in order to draw it I am simply instantiating a unique game object for each tile with its sprite set to that tile's texture.  I then place the game objects side by side in an array, and voila: we have a tilemap!  However, for the longest time I was having a few issues.  Black lines were showing up between tiles when the camera would move, and the tiles would appear to be distorted.  It took me forever to solve these issues, so I want to present the solutions I discovered so others can benefit.

My Method

First, I want to give a quick overview of how I am constructing the tilemap.  I suspect that my solutions are not the fix for all tilemap issues, so if you are doing something vastly different than me you may want to look around for a separate fix.

Each level is composed to 2 files: the tile set, which is a 2D array of 16x16 pixel-art textures, and the actual tile map, which is a 2D array of integers which represent each individual tile.  This is fairly standard for tilemaps.  Each integer in the tilemap corresponds to a 16x16 texture in the tileset.  So, for instance, if one of the elements of the tilemap is 22, that means its graphic is the 22nd texture from the tileset (starting at the top-left, in our case).

In Unity, I have to take both files and combine them in order to generate the world.  The first thing I do is loop through the tileset, and break it apart into individual textures.  Once the tileset is broken up, I can read through the tilemap and create a new game object for each individual tile, assigning a sprite to that object with the corresponding texture.  The last thing to do is set the position of each tile so they show up in the world as they are supposed to, right next to each other.

Now that you have an idea of what my method is, I can demonstrate how I fixed the black-line issue.

Fixing the Tileset

As it turns out, the lines that were appearing between the tiles were not arbitrary - they were the pixels adjacent to the individual tiles.  There may be some sophisticated way to fix this, but I could not figure it out.  Instead, what I did was add padding to each tile in the tileset.  This way, the lines will still show up, but instead of being the pixels from the adjacent tile, they will be the pixels from the current tile - so they'll blend right in!

Fortunately, there is already a tool for adding this padding to the tileset.  You can find it here.  In order to run it on Windows, I use the following command:

java -jar TileFix.jar -f B_W_DS_tileset.png -h 16 -m 0 -o newtileset.png -s 0 -w 16

Note that "B_W_DS_tileset.png" is the name of my tileset, and it has to be in the same directory as TileFix.jar.  The padding that is added to the tileset will transform it like so:

I also had to modify my algorithm for splitting the tileset into separate textures.  Since there is a one-pixel padding around each tile, they are now each 18x18, but we still want to create 16x16 textures.  Keep this in mind for your own algorithm.

Now that the lines-between-tiles issue is resolved, I still have the problem with distorted tiles to deal with.

Resizing the Camera

Fixing the distorted tiles is actually part of achieving what is called "pixel perfection." This article breaks down the exact size to make your camera for pixel perfection. The final formula for camera size comes out to:

Camera Size = y / (2 * s)

Where "y" is the height of the screen in pixels, and "s" is the desired height of each square on screen.  Since I am using 16x16 tiles, "s" in this case would be 16.  So my total camera size would be "Screen.height / 32".  The denominator can be any multiple of (2 * s), so I actually use "Screen.height / 64" to zoom in further, causing each tile to be 32x32 on screen.


And that's it!  This method should eliminate any lines that appear between tiles, and prevent the tiles from being distorted.  I'm always open to ways to improve upon my methods, so if anyone knows of a better way to accomplish this please let me know!