2
votes

I am trying to create a simple 2d scrolling game as a learning exercise in monogame (XNA). Currently I load tile information from a text file, creating an array (tiles[,]) with a tile object storing information about where the tile is written and what texture should be used when the draw method is called. Currently I draw every tile in the array at once and then simple update the position of the tile to move it across the screen. Obviously drwaing all tiles at once is a bit of a pain, and I think it would be better if I only drew the tiles visible on the screen, so as the tiles move across the screen more tiles are drawn. I am not sure how best to do this? I am a bit lost as to how I can tell what tiles should be drawn. Any help would be greatly appreciated. Here is my draw and update methods in my level class - it simply calls methods in the Tile class to draw/update position.

    /// <summary>
    /// Draws each individual tile in the level.
    /// </summary>
    private void DrawTiles(SpriteBatch spriteBatch)
    {
        // For each tile position
        for (int y = 0; y < Height; ++y)
        {
            for (int x = 0; x < Width; ++x)
            {
               tiles[x, y].Draw(spriteBatch);
            }
        }
    }



    public void Update(GameTime gameTime, int scrollSpeed, ScrollDirection direction)
    {
        // Update the positions of each of the Background sprites
        foreach (Tile tile in tiles)
        {
            tile.Update(gameTime, new Vector2(0, scrollSpeed), scrollDirection);
        }
    }
}

Thanks in advance

I am now trying to only loop through the tiles visible on the screen to draw, using a viewport passed from the graphics device. I basically try to get the number of tiles that can fit on the screen before trying to draw them, by dividing the height of the screen by the height of a tile and the same for width. This works just fine, and it draws all tiles visible on in the viewpoint.

The only problem is it no longer draws any of the tiles which began outside of the viewport, so even though in the update tiles are moved into the viewport they are not drawn as the loop uses their original location. I cant think of a way of fixing this without looping through all tiles though :-(

As a level can be big it would be very inefficient to loop through all tiles to only draw what was in the screen.

Any advice would be greatly appreciated.

here is the updated draw:

    private void DrawTiles(SpriteBatch spriteBatch)
    {
        float tileWidth = 40;
        float tileHeight = 32;


        for (int y = 0; y < (int)Math.Ceiling(mViewport.Height / tileHeight); ++y)
        {

           for (int x = 0; x < (int)Math.Ceiling(mViewport.Width / tileWidth); ++x)
            {
                tiles[x, y].Draw(spriteBatch);
            }
        }
    }
2
Simple 2D Camera example that I just created: pastebin.com/jrGXWfNh It only demonstrates what the camera does and how it works with a drawn rectangle as reference. It's quite simple to work with, quite advanced to fully understand. If you then know you screen has limits, you just check where the camera is and check if another rectangle is "inside" the screen then just draw or not draw.Deukalion
Thanks Deukalion, I think I get it.Pectus Excavatum

2 Answers

1
votes

You need information about your camera. If you know what your camera is center on and what your screen size is, you could have a draw call like this:

private void DrawTiles(SpriteBatch spriteBatch)
{
    // For each tile position
    for (int y = cameraOffset.Y - screenHeight / 2; y < cameraOffset.Y + (int)Math.Ceiling(screenHeight / 2); ++y)
    {
        for (int x = cameraOffset.X - screenWidth / 2; x < cameraOffset.X + (int)Math.Ceiling(screenWidth / 2); ++x)
        {
           tiles[x, y].Draw(spriteBatch);
        }
    }
}

The reason I'm using Math.Ceiling is to cover if tiles are partially on screen. The Math.Floor on the other side is implied, since it's integer division.

Note that this assumes cameraOffset, screenWidth, and screenHeight are in units of tiles. If they aren't, convert them appropriately.

0
votes

Make constants called WINDOW_WIDTH and WINDOW_HEIGHT that store the window's width and height. Then, draw the tiles like you showed, but only with the ones within the WINDOW_WIDTH and WINDOW_HEIGHT values. You also need X and Y properties in your Tile class

private void DrawTiles(SpriteBatch spriteBatch)
{
    float tileWidth = 40;
    float tileHeight = 32;


    for (int y = 0; y < (int)Math.Ceiling(mViewport.Height / tileHeight); ++y)
    {

       for (int x = 0; x < (int)Math.Ceiling(mViewport.Width / tileWidth); ++x)
        {
            if (tiles[x, y].X <= WINDOW_WIDTH &&
                tiles[x, y].X >= 0 &&
                tiles[x, y].Y <= WINDOW_HEIGHT &&
                tiles[x, y].Y >= 0)
            {
                tiles[x, y].Draw(spriteBatch);
            }
        }
    }
}