Converting Heartbeast’s Random Walk to Godot Part 1

One of my colleagues has been requesting that I make the switch to Godot from Gamemaker Studio to develop my games. One key point to our discussions has been about the video of Heartbeast saying that his favorite engine to use is Godot (also, you can use godot in Linux).

I have been reluctant to make the switch because I find the ease of use with Gamemaker Studio very appealing and my overall comfort level with it is high; however, I relented and agreed to try out Godot for the next game that I am working on. Depending on how well this goes I may switch over to Godot permanently.

If I am going to switch over, some of my commonly used tricks would need to come over with me, and one of the first things I would need to do is figure out how to rebuild Heartbeast‘s Drunken Walk algorithm in Godot.

For those who are not familiar with how this algorithm works, Heartbeast creates a DS_Grid, fills it with a void value, and then has a “controller” (just 2 variables that hold an x and y value) move through the DS_Grid, randomly, and assign the value of floor to its coordinates. For this conversion to be considered successful I will need to match this functionality. So, lets open up a new Godot project and see what we can do.

By default the Godot editor will be in 3d, you can switch over to 2d by clicking on 2D at the top of the editor. The png file, that we want to use for our tileset, can be dragged into the asset library at the bottom left of the screen. Once your editor looks like mine, then we are able to proceed. (If you don’t want to make your own sprites, you can download the sprites I will be using here.)

Next we will need to add a node to the scene. Since our scene is a 2d scene, it makes sense to add a 2d node. After your root node has been added we can add a tilemap as a child to the root node. The tilemap is a new feature to Godot and it will provide us the same functionality that the DS_Grid does in Gamemaker Studio, and a little bit more.

Once the tilemap has been added to the scene, we can edit it with the inspector, on the right hand side of the screen, while it is selected. A new tilemap will start with the tileset field being empty, by clicking on the drop down menu we can create a new tileset, and we can edit the tileset by clicking on the drop down menu again (I found this part confusing at first).

Once you click edit tile map, a new window will be displayed at the bottom of the screen. With this window we can assign a texture (sprite) to the tileset and after our texture has been assigned we can select which part of the sprite represents what tile by clicking on “New Single Tile”.

A lot more work in the editor than I am used to in GM:S

After our tiles have been created, we can specify which tiles will be used for collision. My sprite has 3 tiles, a void, ground and wall, so I assigned collision to void and wall, you can do whatever your project requires. We also need to set the tile size in the inspector, my tiles are 32 * 32. Don’t forget to save your tilemap by clicking the save button in the inspector at the top right hand side.

Here is the full process of building the tileset.

This is most of the work that we need to finish in the editor, the rest of the work is going to be coding in Gdscript. To attach a script to the tilemap we can right click the tilemap and select “Attach Script”. After the script has been attached, the editor will open and we will be ready to do some coding.

Finally some coding

The script that was created comes with 2 pre-built functions, the _ready() function and the _process(delta) function. These are comparable to the create and step events from Gamemaker Studio. Most of our work is going to be inside of the _ready() function, but before we do that we need to write some code at the very top of the script.

Like in Heartbeast’s script we first create some constants to reference the tiles in our tile map. Your values may be different than mine depending on the order that you created them. I made mine in order from left to right on the sprite so 0, 1, 2 will be void, ground and wall respectively. We can also use a line of code to add a field to the editor that allows us to set how many tiles will be in the room. Instead of having a separate x and y values, like in Gamemaker, we can just use a Vector2.

export (Vector2) var room_size;
const VOID = 0;
const GROUND = 1;
const WALL = 2;

In the _ready() function we can create our controller and have it start in the middle of the tilemap. We also need to get our first random direction, and set how frequently it will change direction. Like the room size, our controller can also be a Vector2, the direction can still be an int from 0-3 and the odds can still be an int from 0-1 giving us a 50% chance of changing directions. You will also need to call the randomize function so that your map gets a new seed each time it executes.

randomize();
var controller = room_size / 2;
var direction = randi() % 4;
var odds = randi() % 2;

The final part of this script is to use a loop to move the controller in a random direction. This looks pretty similar to how it is done with Gamemaker Studio. We create an if statement to check if we need to switch direction, we multiply the direction by 90 to give us a value in degrees, from 0-360, and then move the cursor in our random direction. We are also going to clamp the controller so that it doesn’t go outside of the tilemap.

for i in 400:
     set_cellv(controller, GROUND);
     var vdir = Vector2.RIGHT.rotated(deg2rad(direction * 90));
     if odds == 1:
          direction = randi() % 4;
     controller += vdir;
     controller.x = clamp(controller.x, 0, room_size.x - 1);
     controller.y = clamp(controller.y, 0, room_size.y - 1);
     odds = randi() % 2;

With our coding done we can go back to the editor and set how large the room will be in tiles with the room_size variable that will now display. The sprites are 32 by 32 so we can make the cell size 32 by 32 and we can make the room size 32 by 32. This gives our game a width and height of 32 cells and each cell is 32 by 32 pixels.

The finished inspector of the tilemap

Before you run the game you need to make sure that your camera is big enough to see the whole map (this tutorial doesn’t cover making a player or a moving camera). To adjust the map size you can click on Project -> Project Settings -> Display -> Window and adjust the resolution so that it will fit your whole map (1024 * 1024). Once that is finished you can run the scene and you should hopefully see something like this.

I have a Drunken Walk map

Because this is being done in Godot, and GDScript isn’t as broken as GML, we can copy the contents of the _ready() function and make our own function called _generate_map() and inside of _process(delta) we can clear the tilemap and generate a new map with a keypress and you can see the different maps you can get.

if (Input.is_action_pressed("ui_select")):
     clear();
     _generate_map();

Changing the odds variable will give us different kinds of maps. Having the odds variable as odds = randi() % 2; gives us a value between 0 and 1, giving us a 50/50 chance of changing direction. Which results in maps that have larger rooms. Lowering your odds will result in maps with longer and longer hallways and less rooms. Here is an example with odds = randi() % 5 (you need to change your if statement to if odds == 4: for this to work)

The script isn’t 100% finished yet. Walls are not yet being spawned in, and there are some other missing features, but this blog post / tutorial is already over 1000 words, so I suppose I will have to come back and write another post to finish the script.

I would like to thank Rkoopah for helping me out while I am learning how to use Godot.

You can click here to read part 2
You can click here to read part 3

Until Next Time
– Steven