Search Unity

Resolved Help, coding a autotile with 16 tiles and bit masking.

Discussion in 'Scripting' started by GMan_Wolf, May 18, 2020.

  1. GMan_Wolf

    GMan_Wolf

    Joined:
    Jan 30, 2016
    Posts:
    7
    Hello

    Been trying to get this working for hours, googling all sorts of dead ends that dont seem to cover what I need.

    I am trying to write my own autotiler instead of using the 2d extras as I have some other requirements like sprite swapping based on some other things.
    So for right now im just trying to get some basic autotiling working first with code.

    Looking at the options you can go with 47 tiles covering every configuration, or 16 tiles that covers almost everything but reaquires atleast 4 tiles together (which im happy with) So i settled on trying the below to test things out.


    https://www.boristhebrave.com/2013/07/14/tileset-roundup/

    It suggest you can use bit masking (looking at the neighbours) to calcualte an index for which sprite this tile should have.
    tile_index = topLeft + 2 * topRight + 4 * bottomLeft + 8 * bottomRight

    I coded this up as
    Code (CSharp):
    1.        
    2. int TR = TileValue(tilemap, position + new Vector3Int(1, 1, 0)) ? 1 : 0;//Top Right
    3. int BR = TileValue(tilemap, position + new Vector3Int(1, -1, 0)) ? 1 : 0;//Bottom Right
    4. int BL = TileValue(tilemap, position + new Vector3Int(-1, -1, 0)) ? 1 : 0;//Bottom Left
    5. int TL = TileValue(tilemap, position + new Vector3Int(-1, 1, 0)) ? 1 : 0;//Top Left
    6.  
    7.         int mask = TR + BR * 2 + BL * 4 + TL * 8;              
    8.         tileData.sprite = spriteList[mask].sprite;
    TileValue just return whether a tile of this type exists in that position.

    Moving the sprites around in my list I was able to generate the below.



    As you can see its not quite right!, and I think its becuase I am not accounting for the cardinal neighbours!
    But a bit mask with 8 neighbours instead of 4 is going to have 256 permitation ( Cut down to 47 unique as many are identical, which is too many. How do i bring that down to just the 16 tiles I have ?

    Can anyone offer any insight or point me in the right direction please.
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,742
    You're close but you have some underlying information theory issues that must be resolved.

    Looking at your 16 tiles above, they are not representable in 4 bits of data.

    The reason is that your possible edges are not one bit of information each.

    If they were, then an edge could either be water, or be land, but it could not be half-water-half-dirt.

    In order to have water, dirt, and water-dirt and dirt-water, that would require four (4) possible combinations, so 2 bits of information per edge.

    You can do this, but you will have to correlate much more about your edges than just one bit of information.

    This also means your possible tiles to handle all forms of smoothing will require 4^4 possible tiles, or 256 tiles.

    For a first try I recommend sticking with 16 tiles (4 pure edge bits total). Here is an example tilemap representable with 16 tiles (4 bits of information). I always make my tilemaps so that north is bit 0 (value 1), east is bit 1 (value 2), south is bit 2 (value 4) and west is bit 3 (value 8).

    The tiles would therefore be numbered 0 to 15, starting from upper left and going across.


    rally_16.png

    The above is green land, or yellow beach. Not shown is "open ocean." For your case, you would need an extra tile that is fully clear, which bit-wise looks identical to the lower right corner tile (all edges are a beach). All edges as a beach is a single island, topologically identical (from the edges perspective) to an open water.

    Smoothing can be quite tricky!
     
    Olmi, eses and PraetorBlue like this.
  3. GMan_Wolf

    GMan_Wolf

    Joined:
    Jan 30, 2016
    Posts:
    7
    Thanks @Kurt-Dekker

    I have seen similar layouts while searching, often related just to walls, fences,paths etc and not open floors/grass/water.
    But I am going to give it another go with a layout as above, thanks.

    I was trying to paint/layout the tiles in a way that was easier to draw and then fiddling with the order in the array but maybe until I get it right I will just need to use the tileset layed out nicely from 0 to 16.

    Also i managed to brute force mine into working with the following.
    Code (CSharp):
    1.         bool N = TileValue(tilemap, position + new Vector3Int(0, -1, 0));
    2.         bool E = TileValue(tilemap, position + new Vector3Int(1, 0, 0));
    3.         bool S = TileValue(tilemap, position + new Vector3Int(0, 1, 0));
    4.         bool W = TileValue(tilemap, position + new Vector3Int(-1, 0, 0));
    5.  
    6.         int mask = N    ? 1 : 0;
    7.         mask += E       ? 2 : 0;
    8.         mask += S       ? 4 : 0;
    9.         mask += W       ? 8 : 0;
    10.         newSprite = tileArray[mask].sprite;
    11.         if (mask==15)
    12.         {
    13.             bool TR = TileValue(tilemap, position + new Vector3Int(1, 1, 0));   //Top Right
    14.             bool BR = TileValue(tilemap, position + new Vector3Int(1, -1, 0));   //Bottom Right
    15.             bool BL = TileValue(tilemap, position + new Vector3Int(-1, -1, 0));  //Bottom Left
    16.             bool TL = TileValue(tilemap, position + new Vector3Int(-1, 1, 0)); //Top Left
    17.                        
    18.             if      (!TL && !BR)    newSprite  = tileArray[5].sprite;
    19.             else if (!TR && !BL)    newSprite  = tileArray[10].sprite;
    20.             else if (!TR)           newSprite  = tileArray[1].sprite;
    21.             else if (!BR)           newSprite  = tileArray[2].sprite;
    22.             else if (!BL)           newSprite  = tileArray[4].sprite;
    23.             else if (!TL)           newSprite  = tileArray[8].sprite;
    24.         }
    25.  
    26.         tileData.sprite = newSprite;
    Basically If I got a full with cardinal neighbors on all sides, I do an extra check on the ordinals

    With this any tiles than a square of 4 are empty/sand/water etc, and edges must be a minimum of 2 tiles wide
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,742
    Nice! What's important is that it works for you. I'm glad you're moving forward productively.
     
  5. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    @GMan_Wolf

    Check the article from TutsPlus... it solves pretty much all the problems you will run into.

    https://gamedevelopment.tutsplus.co...ng-to-auto-tile-your-level-layouts--cms-25673

    IMO, I'd go with rule tile... going for 47 rule blob tile is quite a lot of work. Here are some notes / but warning, I'm pretty new with this stuff...

    I've built such thing once, and did it like @Kurt-Dekker said, using bits. I limited tile to only use transition between two materials (terrain-water). Otherwise it would have been insane amount of combinations, and I would probably have failed creating it.

    Like mentioned already by Kurt, there are several cases, the list of "rules" is quite long. This is how a part of the tile selection code looks like in my solution:
    Code (CSharp):
    1. case 5:
    2. case 13:
    3. case 37:
    4. case 45:
    5. case 133:
    6. case 141:
    7. case 165:
    8. case 173:
    9.     return 2;
    10. case 7:
    11. case 15:
    12. case 39:
    13. case 47:
    14. case 135:
    15. case 143:
    16. case 167:
    17. case 175:
    18.     return 3;
    For tile 2 you will have several combinations using it, and same goes for all the other 46 tiles, because you are not using the full 255 set, ignoring some combinations (as seen in the above mentioned article), you will have to take those too into your calculations... So you will have 0-255 cases in your if/switch statement, but not all of those will have a unique tile for it.

    How to get to this point where you have a list of rules was the hard part (for math ignoramus like me). I could have used the list from the above mentioned article (IIRC it had such), but I wrote an editor script to build all the needed cases. Script first generated all the bit combinations, then removed those combinations that didn't matter, then it created a template bitmap png for those needed 47 combinations to be used for texture sheet and switch code. Then the actual blob47 tile script used the generated switch statement, with all the 255 cases.

    And this was the template I used to define the tiles around inspected tile:
    Code (CSharp):
    1. // indices
    2. // 7 0 1
    3. // 6 t 2
    4. // 5 4 3
    5.  
    6. // bit values
    7. // 128 1 2
    8. // 64  t 4
    9. // 32 16 8
    Anyway, why not look into using Unity rule tile, or build your own rule tile instead? IIRC there are several issues with blob 47 tile thing, whereas rule tile is more flexible, because you can "break the rules" whereas this 47 combination thing works the same every time.
     
    Last edited: May 19, 2020
    Olmi likes this.