Search Unity

2D platformer Terraria-like procedural level generation

Discussion in 'Scripting' started by Emolk, Sep 2, 2019.

  1. Emolk

    Emolk

    Joined:
    Feb 11, 2014
    Posts:
    241
    Could anyone point me in the right direction to making a world generator with a Tilemap, specifically for a 2D platformer? Can't find any information.

    Cheers.
     
  2. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,605
    The process is very similar for procedural 2D or 3D worlds, given that 3D imho is a lot harder to work with.

    To get infinite, or infinitely many, worlds you need some equally "infinite" pool of coherent numbers to sample from. This us generally done by using coherent noise functions, like Perlin Noise for example. 2D Perlin Noise takes in 2 values and returns a value between 0 and 1. Doing this for every pixel coordinate on a texture would look something like this:



    Here the black pixels represent lower values (closer to 0), while white pixels represent higher values (closer to 1).
    When creating terrain, you would sample the noise function, most likely using the coordinate of the tile as input, and spawn a tile if the returned value is below some threshhold value (like 0.5 for example). That way, in the above example, you would create tiles (and thus terrain) whereever pixels are black'ish.
    Be aware tho, that Perlin Noise returns the same value for each even integer (1, 2, 3, ...), so you'll have to scale your inputs by some value (i'll suggest 0.03-0.15).
    At this point you'd already create a cave-like terrain, similar to the image above. You can now combine different noise functions (or differently scaled versions of the same noise) to create more interresting terrain.
    To create a surface to walk on, you'll have to adjust the value you got from the noise function based on the y coordinate you sampled, such that the higher you are, the higher (remember to stay below 1) the value is.
    Applied to the above image, this would result in more white pixels the higher you get, and thus more air.

    Also, when creating a Terraria-like world by spawning in each single tile, you will run into performance problems very quickly, since each tile would have its own render-call. Thus you will need either:
    • combine all tiles of the same type (stone, dirt, ...) to one mesh
    • or use a texture atlas and render the whole world as one mesh,
    • or if you want to later change the mesh (dig for example): do the above but in chunks, for faster updates
    I'd first get the main world generation working with a small 10x10 example before worrying about performance tho. I just thought i'd throw this in, since you are bound to run into this problem

    I'm not quite sure if that's what you wanted, since you mentioned "2D platformer" as well, but if you just want some platforms i doubt Terraria was the right example hehe.
     
    Emolk likes this.
  3. Emolk

    Emolk

    Joined:
    Feb 11, 2014
    Posts:
    241
    This was perfect, thanks. It was surprisingly easy to implement. Could you reiterate about combining the noise functions? Or how to change the layout of the caves? I'm finding a lot of waves reach a point where they get to a massive fall which i don't think the player would enjoy too much. How do i change the shape of the white spaces?
     
  4. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,605
    To better visualize things when working with noise, i'd recomment rendering some texture on a plane to visualize the noise. This allows you to bet a better idea for how changing some parameters influences the noise. If you use a new'ish version of Unity, you could also experiment in the shader graph, which features these same noise functions and visualizes them for you (only for experimentation tho).

    Anyways, you can "zoom" in or out of the noise by changing the aforementioned noise scale (the value you multiply your inputs with). The effect is just that, imagine zooming in our out of the above image. To combine different noise, you'd simply generate different noise values from different noise functions for the same input coordinate and multiply them.
    The result "looks" similar to if you'd overlay two of the above images on top of each other, with some transparency.
    Also, people tend to overlay multiple "octaves" of the same noise function to get less smooth results (1D example below). You should find plenty of information when googling for noise octaves.

    That said, there are different ways to create terraria-like terrain. You will most certainly use noise, but depending on what you need you may chose a different approach. I believe the way most games like Terraria or even Minecraft actually end up doing it, is by generating solid terrain first, then carving in the caves later. This should generally give a bit more control over how the caves are generated, if that's important.
    So for a Terraria-like game you'd use multiple octaves of 1D perlin noise, to generate the "surface outline" and fill everything below it with 1's. Example for the surface line (ignore the text, it's from the image):

    Now you can carve out caves from that shape. That can be achieved with different methods, but i cant go into detail because i've never worked on proper caves so far. I believe there are some more strand-like noise functions, so you could subtract the result from these functions from your 1's and not create a block if the value gets too low, or something along those lines. At the very least, this lets you use a different function for describing the surface, vs describing caves. A disadvantage of this approach is that you cant get "overhangs" anymore, which is often something you want for interresting looking 3D terrain, but for a 2D world i believe you wont miss it. Featuring overhangs could even have gameplay disadvantages depending on the movement options the player has.

    Anyways, then there is something called "Cellular Automata". Maybe you've heard of The Game Of Life, which is probably the most known example of a cellular automata. A CA is a set of simple rules that results in complex behaviour, such as generating caves. In a lot of voxel based games, water is handled using CA as well (not in Minecraft tho).
    I've personally not yet worked with CA, but it's on my TODO list. So i cant really say how compatible it's with what we talked about before, but there are always ways to make things work out.
     
    Emolk likes this.
  5. Emolk

    Emolk

    Joined:
    Feb 11, 2014
    Posts:
    241
    Thanks mate i'll have a look at these methods.
     
  6. Emolk

    Emolk

    Joined:
    Feb 11, 2014
    Posts:
    241
    Could you point me in the direction of doing the first step, the multiple octaves with 1D perlin noise for the surface outline? I can't find anything specific to my problem, or ones that produce an outline similar to your image.
     
  7. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,605
    This article has a lot of information you dont necessarily need to understand, but at the very least i'd have a look at the "Working with Octaves" chapter**. The graphs should help you understand what Octaves are and how they work together to form the shape from my image. Actually, it's the exact example i got the image from.
    https://flafla2.github.io/2014/08/09/perlinnoise.html

    The code example below also shows you how this is done, altough it's for 3D input, while you want 1D input. The only difference being the amount of values the perlin function takes. So after understanding how it works, you only need the correct perlin function now.
    Mathf comes with a function for PerlinNoise, which takes 2 inputs. I did not try this, but you should be able to simulate 1 input, by simply always using the same value for the second input (0 or if that does not work, something like 0.03f). That way you should get "one row of pixels" from the above image, or in other words one output value for one input value, which can be visualized as a graph.
    You could also look for how to implement a perlin function with 1 input yourself. This looks promising; https://jameshfisher.com/2017/10/15/1d-perlin-noise/

    **If you want to generally read up more on Octaves and such, the surrounding topic is called fBM for fractal Brownian Motion, on which you can read up here: https://thebookofshaders.com/13/
    The first part may help you understand how it works, afterwards it may get a bit more complicated and focusses on 2D.