• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
MrZeo

Drawing large tile maps

5 posts in this topic

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.

0

Share this post


Link to post
Share on other sites

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.

Edited by Azaral
0

Share this post


Link to post
Share on other sites

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

0

Share this post


Link to post
Share on other sites
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).
0

Share this post


Link to post
Share on other sites

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

Edited by Racoonacoon
0

Share this post


Link to post
Share on other sites

Thx for all the replies! I have to be able to interact with the map runtime so I don't think splitting it to textures would work. I was able to make some progress though. I dropped the whole idea of separate tile class and instead have the data in simple byte[x,y,z] array. I'm not really sure what was causing the slowdown before but now I can run steady 60 fps as long as I don't zoom too far out.

 

I probably have to change to SpriteSortMode.BackToFront so that I can utilize layerdepth for sorting. Even though I'm already naturally sorting the map itself, I have to somehow get depth sorted with moving objects that might be behind parts of the map.

 

Here is the draw/culling, not pretty but does the job:

 

        public void Draw(SpriteBatch spriteBatch)
        {
            // Start tile of horizontal row to be drawn.
            int dx = topLeft.X;
            int dy = topLeft.Y;

            // Tile to be drawn.
            int x, y;

            bool swap = true;

            spriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, null, null, null, camera.GetViewMatrix());

            do
            {
                // Update tile to start coordinates of new row.
                x = dx;
                y = dy;

                do
                {
                    // Make sure we are within map limits.
                    if (x >= 0 && x < Map.X && y >= 0 && y < Map.Y)
                    {
                        for (int z = 0; z < Map.Z; z++)
                        {
                            if (data[x, y, z] > 0)
                            {
                                tilePosition = IsoTransform(x, y);
                                GetTileData(data[x, y, z], z, ref sourceRect, ref tileAlpha);
                                spriteBatch.Draw(tileSheet, tilePosition, sourceRect, tileColor * tileAlpha, 0, tileOrigin, 1f, SpriteEffects.None, 0);
                            }
                        }
                    }

                    // Add to draw our next horizontal tile.
                    x++; y--;

                } while (x <= dx + maxX); // Horizontal row done.

                // Add to either dx or dy to get to the next row.
                if (swap)
                {
                    dx += 1;
                    swap = false;
                }
                else
                {
                    dy += 1;
                    swap = true;
                }
            } while (dy <= topLeft.Y + maxY); // We are done drawing once we reach maxY.

            spriteBatch.End();
        }

        private void UpdateLimits()
        {
            // Get view rect coordinates.
            Vector2 topLeft = Vector2.Transform(new Vector2(0, 0), camera.InverseViewMatrix);
            Vector2 bottomRight = Vector2.Transform(screenSize, camera.InverseViewMatrix);

            // Calculate how many tiles fit in to this view.
            maxX = (int)(bottomRight.X - topLeft.X) / Tile.X;
            maxY = (int)(bottomRight.Y - topLeft.Y) / Tile.Y;

            // Get first tile to be drawn.
            this.topLeft = WorldToTile(topLeft, false);

            // Add a bit extra around the view to make sure half tiles get drawn.
            maxX += 3;
            maxY += 3;
            this.topLeft.X -= 2;
        }

 

0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0