5
\$\begingroup\$

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

\$\endgroup\$
14
  • \$\begingroup\$ 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). \$\endgroup\$ Commented Aug 20, 2011 at 4:44
  • 2
    \$\begingroup\$ 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. \$\endgroup\$
    – jv42
    Commented Aug 20, 2011 at 11:02
  • 1
    \$\begingroup\$ Can you show the spritesheet? \$\endgroup\$
    – bobobobo
    Commented Aug 20, 2011 at 18:59
  • 1
    \$\begingroup\$ @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. \$\endgroup\$ Commented Aug 20, 2011 at 23:10
  • 1
    \$\begingroup\$ @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. \$\endgroup\$ Commented Aug 20, 2011 at 23:27

3 Answers 3

8
\$\begingroup\$

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);
}
\$\endgroup\$
1
  • 1
    \$\begingroup\$ +1 because you posted a working solution to your problem (instead of just abandoning your question) -- hopefully this will be helpful to others. \$\endgroup\$ Commented Aug 21, 2011 at 7:08
2
\$\begingroup\$

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

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Where do you set this so that I can try it out? You just linked to the enumeration, but not where it is used. \$\endgroup\$ Commented Aug 20, 2011 at 6:31
  • 1
    \$\begingroup\$ 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. \$\endgroup\$ Commented Aug 20, 2011 at 8:12
0
\$\begingroup\$

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

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .