Dungeons Drawcalls - How do I keep them consistant?

Hi there. I need some help with one of my favourite bugbears - draw calls. Now I know full well from previous Unity projects that Draw calls are the things that make or break applications - especially when developing for mobile platforms. I’m currently tinkering away with a tilemap engine, mainly to get up to speed with Unity programming, but with an eye for moving it onto the iPhone.

Hopefully I can turn it into a Roguelike or Dungeon Keeper remake or finally develop that RPG I was meant to do in Flixel last year. Currently it takes a prefab made out of a ceiling cube, a floor cube and a solid block cube, builds a two-dimensional array out of them, and constructs a level. Wall tiles that border a floor are solid blocks, whereas the remainder are ceiling cubes (the intention is to replace them with two-poly planes to reduce the polycount, but this is a rough prototype, so proof of concept is more key). Nice features so far are the shuffling of all the vertices for the cubes to give it an uneven, rough surface feel (which, I’ve noticed, doesn’t add anything to the draw calls… bonus!)

But my engine is falling apart in two areas.

  1. The draw calls spike arbitrarily. I tend to get between 5 to 6 draw calls normally, but around the corners this tends to spike significantly - sometimes to about as high as thirty. It starts to spike when changing walls into floors as well, but moving the camera around a bit tends to fix this. I have no idea what is causing this, and this worries me - especially if I’m meant to be limited to about 30 at the most for the iPhone.

  2. Larger levels take more time to build. This I anticipated, and when constructing the level have it made in stages - one row at a time, every Update call. However, each row takes progressively longer to create during this process - forty rows are barely noticeable, but after that each successive row takes longer, and longer, and longer to make. While not horrifically bad, it does stop me planning anything of a Dungeon Keeper vein, where I anticipate needing maps at about 150 x 150, plus borders.

I’ve attached my current project to the post. It should run out of the box, with the exception of a new axis “Rotate Camera” needing defining in the Input settings. WASD to move camera, click tiles to change thier states. If people could have a look into my code and see what I’m doing wrong (okay, wrong in relation to THESE problems anyway :p) I’d be grateful.

458527–16040–$DungeonGeneratorPackage.unitypackage (167 KB)

Unfortunately I can’t operate Unity on this laptop, but it sounds like you’re creating the entire floor as a single mesh. As you add rows, the calculations for collision will become more and more expensive. You might be able to get around that by creating each, say, ten rows as their own mesh, rather than one enormous mesh - though each mesh will be an additional draw call, I think.

You could ‘cheat’ and create the floor visibly, with flashy effects as it pops into existence like a custom loading bar while the player watches it appear. You might be able to conceal the stuttering that way.

You could also roll a custom “physics” implementation that uses a grid to define where you can and can’t go, rather than an expensive mesh collider. That way you’ll not have to worry about updating the collider(s), which is the really expensive part.

Each prefab has it’s own floor, ceiling and wall. So the chances of them all collision calculations shouldn’t be an issue… at least I don’t believe they are, seeing as AFTER it constructs the level it runs very smoothly.

And no, even at the 40,000 cube mark, we still get between 5 and 30 draw calls.

That was going to be the intention anyway, but the mesh collider has been left in for now purely for mouse clicking. As I said, don’t think it’s these since after the initialisation of all the objects the level runs at the 200fps mark. I’m currently away from my unity computer as well and can’t double check if the collider is to blame at Initialising stage, but I see no reason why it would be an issue if it’s not after initialisation.

I appreciate the effort to help though.

Ah, I see now.

I haven’t had problems instantiating large numbers of objects as long as I do it in clumps at a time, but I have a pretty brand new computer for Unity. It could be your dev computer running low on memory and having issues finding new chunks. If your prefabs are made of several objects, you might try combining the meshes into just one and see if that helps.

Do the prefabs have a script (or is there a script somewhere) that iterates over all floor objects whenever some are added, rather than only checking the new ones?

New Years. Back at the office now…

I doubt it’s the computer running low on memory… even on the screenshot posted above the VRAM usage is up to 16mb. And if it was, that doesn’t bode well for anything to do with the iPhone.

I wouldn’t be able to combine the prefabs down any further from the three cubes they already are, seeing as that forms the ceiling-wall-floor layout described in the first post.

And no, there’s nothing in the script for the blocks that relies on anything else - Start and Update are blank, Awake activates the ceiling block (and deactivates the other two). The only other communication it has is a SendMessageUpwards call.

Still no progress on this. Was hoping people would at least be pleased to see a free tile-based grid prototype for download… and I’m still hopeful that someone can help out.

I’ll help testing this out as well. Let you know thoughts soon.

If you could that would be great. I’ve not managed to improve upon it yet.

hmm

why dont you generate the rooms and then combine the meshes into a single mesh per room?,
if you use a texture atlas they should all share materials and therefore drawcalls.
I know unity is batching its own calls, but if you manage them yourself you might findyou have more control.
Ive done alot of procedurally generating terrain and so on, the slowest part of the process is always where unity recalculates the mesh collider.
If you take out that step, just for a test i imagine things will go much faster.
For optimum mesh construction you should precalculated your dungeon layout in an internal array, iterate through once to work out how many polys it will need and then build an empty vertex array(index,uvs etc) of that size. Then run through and poplate this with the correct data , and finally assign it to a unity mesh object.
If a whole dungeon is going to be too many vertices for a single call, or you want to include some clever LOD then just do the same process with a series of meshes instead (maybe 1 per room).

Also remember unity does frustrum culing (ie wont draw when objects are out of the vieing angles) but im pretty sure it wond do occlusion culling (ie the object is still within the view but its obscured).

Fundamentally, I’m planning a dungeon keeper game. Every cell needs to be destructable and changeable at play time, as the user orders minions to dig and expand.

So generating them isn’t going to help, seeing as the user is going to be changing them into something of thier own. Even if I wasn’t going the DK route and going just down general Roguelike route, I’d still need to procedurally generate the rooms and allow the user to tunnel, collapse and generally alter the landscape as they saw fit.

Normally, yes, I would generate rooms in advance and use appropriate meshes in place of tiles, but when the environment is contrallable by the user then that solution is off the table… I shouldn’t have control. The USER gets that mantle…

Edits:

Also. Even without a MeshCollider attached, the generation of the level still slows down for each row created - it starts off capable of three or five rows at a time, and then starts to slow down to about a row every second… It’s definately not the collider, and every mesh at the moment is already the same object with the texture atlas method, batching the draw calls THAT way instead…

It’s not the number of drawcalls that worries me, more than the actual SPIKING it goes through every so often for no clearly defined reason. A new project of the current edition (not worked on it for weeks, but theres a few tweeks) is now attached…

477944–16785–$DungeonGeneratorPackage.unitypackage (163 KB)

Ok, sounds interesting, The approach I have used in that case is to still split the world into chunks and then have each chunk capable of being redefined.

so say you have a 100x100 grid
you could have 10x10 meshes, each with a resolution of 10x10 tile units (with the appropriate space for floor, celing polys etc)

This is the same way minecraft works. When you need to update an area due to the user dropping in a room or something, only the effected chunks are updated and regenerated, without having to rebuild the whole map. (if you have everything on a strict grid, this should be reasonably easy to implement) Whenever an update like this occurs it may introduce a small degree of slowdown, but most users accept this as part of the notion of dropping in a big room or some such.

Have you tried using the combine meshes function? That might work. I had a terrain generator that peppere the world with trees, while they were all unique objects it killed the framerate, if i combineed them after they had been randomly placed it really sped up.

I guess it comes down to your management model of the meshspace, you want to update only what is being changed and combine as much of everythign else into nice drawcalls.

I’m only going to suggest downloading the package and seeing what’s going wrong. Keeping a 100x100 grid in memory is no problem. But for some reason generating it is.

A very long time ago I was working on a dungeon crawler game where I used a screen (a very long time ago) and drew 20 boxes, then did 10 calls where I linked room 1-2, 2-3 and so on. I then drew a horizontal and vertical line to match some random point within the room creating a corridor to the linked room. (thus assuring myself that all the rooms were connected) The player was to walk from a random room to another random room, looking for a stairwell to the next level. All the while fighting random pixelated monsters wtih his pixelated sword (did I mention it was a LONG time ago?)

Fast forward to Unity, you could use a 256x256 bitmap to represent my old screen, create 50 rooms and do something very similar. Next, you could use tile sets and standard rooms with specific doorway locations. If a room has an extra doorway, put a brick wall up in it… no one will know the difference.

You would have to write a basic texture manipulation script, a hall-pathfinding script so the halls dont run over the existing rooms, but for the most part it is very simple stuff.

Not sure that helps me any since
a) I already know enough about generating random dungeons from my constant reading of the Roguebasin
b) I’m not generating a random dungeon anyway. The user is building thier own…
c) I already have something generating a 100 x 100 dungeon space… it’s just taking forever to Instantiate them
d) The user is going to be able to see the entire map (or a section that the camera is zoomed into) at once, without fog of war…

I know it was a while ago, but Dungeon Keeper was pretty popular, as was Evil Genius that eventually followed in it’s footprints. Both managed to be games set out where the user could click on a brick wall and have it (eventually, after minions did things to it) explode and become a new room. As an idea about what it is I’m aiming for, download the package, run it, and watch this youtube video. You see that when it gets going, I almost have this already. But I can’t ask the user to wait thirty minutes because each row got exponentially slower to make, and would really want to know how to fix the flaky drawcalls.

THAT’S what I need help with.

hey Kayn,

I did download your package, and my comments still stand.
For every prefab you instantiate unity has to make a new game obejct with a transform, add various components,
including a script which then calls some of its functions to actually make the mesh and sassign it and the collider to tat object etc
In fact since each block has 3 subobjects, it has to add 3x this amount.
Then unity has to add these to the scene and place them under your parent transform.

All of this is going to take time, hence the speed issue.
All i can advise you to do is either strip down your code to reduce the number of objects,scripts etc run at startup.(see what is really causing the slowdown, ie take out the collider for a sec etc) Or do as i suggested and make less meshes. As i said before when unity adds a collider to a mesh it needs takes some time. If you are doing that for all your meshes it will get slow. if you combine meshes and only make 1 collider, it should speed things up.

But there isn’t a collider on any of the three meshes. There is only a collider on the gameobject containing them, and even when that’s removed, it gives entirely the same behaviour. Even when I remove THAT one, the exact same behaviour occurs in Mode 1 - it processes the first few rows fast enough, and then drops to below 4fps. Seriously. Remove it from the Dungeon Cube prefab - entirely the same behaviour on a scene with 0 Mesh Collider objects.

So it’s not the collider. And it’s certainly not the memory of having 10,000+ objects on screen at once because after this instantiation it is as smooth as silk, thanks to culling.

ok ive had another look, and Ive another question.

Why are you processing the blocks in the update function?
Instead of generating the world in a Initialising function you seem to be making 5 construction passes through iterations of the update function.
This means no matter what you do it will take 220 update function calls before your creation is done.
Maybe try doing the creation in a seperate function, it might be that unity has to add newly created objects to its batch list before every render, and since you arent adding them all at once, but rather in 220 consequtive frames its slowing it down?

To be honest pon my machine it doesnt really drop too low in fps at any stage in the construction anyway.(but I understand my machine is probably not th target :slight_smile:

Originally I did have the entire thing generate on Start, at least when I was dealing with smaller numbers.

But I am aware that telling a game engine to instantiate ten-thousand prefabs in a single for loop is a sure-fire way to find myself staring at the Crash Report window. So moving it to Update was a way of controlling the bursts into five seperate phases - Instantiation, boundary setting, vertex shifting (to give the uneven feel) and applying the vertex shifting to the meshes. Doing this a row at a time, on Update so as to only do it when Unity finds the time to, was meant to waylay this.

The odd thing is, I think you’re right. It’s managed to generate a 100x100 (plus borders) level in a much faster time after moving it all into a single, massive Length x Width x Creation Level treble for-loop. The only problem now is it feels like the entire engine hangs as a result. Guess this is something I need to fix with a co-routine?

Meh, id just leave it like that and stick a loading bar graphic in or something :slight_smile:
I think there are a fair few games that hang to do background stuff as long as its not a silly length of time.
Glad ive helped somehow though :slight_smile:
Interesting project btw good luck with it!

Thanks Tom for your help, and thanks to Vincetti, Jmarquiso, BigMisterB for thier interest.
Here’s the current project file for anyone interested in this kind of thing - currently generates ten-thousand or so cubes in a decent amount of time, with an average draw call count of 3 or 4 (using about 70mb of RAM). No idea how it handles on devices (guess you’ll need smaller levels… heh), but should handle okay for PC and Mac games.

478834–16823–$DungeonGeneratorPackage.unitypackage (163 KB)