Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

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:

Screenshot of broken rendering

Semi-working:

enter image description here

share|improve this question
    
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
2  
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
1  
Can you show the spritesheet? –  bobobobo Aug 20 '11 at 18:59
1  
@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
1  
@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
show 9 more comments

3 Answers

up vote 7 down vote accepted

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);

    base.LoadContent();
}

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);
}
share|improve this answer
1  
+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
add comment

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

share|improve this answer
1  
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
1  
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
add comment

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

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.