1
\$\begingroup\$

I have set up a tilemap system, and am rendering it in a way that lets me pan through the map using a camera offset. However, I have discovered it is incredibly inefficient, but am unsure of how I can increase efficiency.

Here is my current rendering code:

float lastCamOffSetX = -1, lastCamOffSetY = -1;

public override void Draw(GameTime gameTime)
{       
    GraphicsDevice.Clear(Color.Black);
    spriteBatch.Begin();

    int count = 0;

    int startCol = (int)camOffsetX/world.TileWidth;
    int startRow = (int)camOffsetY/world.TileHeight;
    int endCol = (int)(startCol + viewPortWidth - (camOffsetX/world.TileWidth));
    int endRow = (int)(startRow + viewPortHeight - (camOffsetY/world.TileHeight));

    if (startCol < 0) startCol = 0;
    if (startRow < 0) startRow = 0;

    if (endCol > world.Width) endCol = world.Width;
    if (endRow > world.Height) endRow = world.Height;

    for (int x = startCol; x < endCol; x++)
    {
        for (int y = startRow; y < endRow; y++)
        {
            int screenPosX = (int)((x * world.TileWidth) - (camOffsetX));
            int screenPosY = (int)((y * world.TileHeight) - (camOffsetY));

            Tile tile = world.Data[x, y];

            if (screenPosX >= -1 * world.TileWidth 
                && screenPosX <= (viewPortWidth + 1) * world.TileWidth
                && screenPosY >= -1 * world.TileHeight 
                && screenPosY <= (viewPortHeight + 1) * world.TileHeight)
            {
                Color color 
                    = (tile.TileType == TileType.Land ? Color.Green : Color.Blue);

                DrawRectangle(spriteBatch, new Rectangle(screenPosX, 
                    screenPosY, world.TileWidth, world.TileHeight), color);

                count++;
            } 
        }
    }

    //Debug.WriteLine(count + " - " + (endCol-startCol)*(endRow-startRow));

    spriteBatch.End();

    lastCamOffSetX = camOffsetX;
    lastCamOffSetY = camOffsetY;
}

private void DrawRectangle(SpriteBatch spriteBatch, Rectangle coords, Color color)
{
    var rect = new Texture2D(this.GraphicsDevice, 1, 1);
    rect.SetData(new[] { color });
    spriteBatch.Draw(rect, coords, color);
}

Currently, I am only drawing tiles which are inside the screen/camera region. How can I improve the efficiency of my script?

\$\endgroup\$
  • \$\begingroup\$ Looks like you are using XNA/MonoGame, maybe add the tag too. \$\endgroup\$ – lozzajp Jan 19 '17 at 8:31
  • \$\begingroup\$ Also what exact issue are you having? Is it slowing the game down, are you targetting 60fps and not getting anywhere near that? \$\endgroup\$ – lozzajp Jan 19 '17 at 8:33
1
\$\begingroup\$

your DrawRectangle is the bottleneck. Creating a texture is a very very expensive operation. Create the texture first and than use the reference from it to draw it.

\$\endgroup\$
1
\$\begingroup\$

You are creating a new texture every frame (and even worse, inside Draw() instead of Update()).

private void DrawRectangle(SpriteBatch spriteBatch, Rectangle coords, Color color)
{
    var rect = new Texture2D(this.GraphicsDevice, 1, 1);
    rect.SetData(new[] { color });
    spriteBatch.Draw(rect, coords, color);
}

You could create a more permanent variable to hold the texture and then reuse the texture over and over. Since you are using a color there are two ways about this.

Use a dictionary of sorts and cache a color when it comes across it and just create a new texture if the color does not already exist. This may be a bit overkill in this situation.

A better way would be to create the texture once and use SpriteBatch to tint it, you need to set your textures color to white, this can be done such as:

Texture2D rect = ... // do your new texture here. 1x1 and colored Color.White. 
// Create this ONCE! not inside Update or Draw loops.

public override void Draw(GameTime gameTime)
{
    // Start of your drawing code

    Color color = GetColorLogicMethod(); // or your inline stuff above.
    spriteBatch.Draw(rect, coords, color); // This will tint the White 1x1 with the new color.

    // Rest of your drawing code
}

This way the expensive new Texture2D() and Texture2D.SetData() are only done once. The rest of your code looks like it should be pretty good as it is now for what you are doing.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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