# How to get map tiles to line up nicely?

When I first created my code, there were quite a few cases where I would get horizontal and vertical gaps between tiles when displayed. I put this down to rounding issues with floats and just moved on to other things. Now that I've revisited the issue, I can't seem to get it working properly, even after trying my hardest to eliminate places where this sort of issue might happen. It seems to only happen when I place the camera at certain positions, which is why I think it's likely to be a float issue. If anyone could help me with the correct way to draw the tile layout, it'd be much appreciated.

This is my drawing method for the map:

public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
{
var topLeft = Camera.ScreenCoordinateToWorld(new Vector2(0, 0));
// Readjust the position of the topLeft element so we get the actual
// drawing position for the tile here
topLeft = new WorldCoordinate(topLeft.X - 1, topLeft.Y - 1,
-TileSize / 2, -TileSize / 2);
var topLeftDrawPosition = topLeft.DrawPosition;
var screenWidthInTiles = Game.GraphicsDevice.Viewport.Width / TileSize + 1;
var screenHeightInTiles = Game.GraphicsDevice.Viewport.Height / TileSize + 1;

for (var y = topLeft.Y; y <= topLeft.Y + screenHeightInTiles; y++)
{
for (var x = topLeft.X; x <= topLeft.X + screenWidthInTiles; x++)
{
if (x < 0 || y < 0 || y >= MapData.Count || x >= MapData[y].Count)
{
// Ignore out of range tiles to avoid exceptions
continue;
}

var tile = MapData[y][x];
var drawPosition = topLeftDrawPosition +
new Vector2((x - topLeft.X)*TileSize,
(y - topLeft.Y)*TileSize);
Tileset[tile].Draw(spriteBatch, drawPosition);
}
}

base.Draw(gameTime, spriteBatch);
}


Broken:

Semi-working:

• Is anti-aliasing enabled with your tile graphics? If so, try disabling it just in case the edges aren't at 100% opacity. In particular your water (blue) tiles are responsible for this hunch (see the dark area gradually disappearing along the bottom, starting from the left, of each of these tiles). – Randolf Richardson Aug 20 '11 at 4:44
• You could look at TiledLib's source (tiledlib.codeplex.com) to see how they've done it, I never had rendering problems with it, and it's quite efficient. – jv42 Aug 20 '11 at 11:02
• Can you show the spritesheet? – bobobobo Aug 20 '11 at 18:59
• @bobobobo: Sure, here. I know that the spritesheet is poorly done because I blew up a smaller one to get the size I wanted. There's a little bleed between the squares which explains the vertical lines being shown on both screenshots. It's the horizontal black lines that I'm interested in with this question. – Matthew Scharley Aug 20 '11 at 23:10
• @bobobobo: On a whim, I double checked. There is a tiny amount of transparency on the 80th pixel. I went back in time to before I did my fix and tried patching up those pixels, but still no luck, so the black lines aren't caused by transparency. – Matthew Scharley Aug 20 '11 at 23:27

One method I've discovered to fix this is to use a two-pass approach; first render the tiles onto a texture, then render the texture in the correct place. This seems to be mildly more expensive, as you'd expect, but does the job perfectly.

public override void LoadContent()
{
MapSprite = new RenderTarget2D(Game.GraphicsDevice, ScreenWidthInTiles * TileSize, ScreenHeightInTiles * TileSize, true, Game.GraphicsDevice.DisplayMode.Format, DepthFormat.Depth24);
MapSpriteBatch = new SpriteBatch(Game.GraphicsDevice);

}

public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
{
var topLeft = Camera.ScreenCoordinateToWorld(Vector2.Zero);
// Readjust the position of the topLeft element so we get the actual drawing position for the tile here
var topLeftTile = new WorldCoordinate(topLeft.X - 1, topLeft.Y - 1, -TileSize / 2, -TileSize / 2);
Vector2 drawPosition;

Game.GraphicsDevice.SetRenderTarget(MapSprite);
Game.GraphicsDevice.Clear(Color.Black);

MapSpriteBatch.Begin();

for (var y = topLeftTile.Y; y <= topLeftTile.Y + ScreenHeightInTiles; y++)
{
for (var x = topLeftTile.X; x <= topLeftTile.X + ScreenWidthInTiles; x++)
{
if (x < 0 || y < 0 || y >= MapData.Count || x >= MapData[y].Count)
{
// Ignore out of range tiles to avoid exceptions
continue;
}

var tile = MapData[y][x];
drawPosition = new Vector2((x - topLeftTile.X)*TileSize,
(y - topLeftTile.Y)*TileSize);
Tileset[tile].Draw(MapSpriteBatch, drawPosition);
}
}

MapSpriteBatch.End();
Game.GraphicsDevice.SetRenderTarget(null);

// Render the result into the right place. First push the sprite up into the top left corner
drawPosition = Camera.Position.DrawPosition - Game1.ScreenCentre -
// The account for the offset of the camera
topLeft.Offset -
// And account for centering issues
new Vector2(TileSize*1.5f);
spriteBatch.Draw(MapSprite, drawPosition, null, Color.White, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.None, 1.0f);

base.Draw(gameTime, spriteBatch);
}

• +1 because you posted a working solution to your problem (instead of just abandoning your question) -- hopefully this will be helpful to others. – Randolf Richardson Aug 21 '11 at 7:08

It looks like the TEXTURE WRAP mode is wrong. You want to set it to CLAMP. See Texture address mode.

• Where do you set this so that I can try it out? You just linked to the enumeration, but not where it is used. – Matthew Scharley Aug 20 '11 at 6:31
• Doesn't seem to be working. I may be setting it incorrectly though. I'm using XNA 4.0, if you can advise how I should be setting this, then that would be good. – Matthew Scharley Aug 20 '11 at 8:12

This problem happens because you are working with Vector2, this is, float values. So when XNA draws a sprite in a non integer coordinate, tends to adjust it by antialiasing the sprite.

To fix this, just make sure you convert your floats to integers before drawing it:

        var drawPosition = topLeftDrawPosition +
new Vector2((int)((x - topLeft.X)*TileSize),
((int) (y - topLeft.Y)*TileSize));


Regards