Recently it was pointed out to me that there are frame rate issues when using large maps in my RPGenerator project. I am now looking for an efficient way to render only the part of the mesh that would show up on screen. I have tried the following so far:
- Position buffer (but that seems to only apply to the mesh texture and not the mesh rectangles)
- Clip (seems as though it clips off screen by default so it didn’t help)
- Generating a smaller mesh and changing it every time the player moves (this lowered frame rate while standing still but made it very choppy on every move)
What is the best way to go about limiting how much of a mesh should be drawn?
@West, your solution is my #3. It works well for when the player isn’t moving, but recreating the mesh (which is really more like 50 meshes give or take depending on how many tile textures are being used) causes each player movement to lag, so movement is very choppy.
@Ignatz, yep, it is very easy for me to figure out which tiles should be rendered, the problem is that my maps are generated as a whole, so how do I take those generated meshes and pick out the tiles that I need to be rendered?
@tomxp411, it is a 2D tile map. Lego brick style would explain exactly how it is currently configured. It consists of multiple map layers (floor, animated tiles, objects, decoration objects, then events which are invisible).
Here is the process my project currently goes through. I read from 2D tables and create multiple meshes per table depending on the texture that needs to be used. The table of meshes it generates is stored for future use (so it doesn’t have to recreate a map that’s already been generated). The stored maps are the full maps including every tile, so using the standard mesh:draw() function would render everything, including tiles outside of view. Is it possible to edit the mesh:draw() function so I can make it only renders what is in view?
If this isn’t possible (or just not possible in the current version of Codea) that is ok. My own RPG I want to make I actually planned on making most maps as zones so even the world map would be split up into sections. It is even possible to make a zoned off world map seem seamless if the borders include tiles of the next zone and a teleport event is called before you can see the edge of the map. Small maps render quickly and doing something such as this would barely be noticeable loading wise.
@Slashin8r - you either draw a (whole) mesh or you don’t. So if your map is one big mesh, you have no choice. But if your map is made up of many meshes, you can test whether each of them appears on screen, using a function like this, that tests if two rectangles (in your case, the screen and one of the meshes) intersect.
function intersect(a, b)
return (a.left <= b.right and b.left <= a.right and
a.top <= b.bottom and b.top <= a.bottom)
@Ignatz, that’s what I thought. I am actually thinking now to return to using sprites to render the maps. When I first started the project, sprites weren’t as efficient, but now they may be capable of doing the task at hand. So instead of tables of meshes, I would make tables of sprites and then I would have full control over which ones get drawn. Do you think this would be a viable solution? I’m not sure how much more efficient batch sprite rendering is now, but from what I read on release notes, they should be comparable to using meshes.
Edit: Thinking more about it, I think sprites may be the way to go for the tile maps. I will continue using meshes for the characters since they utilize shaders, but I don’t think I have any reason to continue using meshes for the maps since Codea is no longer on v1.5.2, hehe.
Thanks for the quick answers everyone. Hopefully I will be able to let you know how the rewrite works out some time tomorrow (if I can get it done that fast, lol).
If you have a table of meshes, you can choose which items in that table get drawn.
So I’m not sure what difference it would make if you changed to sprites. If you have one big mesh, then of course you have a problem).
It’s both. I use a table of meshes, but the meshes are intertwined. So basically each mesh individually is roughly the same size. For example, if I have 4 desert tiles, one in each corner of the map, and the rest of the tiles are grass, this would only need 2 meshes, but the size of the meshes (coordinate wise) would be the same. Now take that example and make it a set of 50 meshes all mashed on top of one another, lol.
Not sure how you are implementing your mesh. The way I’ve done it in the past is to check to see if the rectangle from the mesh (and I guess triangle though I’ve not tried) is on screen. If not, then don’t add it to the mesh (I think this is your part 1?)
This demo shows shows the the difference in speed between drawing all the mesh elements and just those within a portion of the screen
-- Use this function to perform your initial setup
img=readImage("Cargo Bot:Crate Blue 3")
for i=1,size do
for j=1,size do
-- This function gets called once every frame
-- This sets a dark background color
background(40, 40, 50)
globalx = globalx + math.sin(0.5*ElapsedTime)
for i=1,size do
for j=1,size do
if tile[i][j]==1 then
if switch==1 then
if w*i-globalx>border and w*i-globalx<WIDTH-border and w*j-globaly>border and w*j-globaly<HEIGHT-border then
@Slashin8r - as I recall, you have made things easier for yourself by using a standard tile size and making each one a separate mesh, in a 2D table of tiles. If that’s the case, it should be pretty simple to figure out which tiles are on screen at any time.
Is this a 2D or 3D engine?
Rather than using a single mesh, can you render tiles on the screen using tiles? Games like Neverwinter Nights (the original one, not the MMO) actually use 3D “tiles” to build the scene, only drawing the elements necessary for the part of the world that’s visible to the user.
I’ve been working on a 2D game engine for Windows, and I render the world using a grid of tiles. When you render the scene, you just draw the cells that are actually in your view area.
The advantage of both systems is that you can assemble your world “Lego Brick” style, and it’s easy to put together large, complex worlds that way.
Have you considered making one giant mesh and rendering the whole lot in one go? The GPU does vertex culling which means that it already has the code for deciding which vertices to ignore. Doing it in the main Codea program means that you are, in effect, duplicating this facility but losing all the benefits of the GPU due to parallel processing. The GPU can decide for vertices all in one go, but the CPU needs to consider them one by one.
So if you can rework it so that there are very few meshes, I think that you will see a significant increase in speed.
On my end using one huge grid with one rect per tile for a 150\*100 map works great, and I can effortlessly shift it about at 60fps. A 150\*150 map however drops to around 40fps on my 2nd generation ipad, and 200\*200 drops to around 20fps.
@Andrew_Stacey, I could make it use one mesh per tilesheet set which would maybe cut it down from 50 meshes to 10. I’d then have to rework some of the math, but it is definitely possible. The main problem of getting it down to one large mesh is that I need to control the drawing order (layers) and the image size limitation of Codea prevents me from cramming all my tilesheet sets into one image.
@HeadCase, if I were to make a very large map of just one texture, I don’t see any of the fps issues.
So I definitely have some ideas on how to refine the meshes now. I think I will go both routes (meshes and sprites) and keep the functionality of both in my project just in case something changes in a future version of Codea.
Just finished the code to convert my meshes to sprites and all I have to say is:
DON’T USE SPRITES! lol
Using my original map which ran 60 fps on average was now running below 10 fps. Tried the large map which normally ran at 30 fps on average and it was running at about 3 fps.
Good thing I made new functions for this test. I went back to meshes and even limited new mesh creation to full tilesheet sets. Now only 11 meshes are being created instead of 50. Most of my tilesheet sets are 512x512 or smaller so I can go even further and cram them together into larger images. I would need at least 4 separate tilesheet sets (floor/objects/animation, walls, roofs, and decor), but 4 meshes are better than 11 and much better than 50.
After making the above change, the large map moved from 30 fps to about 40 fps on average.
Just a thought, what about the
clip function? but a frame rate reduction, if any, will probably depend on how it handles drawing outside of the clipping rectangle
@XanDDemoX, tried that already (see #2 in first post). I think Codea clips beyond the screen by default.
@Slashin8r: Are you recreating your mesh each time you need the background to change? If so, perhaps you could try modifying your approach to #3; instead of recreating your mesh each time, just rearrange it a little. What if you took the rects in the mesh that have scrolled off-screen, and reuse those (by repositioning them and re-setting the texture coordinates) for the upcoming portion of the screen? Your mesh would need to have 1 extra row and 1 extra column of tiles than what’s visible on screen. It’s possible this might work, but I haven’t yet got around to playing with tile maps, so I can’t say for sure.
@toadkick, currently, the whole map is created once and then translated into position as the player moves. I think I can imagine how your suggestion can be accomplished. Would making a reference table with the mesh indexes (each tile technically has 4 textures for the autotile process) stored in it be the best way to go about it? Then I could call vertex and texCoord functions to get the needed information of each index.
@Slashin8r: Sorry, I’ve been pondering on this a little more and the solution I proposed is tricky; I need to think over it a bit more. I’ll actually be attempting an experiment with this in a couple of weeks, so I’ll keep you updated as to how it goes.
If you are only building the map once, and are seeing framerate issues, then you are probably GPU limited (from the sheer amount of work the GPU is doing), and not CPU limited (which you might be if you were updating mesh verts each frame). I thought in your case it might be the latter, due to re-creating the mesh frequently, but since you’re only building the map once that’s probably not the case. At any rate, a solution like the one I proposed could potentially help on both fronts, since a) you are only submitting what’s actually visible at the time to the renderer (which should reduce strain on the GPU), and b) you are only updating the mesh when it actually needs to be updated, and even then, you only need to update portions of the mesh (which should reduce CPU usage).
But basically, yeah…in order to determine which rects of the mesh have scrolled off screen, you’d need some way to index them, so that you can move them and assign them different texture coordinates. You’d probably need a table somewhere that maps the mesh rect indices to their location in the map grid, so that when a row/column of the map grid scrolls offscreen, you can round up the mesh rect indices in the row/column, and set their new x/y positions and tex coordinates.
I actually got this idea from old Nintendo 2D consoles; this is pretty much how it worked, but in hardware. Sorry again for not having brought more ammo to back this idea up at the moment. I’ll mull over it a little bit and see if I can’t flesh out some more details as to how it might be implemented.
EDIT: I just realized that I think I’m saying the same thing as @tomxp411.
@Slashin8r, how many layers are you drawing, and how big are your tiles? I’m just thinking about doing some performance tests…
@toadkick, my whole project is based off the old Nintendo 2D classic RPGs, so if it is how Nintendo accomplished it then I’m sure it would fit in perfectly for my project. I’ll wait and see what you come up with. Feel free to download and modify the RPGenerator project for testing. I tried to keep most of it commented and user-friendly. I should have my current project out as the latest update by this weekend.
I have also merged most of my tilesheets (still got decor objects to do) so I have the project down to using only 5 meshes per map. This gave me a boost of about 4 fps on the large map. Seems like I get almost 1 fps per mesh removed. The large map is getting closer and closer to running at my goal of 50 fps on average.