I'm developing a 2D sidescrolling game and I need to optimize my tiling code to get a better frame rate. As of right now I'm using a texture atlas and 16x16 tiles for 480x320 screen resolution. The level scrolls in both directions, and is significantly larger than 1 screen (thousands of pixels). I use glTranslate for the actual scrolling.
So far I've tried:
Drawing the entire map as a Display List (great on a small level, way to slow on a large one)
Partitioning the map into Display Lists half the size of the screen, then culling display lists (still slows down for 2-directional scrolling, overdraw is not efficient)
Any advice is appreciated, but in particular I'm wondering:
Thanks :)
Here's how I would do for coding a fast 2D tile engine:
First I would make a clean separation between dynamic tiles (characters, items..) and static ones (level).
For drawing the static ones (tiles that build up the whole level), I would use a static buffer (stored in a buffer object) that contains each tile position (x, y, layer) and an index to the atlas texture data (i). Since your texture atlas contains fixed-size tiles of 16x16 pixels, you could easily compute in the vertex shader texture coordinates for each vertices.
For drawing the level I would use a single draw call (using instancing) of a triangle strip forming a quad, vertex data is stored in a static VBO (made of 4 vertices) and index data in a static IBO (made of 4 indices), using per instance values for computing vertices attribute in the vertex shader.
This would give you almost "free" tile culling done on the GPU, since clipping hardware is very fast.
Even if you have big number of tiles in your level, let's say 30*20 (tiles/in a screen), and about ~50 screen/level, it would make 30,000 tiles. I think it's still acceptable (even on low-end GPUs. BTW, are you targetting iPhone/Android? If yes instancing/shaders are not available on OpenGL ES 1.0 and OpenGL ES 2.0 has no instancing support but can do shaders, so you will have to explode tiles instance data in a VBO/ IBO, and use GL_TRIANGLES
. You can explode less data and spare GPU memory, computing vertices attributes in a shader).
In any case you'd better not to duplicate texture tiles data and keep a texture atlas and a VBO and IBO.
I would use a dynamic VBO (and an static IBO representing GL_TRIANGLES
so 0,1,2, 2,1,3, 0+4,1+4,2+4..,) representing tile positions, texture coords, layers and update it with visible dynamic tiles in a screen via glBufferSubData
and draw thoses tiles via glDrawElements
.
Of course this means that you have a maximum number of dynamic tiles that you can draw per glDrawElements
, so if you bump into this limit you'll have to do a second update/draw of the VBO.
If your OpenGL implementation has no support for VBO/IBO (like in OpenGL ES 1.0) use VA instead. I don't recommand usage of DL or immediate mode (no support for it on OpenGL ES).
Finally, use glOrtho
for moving your camera across the level, zoom in/ zoom out etc. Good luck!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With