Skip to main content
GameDev.net gamedev.net
🔒 Locked

Drawing large tile maps

Started by GameDev.net Apr 13, 2013 at 4:15 PM 4 replies 6.9k views
Original Post
GameDev.net
GameDev.net

Hello everyone!

I've been recently working on 2d isometric tile engine using XNA as school project. I've set up the engine to limit drawing only to what is inside the camera view. Problem is as soon as I start zooming out and therefore increasing the amount of tiles to be drawn the framerate starts to drop really fast. Around 100x100x3 (3 is the amount of layers for grass, roads etc) draw calls is enough to go from 60 to 50 FPS.

So how do other tile based games handle drawing large areas? For example I remember in OpenTTD you could zoom pretty far out and have huge maps (something like 2048x2048) yet it was really smooth. Thank you in advance for any solution/different approach you might have.

Also I'm new here, so hi again! I hope this is the right section for this topic.

Azaral
Azaral

You compile your tiles into larger bits. Take each 512x512, or whatever, pixel section of tiles and compile them all into their own texture. This will DRASTICALLY cut down on the amount of draw calls you have to make. It will make loading longer though, but not that much really.

If your tiles are 16 x 16 pixels, this means for each 512 x 512 pixels you cut down your calls down 1023 for that section.

Bearhugger
Bearhugger

If you are using XNA, the sprite batching class should optimize 2D drawing automatically. If you are using it, do you have all your tiles on the same texture or on a different texture? If you have them on different texture, it's impossible to batch the draw calls because the video card is constantly swapping textures.

The problem is that draw calls have some overhead that happens whether you are drawing 1 triangle or 10,000. The video card works asynchroneously so this is not a problem for the first draw call, but if the next one happens while the video card is still busy, the CPU has to wait until the video card is idle and this is the problem. This is why issuing 30,000 (100 x 100 x 3) draw calls in quick succession every frame is going to seriously drain your performance.

The trick to drawing multiple tiles without killing your framerate is to batch your draw requests into a single draw call. Any tiles using the same tileset and the same render states can be batched together. So basically, put all your tiles into one tileset, and if you need tiles with different blend states (additive and subtractive blending is popular in 2D game) put them on their own layer so they're all grouped together.

Basically, when you call your Draw() method, don't issue a draw call immediately. Instead, put the call into a buffer and only do the draw call when you have to. This is usually when you have to draw something with another texture or render state or when you're about to flip buffer, but you can also have a Flush() method to force it if you want to optimize. It adds some logistic because you have to make sure that no texture is deleted while it is still waiting to be drawn, but this was not a big deal for me.

For my 2D RPG this made the difference between 51 FPS and 1,300 FPS. smile.png

Servant of the Lord
Servant of the Lord
Welcome to GameDev.net!

What I'd do is break your 100x100 map into 10x10 or 20x20 chunks. One 'chunk' should be about the size of the width of the largest screen resolution you support, roughly. Then load and draw just the chunks around the player (9 chunks (3x3), 60x60 tiles if 20x20 tiles per chunk). Your maps can then be of any physical size you want, just streaming in, and unstreaming, the map chunks as you need them.

For example I remember in OpenTTD you could zoom pretty far out and have huge maps (something like 2048x2048) yet it was really smooth.

If you need zooming out, then when more than 3x3 chunks are visible on your screen, dynamically pre-render each chunk on a smaller image and draw that each frame. 50% zoom? Your chunk is (50% * ChunkSizeInTiles * TileSize).
Shawn Myers
Shawn Myers

Hey there MrZeo!

I'm curious how you are drawing your tiles. Are you doing something like this:

for each tile

{

spritebatch.begin();

spritebatch.draw(tile)

spritebatch.end()

}

If so, that is likely the reason of your performance issues. You will want to do something like this:

spritebatch.begin()

for each tile

{

spritebatch.draw()

}

spritebatch.end()

The first method interrupts the graphics card with draw requests equal to the number of tiles you wish to render (10,000 tiles equals 10,000 draw calls), while the second method will interrupt the graphics card once (10,000 tiles equals 1 draw call.)

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.