Asset - Scripts [Jen_scripts] Procedural Terrain Generation

[Jen_scripts v2.0]
Asset for Procedural Terrain Generation


Download: https://cloakedgames.itch.io/jen-scripts

[Jen_scripts] Asset for Procedural Terrain Generation Created by Cloaked Games
Jen_scripts is a GameMaker Studio 2 library designed for rapid prototyping and development of procedurally generated levels. It contains over 60 iterative functions that can be combined with flexible parameters to create varied terrain. Jen_scripts supports geometric shapes, randomized distributions, and other algorithms to produce a limitless variety of patterns within a 2D grid. The grid can then be interpreted as object indices or tilemap data to convert into terrain in your room.

Want to create a lake? Try jen_ellipse and jen_near. Want to create a river? Try jen_wander_direction or jen_wander_line. Want to create a cave? Try jen_automata. Want to create a maze? Try jen_maze_prim or jen_maze_backtrack. Want to create a building? Try jen_grid_room. Want to create an island? Try jen_heightmap_sampling. Something more complicated? Combine the functions together to form terrain that fits your ideal design, using optional arguments for extra flexibility.

Features:
  • Create lines, shapes, and blobs.
  • Create a wandering path between two points or in a direction.
  • Maze generation with Prim’s Algorithm or Backtrack Algorithm.
  • Cave generation with Cellular Automata.
  • Heightmap generation with Average Sampling or Gradient Scattering.
  • Copy sections of your rooms to incorporate hand-crafted content.
  • Tilemap support and programmatic autotiling.
Documentation: [Jen_scripts Documentation]
There is full documentation for every [Jen_scripts] function, including example code. You can read the documentation here.

Introduction to Using [Jen_scripts]: [Jen_scripts Tutorial]
Confused or just want to know more? This includes step by step instructions for basic tutorial, and some complete terrain examples you can use as additional resources.

Active Support: If you have questions, suggestions, bug reports or any other feedback, this is the place to do it! I will be continually answering any questions people have and improving the asset over time. If you have a question concerning your purchase or something you wish to discuss only with me, please feel free to send me a PM on this forum.

Terraria Style.jpg Dungeon with Treasure.jpg Islands in Ocean.jpg Cave with Ore.jpg River through Chasm.jpg
 
Last edited:
Very interesting. I notice that the 'resolution' and overall detail is rather low. Is there facility to increase this? For example, I'd like to approach something similar to a height map from Google Earth.
 
Unfortunately, not really. [Jen_scripts] is designed for small pieces of terrain, like a rouge-like level or a survival island map. You could connect multiple ones together. But while it can handle a fairly large room, generating very large pieces of terrain (especially with the heightmap functions), is probably not going to work for you. It doesn't run asynchronously.

In addition there aren't very many options for heightmaps yet. Those will be added eventually, but at the moment, you just have the one.
 
Last edited:
I have finished the first update for [Jen_scripts]. There were a few things that I had wanted to do before, but I didn't really know exactly how to implement them. Turns out it was really easy and only took a few hours, but here we go, version 1.1.0, with five new functions:
  • jen_number: allows you to create a specific number of instances at random positions.
  • jen_scatter_offset, jen_number_offset: allows you to create instances offset from the original location. (Good for platformers. You can do things like add torches to the floor of a cave like in Terraria).
  • jen_scatter_apply, jen_number_apply: the same as the above, but you can actually apply entire grids, like pre-built structures or randomly generated trees at positions within the terrain. This I've found to be pretty powerful for more advanced terrain, and actually simplifies a lot of the code I used in the demo by significant amounts.
Thanks to everyone who's bought the asset so far, I appreciate the support. I hope it has been working out for you.

If you haven't bought it yet, there's several resources available so you can learn more.
Finally, an example of the non-terrain use of [Jen_scripts], in the GMC Jam #8 just recently, I and @Pere submitted this game, The Hoard. I used [Jen_scripts] to generate the randomized army formations you fight. It only took me 10-15 minutes to do the actual code work because jen_apply and jen_grid_room_array made things so easy.
 
GML 2.3 is now released. By the end of this week, I will be updating [Jen_scripts] to use the new syntax, and fix the conversion errors. (Currently, the asset will not compile on 2.3).

Additionally, over the next few months, I will be releasing updates with new features. I have a few things in mind:
  • More heightmap options, including Perlin noise.
  • Templates, which are sort of like advanced grids, for creating levels with variations.
  • Layered function calls, which replace chance functions and provide more flexibility.
Please stay tuned! I hope to make this an even more powerful set of scripts very soon.
I recognize that this asset was published two years ago, but I think I did not really have the skills to update it properly until now.
 
Version 2.0 Release (20% OFF)
Jen_scripts v2.0 is now released and can be purchased on the marketplace page.
Additionally, it will be 20% for a short time to celebrate this new major version.

This update is primarily designed to update Jen_scripts to support GML 2.3, as the 2.2 version of Jen_scripts does not convert flawlessly to GML 2.3. This new version will also not work on earlier versions of GML. The old 2.2 code will remain available, but it will remain on v1.1 and will receive no further updates.

Some features have been updated or expanded upon. There are new features and some improvements to existing functions. The main improvements include:
  • Wandering line feature now has two more powerful functions to use.
  • Heightmaps have been improved, and a second heightmap option has been added.
  • Programmatic autotiling is now supported with jen_instantiate_autotiles.
  • Scale a grid to be larger or smaller using jen_grid_scale.
  • All data structures have getters and setters for more fine control.
  • All data structures have their own create and destroy functions--please use them!
  • Most functions have been rewritten and optimized to be more efficient.
  • Custom function parameters are reworked and are now much more powerful.
There are full update notes here: v2.0 Update Notes. Please check this document before updating, as some old code is no longer supported.
Also, the documentation has been fully updated, you can see the v2.0 Documentation here: Jen_scripts v2.0 Documentation.

Overall, this update includes many new powerful features, and the asset in total has over 60 different functions! I hope you find it useful.
There will be more updates over time--next, I will be working on noise functions, among other things.
 
Itchio Release (Now Available for FREE!)
Hey folks! Jen_scripts has got a new release over on itchio, and I have transitions support away from the Yoyogames Marketplace, at least for the time being. So you can now download Jen_scripts v2.0 as a Local Asset Package on itchio here: https://cloakedgames.itch.io/jen-scripts. Additionally, it is now available for FREE/pay-what-you-want. If you are financially able to support my game development, the recommended donation is $10.00.

For the time being, there are no changes for this release. Future updates will still probably come, either when there is enough interest expressed, or when I end up developing new features for my own use. Thanks for your support, and I hope that this new platform (and price-point) will get Jen_scripts into the hands of more developers!
 

Hamrath

Member
Hi,

I have a problem understanding the maze generator functions.

I use the following code based on your documentation

GML:
maze = jen_maze_prim(3, 3);
terrain = jen_maze_build(maze, obj_wall, 1, 1, 1, 1, 1, 1);
jen_grid_instantiate_layer(terrain, 0, 0, "Instances");
The displayed maze has a width of 7x7. If I do jen_maze_prim(19, 5) the maze displayed has a dimension of 39x11. Am I misinterpreting something? Can I only generate room based mazes, as in your examples?

Thanks,
Oliver
 
@Hamrath The width/height of the jen_maze refers to the number of rooms in the maze. The parameters in your build function determine the size of the walls/rooms, and thus the jen_grid output will be larger. If you want to generate just a maze and use it for something else, you can use jen_maze_get to get the connections of the maze for each room in the maze.
 

Hamrath

Member
@Cloaked Games Thanks! I might do it differently than I first thought and won't give the player the full numbers for maze generation, but let him choose between small, medium and large height and width. 5x5 mazes are not fun anyways. :D
 

Appsurd

Member
This asset is really great! :) The documentation is very extensive and clear. Can highly recommend this extension if you are looking into randomly generated dungeons/mazes/platformers/terraria-like games/etc.


I have a few minor remarks/suggestions:

1. GMS is complaining (under "Syntax errors") about functions having the wrong number of parameters. It has to do with optional arguments. Is there anything you can do to fix that?

2. I found a small bug in the function jen_maze_build_special, on line 583:
GML:
jen_grid_rotate(_temp, irandom(3));
This should be instead:
GML:
jen_grid_rotate(_temp, irandom_range(1,3));
If you use the WRONG code, you will get the following error
___________________________________________
############################################################################################
ERROR in
action number 1
of Create Event
for object obj_control_game:

Jen_scripts function jen_rotate requires a rotation number of 1, 2, or 3.
at gml_Script_jen_grid_rotate (line 193) - if (_rotations < 1 || _rotations > 3) { show_error("Jen_scripts function jen_rotate requires a rotation number of 1, 2, or 3.", false); exit; }
############################################################################################
gml_Script_jen_grid_rotate (line 193)
gml_Script_jen_maze_build_special (line 583) - jen_grid_rotate(_temp, irandom(3));//irandom_range(1,3));
gml_Object_obj_control_game_Create_0 (line 68) - terrain = jen_maze_build_special(maze, -1, 5, 5, 0, 0, 5, 5, global.list_U, global.list_L, global.list_I, global.list_T, global.list_O, 1);

3. There is a textual error in the documentation for jen_maze_build_special. In the example, you have
GML:
jen_grid_instantiate_layer(0, 0, 16, 16, "Instances");
which should be
GML:
jen_grid_instantiate_layer(terrain, 0, 0, "Instances");
4. The generation of mazes could benefit from having the option not to use objects but tiles instead. The thing is that the script jen_grid_instantiate_tiles does not work for mazes that are constructed using jen_maze_build_special, because they are made out of objects. Below you can see my approach to this problem. Admittedly, my script isn't very generic, because you need to manually edit this script while you add each object/tile combination. But perhaps you can use it as a starting point for an official way to solve this problem.

GML:
function jen_grid_instantiate_maze_tiles(_grid, _x1, _y1, _tm_wall, _tm_floor)
{
    //Getting width and height of the grid.
    var _width = jen_grid_width(_grid);
    var _height = jen_grid_height(_grid);
    
    // Loop over all visible cells
    for (var xx = 0; xx < _width; xx++) {
    for (var yy = 0; yy < _height; yy++)
    {
        // Get object index
        var index = jen_get(_grid, xx, yy);
        //show_debug_message(index);
        if (is_real(index) && index != noone && object_exists(index))
        {
            switch(index)
            {
                // Create wall
                case obj_place_wall:
                    tilemap_set(_tm_wall, 1, _x1 + xx, _y1 + yy); // number 1 corresponds to wall tile
                    break;
                
                // Create floor
                case obj_place_floor:
                    tilemap_set(_tm_floor, 2, _x1 + xx, _y1 + yy); // number 2 corresponds to floor tile
                    break;
                
                // Other instance, which is not possible. Create nothing.
                default:
                    break;
            }
        }
    } }
}

5. Perhaps it is nice to add a script for tile-based collisions? After all, most devs will want to use tiles as they are much faster than objects. Here is the code.
If you want to use tile-based collisions, it's best to place walls on a different tile layer than the other non-collision-based objects.
GML:
// Replaces place_meeting() for tiles 
function tile_meeting(_tile_x, _tile_y, _tm)
{
    var _x1 = tilemap_get_cell_x_at_pixel(_tm, bbox_left + (_tile_x - x), y),
        _y1 = tilemap_get_cell_y_at_pixel(_tm, x, bbox_top + (_tile_y - y)),
        _x2 = tilemap_get_cell_x_at_pixel(_tm, bbox_right + (_tile_x - x), y),
        _y2 = tilemap_get_cell_y_at_pixel(_tm, x, bbox_bottom + (_tile_y - y));

    for(var _x = _x1; _x <= _x2; _x++)
    {
        for(var _y = _y1; _y <= _y2; _y++)
        {
            if (tile_get_index(tilemap_get(_tm, _x, _y)) > 0)
            {
                return true;
            }
        }
    }
    return false;
}

function tile_meeting_xy(_tile_x, _tile_y, _tm)
{
    var _x1 = tilemap_get_cell_x_at_pixel(_tm, bbox_left + (_tile_x - x), y),
        _y1 = tilemap_get_cell_y_at_pixel(_tm, x, bbox_top + (_tile_y - y)),
        _x2 = tilemap_get_cell_x_at_pixel(_tm, bbox_right + (_tile_x - x), y),
        _y2 = tilemap_get_cell_y_at_pixel(_tm, x, bbox_bottom + (_tile_y - y));

    for(var _x = _x1; _x <= _x2; _x++)
    {
        for(var _y = _y1; _y <= _y2; _y++)
        {
            if (tile_get_index(tilemap_get(_tm, _x, _y)) > 0)
            {
                return [_x, _y];
            }
        }
    }
    return [-1, -1];
}
 
Thank you @Appsurd I appreciate your feedback!
1) I can fix that as of GMS 2.3.3 which improves optional parameters. Thank you for pointing it out.
2) I will fix that as well. I'll try to have a new version soon but I am busy with other projects so it may take a little bit.
3) Fixed.
4) Excellent idea. I will consider how to best implement that, no promises yet.
5) Collision handling is outside of the scope of Jen_scripts sorry.

EDIT: The fix for No. 2 is actually a tiny bit more complicated, as your code does not allow the room to remain un-rotated. The code should actually be:
GML:
var _rotations = irandom(3);
if (_rotations != 0) { jen_grid_rotate(_temp, _rotations); }
This bug was introduced when I added error checking to jen_grid_rotate. Before, inputting zero would do nothing.
 
I released a small update v2.0.1 which fixes the crash in jen_maze_build_special as well as optional parameters. Please note it requires GML 2.3.3+.

I have planned 6 more functions which will in part be useful to create mazes with tilemaps. They will be released whenever I get around to updating it--hopefully in the next month. I added a section at the bottom of the documentation which outlines my future plans if anyone wants more details.

@Appsurd You may consider converting between objects and tiles just using jen_replace. jen_maze_build_special should actually work fine with tiles if you gave it lists of jen_grids that had tiles. So you might just want to jen_replace the input jen_grids to use tiles instead of doing it when you instantiate it. I hope that makes sense...
 

Appsurd

Member
I released a small update v2.0.1 which fixes the crash in jen_maze_build_special as well as optional parameters. Please note it requires GML 2.3.3+.

I have planned 6 more functions which will in part be useful to create mazes with tilemaps. They will be released whenever I get around to updating it--hopefully in the next month. I added a section at the bottom of the documentation which outlines my future plans if anyone wants more details.

@Appsurd You may consider converting between objects and tiles just using jen_replace. jen_maze_build_special should actually work fine with tiles if you gave it lists of jen_grids that had tiles. So you might just want to jen_replace the input jen_grids to use tiles instead of doing it when you instantiate it. I hope that makes sense...
Thanks for your reply! Your suggestion definitely makes sense, but I managed to resolve it in the way that I described above. But anyway good to hear that it's possible to do it using the build-in functionality!
 
Top