Various question about tiles with map generation

Discussion in 'Programming' started by Fabseven, Jun 12, 2019 at 8:29 PM.

Tags:
  1. Fabseven

    Fabseven Member

    Joined:
    Oct 7, 2016
    Posts:
    321
    Hello there,
    I am working and a random map system and encountering some issues.

    First issue :

    Cells w/h is 16, in gamemaker gui i can change my room to 1024x1024 or 2048x2048 as example, the game is working fine.
    But when the room is 4096x4096 or more i have an unexpected bug
    First generation is always good, the bug occurs when i generate the map again,


    ___________________________________________
    ############################################################################################
    FATAL ERROR in
    action number 1
    of Key Release Event for <Space> Key
    for object obj_system:

    Push :: Execution Error - Variable Index [1,0] out of range [1,-1] - -7.tiles(100001,32000)
    at gml_Script_system_reset_generator (line 25) - tile_delete(tiles[t]);
    ############################################################################################
    --------------------------------------------------------------------------------------------
    stack frame is
    gml_Script_system_reset_generator (line 25)
    called from - gml_Script_system_goto_next_level (line 8) - system_reset_generator()
    called from - gml_Object_obj_system_KeyReleased_SPACE_1 (line 1) - system_goto_next_level()


    In order to do my stuff i am destroying some remaining obj (as drops , monsters, ...) and then delete tiles
    Script system reset generator
    Code:
    with(obj_system)
    {
       
        ds_grid_destroy(map)
        mp_grid_destroy(map_path)
       
        with(obj_monster) { instance_destroy();}
        with(obj_water1) {instance_destroy();}
        with(obj_drop) {instance_destroy();}
        with(obj_mushroom) {instance_destroy();}
        with(obj_animal) {instance_destroy();}
        with(obj_drop) {instance_destroy();}
        with(obj_dropxp) {instance_destroy();}
       
        for(i=0;i< ds_list_size(list_obj);i++)
        {
            instance_destroy(list_obj[|i])
        }
       
        var tiles = tile_get_ids();
        for (var t = 0; t < array_length_1d(tiles); t++;)
           {
           
              show_debug_message("delete tile " + string(t))
              tile_delete(tiles[t]);
             
           }
    }
    
    the game maker output show delete tile 1 to 32000 and the error occurs at 32000
    next map goes well (except some visual bugs) if i delete the tile_delete loops.
    Why the bug at 32000, tile_get_ids limited or something ?

    Second issue :

    I mainly use a big ds_grid to randomly generate the map and after this i have to draw tiles on the real map
    ( = loop with draw_tile executed a lot of time)
    I think (because i have a show_debug_message before drawing tile) that's the drawing part take the most time (~35s for a map 5120x5120, so about 100 000 tiles)
    Is there anyways to do this faster ?

    The code below
    generator_draw
    Code:
    with(obj_system)
    {
        instances = ds_map_create()
        ds_map_add(instances,string(EXIT),asset_get_index("obj_exit"))
        ds_map_add(instances,string(ENTRANCE),asset_get_index("obj_entrance"))
        ds_map_add(instances,string(CHEST),asset_get_index("obj_chest"))
        ds_map_add(instances,string(GCHEST),asset_get_index("obj_chestG"))
        ds_map_add(instances,string(OTREE),asset_get_index("obj_otree"))
        ds_map_add(instances,string(ADVICE),asset_get_index("obj_advice"))
        ds_map_add(instances,string(MINERAL),asset_get_index("obj_mineral"))
        ds_map_add(instances,string(WATERO1),asset_get_index("obj_water1"))
       
       
        show_debug_message("Draw start ")
        iz = 0    
    
        for(yy=0; yy < height;yy++)
        {
            for(xx=0; xx < width;xx++)
            {
                iz ++
                loading_info = string(iz) +"/" + string(height*width)
                show_debug_message(string(iz) +"/" + string(height*width))
           
                if( map[# xx,yy] >= 1000 and map[# xx,yy] < 5000 and  map[# xx,yy] != VOID)
                {
                    //obj = tile = floor, value = FLOOR, generation of obj
                    o =  map[#xx,yy]
                    // show_debug_message("o =>" + string(o))
                    o =  instances[? string(o)]
                    //show_debug_message("o =>" + string(o))
                    z = instance_create(  xx*cell_www,yy*cell_hhh,o)       
                    ds_list_add(list_obj,z)
                   
                    map[#xx,yy] = FLOOR
                    tile_add(tileset,choose(0,16,32,48),0,cell_www,cell_hhh,xx*cell_www,yy*cell_hhh,-1)   
               
               
                }
                else
                {
                    if( map[# xx,yy] >= 5000 )
                    {   //great tree en others stuffs
                        var z = map[# xx,yy] -5000
                        tile_add(tileset,z*cell_www,6*cell_hhh,cell_www,cell_hhh,xx*cell_www,yy*cell_hhh,0)                   
                   
                    }
                    else
                    {
                        switch(map[# xx,yy])
                        {
                           
                       
                            case FLOOR:
                            tile_add(tileset,choose(0,16,32,48),0,cell_www,cell_hhh,xx*cell_www,yy*cell_hhh,0)
                            break
                           
                           
                            case VOID:
                            tile_add(tileset,choose(0,0,0,16,32),5*cell_hhh,cell_www,cell_hhh,xx*cell_www,yy*cell_hhh,0)               
                            break
                           
                            case WATER:
                            //tile_add(tileset,0,10*cell_hhh,cell_www,cell_hhh,xx*cell_www,yy*cell_hhh,0)               
                            generator_sub_draw_water()
                           
                            break
                            /*case EXIT:
                            case ENTRANCE:
                            tile_add(tileset,0,12*cell_hhh,cell_www,cell_hhh,xx*cell_www,yy*cell_hhh,0)               
                            break*/
                        }
                    }
                }
            }
        }
       
            ds_map_destroy(instances)
            show_debug_message("Draw END ")
    }
    
    
    
    Third issue (not so important bug still)

    I trid to draw on gui the progress of the draw (iz var) , work fine in game maker output thanks to show_debug_message but not in-game, i am 99% sure it's because the process cannot leave the 2 loops and cannot draw my things in the same time.



    If you have any advice it would be great, feel free to ask for more code if needed.
     
  2. Simon Gust

    Simon Gust Member

    Joined:
    Nov 15, 2016
    Posts:
    3,086
    1st issue:
    Most probably, tiles are limited. What you can try is chaning the layer of the tiles sometimes and call
    Code:
    tile_get_ids_at_depth()
    
    this will return the array of tiles at a certain depth.

    Isnt there also a function that lets you delete a tile-layer without needing to list all tiles and removing them that way. Take a look in the manual.

    2nd issue:
    There is a lot to optimze here.
    The biggest time-killer is definetly the show debug message.
    More things:
    - not using temporary variables:

    - in your for-loop
    Code:
    for (var yy = 0; yy < height; yy++) {
    for (var xx = 0; xx <  width; xx++) {
    
    }}
    
    - iz before the loop starts.
    - loading_info inside the loop.
    - o
    and some others

    - not shortcutting refrences:
    like checking for map[#xx, yy] multiple times instead of first create a value that holds map[# xx, yy] and check against that value.

    No
    Code:
    if (map[# xx, yy] == this)
    
    if (map[# xx, yy] == that)
    
    if (map[# xx, yy] != those)
    
    Yes
    Code:
    var tile = map[# xx, yy];
    
    if (tile == this)
    
    if (tile == that)
    
    if (tile != those)
    
    - using a ds_map inside a massive loop:
    ds maps are very slow, combine that with a massive loop, and your game will be crawling.
    There are better ways to translate this kind of data.

    Basically, you're connecting a macro to an object index.
    Why use a ds-map and strings when you could just use an array instead.
    Code:
    var macro_to_object;
    macro_to_object[EXIT] = obj_exit;
    macro_to_object[ENTRANCE] = obj_entrance;
    macro_to_object[CHEST] = obj_chest;
    macro_to_object[GCHEST] = obj_chestG;
    etc...
    
    then inside the loop
    Code:
    var tile = map[# xx,yy];
    var object =  macro_to_object[tile];
    
    var inst = instance_create( xx*cell_www, yy*cell_hhh, object);
    ds_list_add(list_obj, inst);
    
    I'd also appreciate it if you explain why you need tiles, objects and a ds grid for your world. Personally I'd settle with one of those options.

    About the third issue, you are correct. The loop must finish first before any other computation can be done.
     
    Last edited: Jun 13, 2019 at 9:11 AM
  3. Fabseven

    Fabseven Member

    Joined:
    Oct 7, 2016
    Posts:
    321
    TY for your answker
    for 1 : I will take a look in the manual as you said
    for 2 : Ok i am going to optimize the code with your advice.

    I donot know if its the best way to do so (but tutorials give this method) but my map is random, so in the beginning i use a ds_grid filled with 0 and starting from the middle of the grid i navigate randomdly X times through the ds_map to change 0 to another value. In the end, using this modified ds_grid i draw the tiles (0 = forest, 1 = plain, 2 = water ....). For objects, after filling the ds_map, some scripts check empty spots (3x3 for example) and set values EXIT,ENTRANCE,CHEST,etc in the ds_map , theses values are known in generator_draw to not draw tiles but to create obj instead.
     
  4. Fabseven

    Fabseven Member

    Joined:
    Oct 7, 2016
    Posts:
    321
    hello again,
    For 1 : there is a function tile_layer_delete(depth) to erase a lot of tiles in 1 go in the end got 2 of them, no more bug with this x)
    For 2 : Optimized the code as you said, i think it's better, added a timer , 22s for a 5120x5120 room, 8s for a 4096x4096 room,
    would be great if i could go <10s for 5120x5120, do you think it's possible ?

    For 3 : Should i just draw in gui "Generating map" before and that's all or is there a way to show the progress ?
     
  5. Simon Gust

    Simon Gust Member

    Joined:
    Nov 15, 2016
    Posts:
    3,086
    2: It is going to be slow as in GM 1.4 adding tiles is just very slow. Not much you can do about it except move away from tiles.
    3: You'd need multi-threading probably, which game maker does not support. There is a forum thread in here that does discuss the contents, but this is probably not the right step of development right now.
     

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