‘self’ and variable scoping:
Check out this thread: Explanation of the "self"argument for dummies
Also make sure you’ve read: Locals, globals, and lexical scoping.
In this case I could have made self.stepCount and self.stepFunc as local variables in the base level and it would work just fine. Putting them in self just means you could run multiple instances of this script and they wouldn’t interfere with one another.
self.tiles.data
self.tiles is mainly my 2D array with the data for each tile. Since I’m only using number keys for the tile grid, I decided I might as well throw some extra stuff in there. In this case, the URL of the tilemap component. You need that URL to use the tilemap.set_tile function. This way, all the stuff you need is in one place, rather than hard-coding “#tilemap” or storing it in a separate variable somewhere. The only place I actually use it is in my own “set_tile” function. In general, keeping your pieces of code as self-contained as possible will keep it more organized and help prevent weird bugs.
Y before X
This is 99% a personal preference. I sort of mentioned it before. It means your table will have Y as the “row” and X as the “column”. The only thing it actually affects is the direction you will loop over the table. If you put X first then it will loop from left-to-right instead of bottom-to-top (with the same code, but you can change how the loop works if you want).
There’s lots of articles around the internets about procedural generation, and many of them are not language or engine-specific. A lot of the terrain ones are talking about 3D terrain, but the same methods will work for 2D terrain, just a difference in the final, graphical output.