Search Unity

Creating an efficient layered tilemap system...

Discussion in 'Editor & General Support' started by ecutruin, Feb 12, 2016.

  1. ecutruin

    ecutruin

    Joined:
    Aug 19, 2014
    Posts:
    39
    I initially posted this over in the Shader section as I felt they may be the best solution. However, after some discussion there, it seems they are not likely so. This leads me to inquire as to how best to implement a terrain system akin to the game Don't Starve.

    The following link has some images explaining how turf works in the game. Essentially its a terrain where edging between terrain is drawn based on a layering order. An individual location still only has one tile type, but it draws varied edging based on rules.

    I've tried to replicate something similar by just using a 3D map and having each be a layer. However, I am using disconnected vertices to achieve my current design and as such find myself with lines between tiles. With connected verts, I believe I would not be able to rotate UVs (to re-use tile edges/corners). I'm also kind of worried about the layer distance between layers if I have too many layers.

    So I ask you all, how best would you recommend implementing such a terrain style?

    Edit: I am also worried about animating tiles or applying overlays/shaders to an individual layer (like they do in Don't Starve, for say the stone turf).
     
    Last edited: Feb 12, 2016
  2. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    9,052
    If I understand what you are asking, I would suggest generating your own mesh, and uv'ing according your map layout logic. I did something similar a while ago to do some perf testing.

    you can see in this on that the paths (and fills) have several variations to make it look more organic. It can be a lot more complex, but I just drew some simple paths for testing. In the scene view you can see a little better what is going on:
    Screen Shot 2016-02-11 at 6.05.30 PM.png
    And the source texture looks like this:
    Screen Shot 2016-02-11 at 6.11.02 PM.png
    For a testing version this shows a little more what is going on:
    Screen Shot 2016-02-11 at 6.06.19 PM.png Screen Shot 2016-02-11 at 6.11.06 PM.png

    Here is pretty much the same with a different skin:


    The goal behind this was to build a flexible mapping system that was highly performant. Each texture as a definition, so the actual ground can get very detailed, (mimicking splat maps to a degree). But because it is constructed via the uvs in the mesh, it is pretty much 1 draw call, and the mesh is welded so there are no lines between tiles.

    As an example of performance, this video shows 1.3 million tiles, and still solid performance:


    So, that is how I would handle it, generating the mesh and building the uv map. It's performant and inexpensive for resources. And with a little creativity, you can pull off just about anything.

    Also, I am sure what you mean when you say layers. If you mean scene layers, I would avoid that, there is a limit, and it adds needed complexity.
     
  3. ecutruin

    ecutruin

    Joined:
    Aug 19, 2014
    Posts:
    39
    I see, so to remove the lines between the tiles, I absolutely need to make sure the quads share vertices? When doing that...do I still use 4 UVs per tile (sorry, my understanding of 3D development is far from perfect)? Right now, I'm not sharing vertices and using 4 UVs per tile, stored in a list and shifted based on the tile's rotation value, which is then added to the master UV list and eventually passed to the mesh as the UV array.

    How can I combine multiple layers like you do? From my understanding, I cannot layer multiple UV sets per tile... am I wrong?

    Edit: I'm also extra curious how you generated such a large area in the final map? Since, due to vertex limits, you would not be able to have that many tiles in a single mesh, and multiple mesh would lead to the line bleeding issue again...
     
  4. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    9,052
    I use 2 uvs per quad (for the two tris). and yea, the are shared. Though, i have also found that if they are not shared, it should show up without lines as long as all the mat/tex settings are correct.

    The exception is that on the dirt one, I made the path transparent and drew it on top, this was to achieve a different effect that isn't shown here. But you can do that for various things like rocks, bushes and whatever to get a clean overlapping element without having to break it all up in texture.

    Screen Shot 2016-02-11 at 7.08.21 PM.png
     
  5. ecutruin

    ecutruin

    Joined:
    Aug 19, 2014
    Posts:
    39
    You only use 2 UVs per quad? I thought a UV was required per vertex...

    And from the looks of it, you're essentially doing the same as I was for the paths.. using a second mesh layered with a very small space in-between. Still very curious as to how you render such large maps.
     
  6. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    9,052
    It's actually 27 meshes so to not exceed the very max. But the line up exactly, and use whole numbers. The lines do show a bit, if you zoom in and hit just the correct angle. But it was just a stress test. If I actually needed something that scale, I would hide the seems in the design.
     
  7. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    9,052
    2 tris (or more accurately 2 sets of 3 uvs coords). A single uv coord is assigned to each vert, but calculated per face (tri). A quad could have between 4 and 6 uv coords depending on how it is split.

    Yes, on that one I did use a layer over top, it was because I wanted to cast a glow effect between the path and the terrain. The others are a single mesh using tiles like this:
    Screen Shot 2016-02-11 at 9.31.04 PM.png
     
  8. ecutruin

    ecutruin

    Joined:
    Aug 19, 2014
    Posts:
    39
    How do you composite the path on top of a base tile without using a secondary mesh?
     
  9. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    9,052
    Just drew the tris with a small y offset. I run my mesh constructor method twice, once generating the tris for main plane, then again only drawing the path. Then I push the arrays into the mesh. The bit with the light tracer is a third pass using a different method for uving and different material. For that one I just use a shader to scroll the texture to give the effect.
     
  10. ecutruin

    ecutruin

    Joined:
    Aug 19, 2014
    Posts:
    39
    Ah ok. So its still essentially a layered mesh, you're just doing it all as the same mesh. Interesting. So it sounds like I'm kind of on the right track already. Just need to work out the kinks in my current system with the lines between tiles, but otherwise, I'm following a similar design.

    Curiously, is there a worry of too many layers being drawn? I figure eventually, with a large amount of layers, the gap between layers would become noticeable unless it was top-down. That was one worry I had with my current approach.
     
  11. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    9,052
    This is pretty much why I built the experiment in the first, to find those kind of things out. I found that by putting multiple layers in a single mesh instead of different ones I avoided z-fighting an occlusion, and improved performance. I only tried about 3 layers. I would say the best answer would be to try it out and test different gap sizes and number of layers. By doing it programmatically, the nice thing is you can test a lot of variations quickly and find the perfect settings for a particular use case.
     
  12. ecutruin

    ecutruin

    Joined:
    Aug 19, 2014
    Posts:
    39
    Well part of the issue is that I wouldn't know how many layers I would need. The idea would be to allow layers to be added on an as-needed basis easily. I wonder if drawing to a texture might be a better solution, as it would let me keep it a single layer. I could even store a texture per chunk per layer that way changes to a chunk don't have to re-draw the whole world, just that particular chunk layer.. and then a final texture is created by sequentially compiling the layers on top of each other.
     
  13. ecutruin

    ecutruin

    Joined:
    Aug 19, 2014
    Posts:
    39
    Bumping this as I'm still really unsure...

    I will likely have an ever growing number of layers as the game is developed, so ideally, I need them rendered on a single surface in a specific order with optional shader support per surface, unless I can have VERY small distances between layers, the distance between the meshes will become very noticeable.

    Any suggestions? I know I'm trying to optimize ahead of time, but this is one of those things that going back and re-working could involve a lot of work down the road and is a core aspect of the game, so I need to get it working right.

    Edit:

    I think I have a pretty good idea in concept what I need to do, I just don't quite know how to code it. Don't Starve has different tiling sizes for different tile (or seems to) and the tiling of the texture is definitely larger than the tiles themselves (seen by their stone areas) in many cases.

    To replicate this, I think one could render out the borders for a layer, and a solid color where the tile is at. Then render the tiling, replacing only the solid color with the tiling. You'd need to do this for every layer you have, from lowest to highest, merge the results of each in a final pass as your world.

    Really crappy example to illustrate what I mean. This would need to be done per layer..

     
    Last edited: Feb 14, 2016
  14. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    9,052
    Again, I think your best bet would be to prototype and test it. Try it with a 100 or even a 1000 layers and see how it works.

    Sure, building to a texture is another way you could do it. That route would be much more memory intensive though, as you are not repeating image data. It depends on your target platform. I do mobile, so my tests were intended to use as little memory as possible. If your target is pc, you have a little less pressure.
     
  15. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    9,052
    You could stack them all the same height, and just have a separate shader for each with a specific render order. You have to plan a little bit more, but it is effective. That is what we do on SWC, the ground is always a geo-20, shadows are geo-19 and so on. This forces render order regardless of position.
     
  16. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    9,052
    Normally pre-optimization is considered a bad idea, but what you are talking about is really just good planning, I wouldn't consider it pre-optimization. You are correct, it would be much more time consuming to completely change it later. Again, I would suggest prototyping it a few different ways and finding out what best suits your needs.
     
  17. ecutruin

    ecutruin

    Joined:
    Aug 19, 2014
    Posts:
    39
    A good friend of mine suggested an kind of merger of your approach and a shader approach. Basically, render each layer and set sorting layer and sorting order such that the layers can render in the order that they should right on top of each other.

    Then, write a shader to handle merging the edging and full tiles with the actual larger tile. This should, in theory give a similar tile engine to what Don't Starve has (one that is flat, regardless of the number of layers). Sort of a best of both worlds situation. I can re-use mesh information for each layer, only re-generating the UVs for the tiles and then just use UV2s to pass the larger texture tiling to the shader.

    This'll be my next week I think. Seeing if I can get it all working. Gotta learn shader programming and that'll be interesting, I'm sure.

    Edit:

    Pretty funny though that in the end, I'd still have a data structure that is essentially a voxel engine to handle this, only its just rendered flat. Also just noticed that you suggested something somewhat similar. Didn't see it before I posted.
     
    Last edited: Feb 15, 2016
  18. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    9,052
    Looking at videos of Don't starve, it looks like they are doing a combo of creative use tiles and splats or layers on top. So using that type of method, you wouldn't need a ton of layering. Do a bulk of it on the main layer, then edges/blends and then pathing. (or something like that). Splat mapping is pretty similar in concept, but instead of placing transparent layers on top of each other, it uses a shader that uses r,g,b,a channels to cross blend. This is more performant because it isn't doing alpha calculations. This is also another technique we using in SWC, for example if you look at the ground textures here:
    swc_terr.jpg
    These are done in shader via splats. There are 4 stacked and uniquely tiled textures. Basically the same concept as the unity terrain painting.
     
  19. ecutruin

    ecutruin

    Joined:
    Aug 19, 2014
    Posts:
    39
    Yeah, I'd be doing something very similar to that. I will end up using a layer (or game object) per terrain type though, mostly for extend-ability and ideally, mod-ability. This way though, I should be able to render a large amount of terrain tiles drawn via priority, with edging and splats. Since it'll all be layered under a manager, I'll also be able to say duplicate the manager and have a second set of flooring to allow for multi-level structures, said second manager could have different layers as options as well.

    That is all, of course, assuming I can get it all working right. Lots to learn to make it happen. Your game looks beautiful, btw.