1. Hello Guest! It's with a heavy heart that we must announce the removal of the Legacy GMC Archive. If you wish to save anything from it, now's the time! Please see this topic for more information.
    Dismiss Notice

GM:S 1.4 Help with a concept in a tilemap.

Discussion in 'Programming' started by ookki, Mar 7, 2019.

  1. ookki

    ookki Member

    Joined:
    Aug 19, 2016
    Posts:
    12
    Hello.

    I've been playing around with a ds_grid based tilemap, random generation and autotiling.
    My basic idea is to have destructible terrain with some other stored variables like perhaps friction in a ds_map stored within the grid cell corresponding the tile. Now, I have watched some tutorials concerning the topics and have blatantly copied and modified some of the code. Here's the most basic part of the modified code:

    Code:
    
    // initialize ds_grid as a tilemap 
    var width = room_width div CELL_WIDTH; // cell_width is the width of a tile
    var height = room_height div CELL_HEIGHT; // convert room height into DS_GRID size
    
    tileMap = ds_grid_create(width, height);
    ds_grid_set_region(tileMap, 0, 0, width-1, height-1, VOID); // initially set every cell as VOID
    
    /*  fill 2/3 of the map with soil. We do that by adding a ds_map for the tile.
        the parameters are:
        -Type = 
        -TileId = the index of the tile
        -HP = the hitpoints of the tile
        -Friction_s and _k = respective friction coefficients
        -CenterTile = is this a center tile or not?
    */
    
    
    // set the center tiles
    for (var _xx = 0; _xx < width - 1; _xx++)  {
        for (var _yy = height div 3; _yy < height - 1 ; _yy++)  { // fill 2/3 of the map with tiles
            tileMap[# _xx, _yy] = ds_map_create();
            var dsMap = tileMap[# _xx, _yy];
            dsMap[? "CenterTile"] = true;
            dsMap[? "TileBg"] = bg_soil;
        }
    }
    
    // add border tiles
    for (var xx = 0; _xx < width - 1; xx++)  {
        for (var yy = 0; _yy < height - 1 ; yy++)  {
            if (grid[# xx, yy] == FLOOR) {
                if (grid[# xx+1, yy] != FLOOR)  grid[# xx+1,yy] = WALL;
                if (grid[# xx-1, yy] != FLOOR)  grid[# xx-1,yy] = WALL;
                if (grid[# xx, yy+1] != FLOOR)  grid[# xx,yy+1] = WALL;
                if (grid[# xx, yy-1] != FLOOR)  grid[# xx,yy-1] = WALL;
            }
        }
    }
    
    
    The last part of the code is what makes this messy for me. (I left it here as it's in the original code. FLOOR, WALL and VOID are just constants.) The questions are:

    1) Should I actually even take this approach or is there another way I should pursue and what way is that?

    2) If this is a suitable approach, is there a way to do the last code block more tidy than something like:

    Code:
    
    var dsMap = tileMap[# xx, yy];
    if dsMap[? "CenterTile"] {
       dsMap = tileMap[# xx+1,yy];
       if (!dsMap[? "CenterTile"]  {
           dsMap[? "CenterTile"] = false;
        }
       dsMap = tileMap[# xx-1,yy];
       if (!dsMap[? "CenterTile"]  {
           dsMap[? "CenterTile"] = false;
        }
    
    ... and so on.
    
    
    3) I would think it would be better to have the empty tiles just have a simple number (VOID at the moment) than just fill them with ds_maps as well, but that would mean checking an empty tile with an accessor would give an error. The solution, which comes to my mind is doing something like:

    Code:
    if tileMap[# x, y] != VOID {
       dsMap = tileMap[# x, y];
    }
    
    And do that every single time I want to check the ds_map. But it just feels really ugly. Perhaps it's the right way but I'd like to have some thoughts on this.


    Here's the link to the original code I stole:
    https://www.dropbox.com/s/d15t23zqr2kehnp/Level Generation and Autotiling.txt?dl=0

    I think it was by HeartBeast.
     
  2. Simon Gust

    Simon Gust Member

    Joined:
    Nov 15, 2016
    Posts:
    3,220
    I'd say it entirely depends on what kind of game you want to make.

    Say if you have an ice block that has friction (because ice), you don't need to store that information in that block of ice where it is.
    You just have to make the player remember that on ice-blocks, friction is different.

    This is global information that always applies to ice blocks, same with a block being destructible or not.
    You can store this information outside the grid.

    And if you do end up needing individual info (e.g. how much health a block has left, what autotile it is on, is it a wall or a floor)
    you can at least put an array in that block instead of ds_map. ds_maps are super slow compared to arrays.

    example:
    Code:
    tileMap[# x, y] = lookup_add(VOID, 0, 100);
    
    Code:
    /// lookup_add(tile type, autotile, health)
    var array = array_create(argument_count);
    for (var i = 0; i < argument_count; i++) {
       array[i] = argument[i];
    }
    return (array);
    
     
    ookki likes this.
  3. ookki

    ookki Member

    Joined:
    Aug 19, 2016
    Posts:
    12
    Thanks for the reply! It seems that arrays are the way to go and I actually had thought about that before. The documentation of arrays, however, doesn't give the most coherent view of them. There's a couple of questions regarding to them I'm wondering:

    1) What is the fastest way to access them in code?
    Code:
    var array = [# x, y];
    array[x] = 3;
    
    is something that comes to my mind. Or should I use array[@ x]=3; here? And is there a way to do all that in one line of code?

    2) In the scenario that a tile with three array keys e.g. a[0]=something, a[1]=1, a[2]=100, gets destroyed and I want it to become a VOID tile, which needs only one key, is there something I can and should do to tidy up the previous array?
     
  4. Simon Gust

    Simon Gust Member

    Joined:
    Nov 15, 2016
    Posts:
    3,220
    1)
    unfortunately, you can't do it in a single line in GM:S 1.4 (I believe you wouldn't even in GM:S 2.0 yet).
    So you always have to extract the array first.

    get
    Code:
    var array = grid[# x, y];
    var hp = array[2];
    
    set
    Code:
    var array = grid[# x, y];
    array[@ 2] = 350;
    
    important:
    when setting values, you must use an accessor (the @-symbol), otherwise the array will be copied (as you've created a new local variable to get it's address from memory)
    And you would be forced to put the array back into the grid like this
    Code:
    grid[# x, y] = array;
    
    I assume this is annoying.
    If you use the accessor, you don't need to worry, ever.

    2)
    if you want void tiles to store no information at all you don't have to put an array in to them.
    The problem with putting in VOID, which I assume is a macro for the value 0, is that you have no way of differentiating an array from a number.
    If you create an array, it might be given the index 0 or any positive value.

    What you can do is put in noone which equals -4, then you are sure that there is no tile and definetly no array either (because array indexes start at 0 and never go negative).
    noone is a keyword and shares the same speed as a macro.

    If arrays are too confusing, you may try ds_lists, they're comparably fast and easier to handle, the "noone-trick" also works with ds_lists (any data structure for that matter).
     
    ookki likes this.

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice