GMS 2 Procedural generation: creating prebuilt room templates in a GMS2 Room and combining them into one level

Hello,

I'm working on a game that will use a combination of procedural generation and prebuilt rooms to create its levels. Currently, the layout of each prebuilt room is specified via a character string (in which each character represents a tile, object, NPC, etc.). This method has the advantage of simplicity, but has a few downsides: the structure of the room is contained in a single unbroken character string and so is difficult to visualize, which can present problems when there are dozens or even hundreds of room templates to keep track of, and the use of unique characters to specify different entities in the game will become more difficult as the total number of entities grows (e.g. if you're using "s" for a skeleton NPC, what do you do if you have 5 different types of skeletons?). Because of this, I've been looking for different ways to create and spawn prebuilt rooms into the game. I've considered creating room templates with sprites and using draw_getpixel to parse the images, but the function is very slow and this method still runs into the problem of the templates being too abstract. What I was interested in doing was somehow creating all the templates in a GMS2 room, and then loading or copying them into the generated level. I'm not sure if this is possible, but I'd like to know if you have any ideas.

Thanks in advance.
 
Yes. You'll want to use ds_grids to store the information. You can use for loops to grab the room data, then paste the same objects into your procedural level. ds_grids can be saved and loaded, or you could rebuild the grids every time you load the game. It may not be more efficient than the pixel solution, but it gives you a lot more flexibility.

And, if you'll pardon a bit of self-promotion, I have a marketplace asset that can do this exact sort of thing, along with other tools for procedural generation. It may serve your purpose, if it appeals to you: https://marketplace.yoyogames.com/assets/6835/jen_scripts-terrain
 
Last edited:
Thank you for the reply. I've been using a ds_grid to contain the layout of the active level. The problem I'm encountering is figuring out the format to store the room templates in before loading them into the ds_grid on the active level. Right now it checks each character in a string, and then sets the corresponding grid cell to a wall/floor/pit/NPC/etc. I've included a small sample of the code I'm currently using below.

GML:
var rm = 0;
global.entrance_exists = false;
global.exit_exists = false;

repeat(global.roomNum)
{
    // Go through each room and get the dimensions
    var Lbound =  r[rm, 0] + 1;
    var Tbound =  r[rm, 1] + 1;
    var Rbound =  Lbound + r[rm, 2];
    var Bbound =  Tbound + r[rm, 3];
    
    var width = r[rm, 2] + 1;
    var height = r[rm, 3] + 1;
    // Assign a layout to the room based on its dimensions
    var room_string = "none";
    switch((string(width) + string(height)))
    {
        case ("3" + "3"):
            switch(irandom(5))
            {
                case 1: room_string = "FFFFFFFFF"; break;// Empty
                case 2: room_string = "FFFFPFFFF"; break;// Central pit
                case 3: room_string = "FFFFWFFFF"; break;// Central wall
                case 4: room_string = "PFPFFFPFP"; break;// Corner pits
            } break;
    }
    
    var counter = 1;
    for (var j = Tbound; j <= Bbound; j++)
    {
        for (var i = Lbound; i <= Rbound; i++)
        {
            var tile_char = string_char_at(room_string, counter);
            
            switch(tile_char)
            {
                // Tiles
                case "A":    ds_grid_set(global.roomIDgrid, i, j, ENTRANCE);            break;
                case "F":    ds_grid_set(global.roomIDgrid, i, j, FLOOR_ROOM);        break;
                case "H":    ds_grid_set(global.roomIDgrid, i, j, HALF_WALL);        break;
                case "P":    ds_grid_set(global.roomIDgrid, i, j, PIT);                break;
                case "W":    ds_grid_set(global.roomIDgrid, i, j, WALL);                break;
                case "Z":    ds_grid_set(global.roomIDgrid, i, j, EXIT);                break;
            }
        }
    }
 
That's what I mean. Store the room templates in a grid as well. Additionally, the spaces in both grids can use object ids instead, so you don't have to do any conversions. You can fill a template grid from a room that has instances placed in it. It might look like this:

GML:
 // How far apart in the room are your objects? (Aligned to a grid!)
cellw = 32;
cellh = 32;

// Create a new ds_grid that matches the size of the room.
template = ds_grid_create(room_width / cellw, room_height / cellh);
ds_grid_clear(template, noone);

// Loop through x and y and fill grid with corresponding objects.
for (var yy = 0; yy < room_height; yy += cellh) {
for (var xx = 0; xx < room_width; xx += cellw)
{
    // Check position for an instance, store its object index in the template.
    var inst = instance_place(xx, yy);
    if (instance_exists(inst))
    {
        // Note this: ds_grid accessor so you don't have to use ds_grid_set. Much more convenient.
        template[# xx, yy] = inst.object_index;
    }
} }
Then you would do the inverse to place those objects into a new room again later. Loop through and do instance_create, at the corresponding position for every valid value in the template. Now, it looks like you're doing a few other things too, but I hope this helps you find a better solution, at least.
 
Top