ProcGen in Defold?(SOLVED)

Hey guys,
Just wanted to know about how do we do procedural generation in Defold? I saw @britzl 's OneRoom, but the code was quite over my head, so I thought that what could be better than the community to get it under my head :stuck_out_tongue: .
Thnx in advance guys.

1 Like

What kind of procedural content generation do you need? Random maps? If maps, then dungeons with corridors and rooms or outdoor environments? Or maybe procedurally generated item names and characters? Or random quests? Proc gen is a huge subject and depending on what you want to do there are many different techniques and algorithms to use. Applying them to Defold is not really that hard, but Iā€™m happy to help if you let me know a bit more what youā€™re looking for.

1 Like

I am looking for exactly this. So I have a tileset which I want to use to create many and many random maps, of random shape and size. I had read on the web, about proc gen being used in many games to generate many maps,and I saw it in action in OneRoom so, I decided that it may be perfect for my current situation.

Hereā€™s a super simple random dungeon generator:

local function fill(tile, x, y, w, h)
	for xi = x, x + w - 1 do
		for yi = y, y + h - 1 do
			tilemap.set_tile("#dungeon", "level", xi, yi, tile)
		end
	end
end

local DOOR = 40
local FLOOR = 16
local WALL = 2

local divide_horisontal
local divide_vertical
divide_horisontal = function(x, y, w, h)
	local ratio = math.random(2, 8) / 10
	local w1 = math.floor(w * ratio)
	local w2 = w - 1 - w1
	if w1 >= 2 and w2 >= 2 then
		fill(FLOOR, x, y, w1, h) -- left area
		fill(WALL, x + w1, y, 1, h) -- separator wall
		fill(FLOOR, x + w1 + 1, y, w2, h) -- right area

		divide_vertical(x, y, w1, h)
		divide_vertical(x + w1 + 1, y, w2, h)
		
		local dx = x + w1
		local dy = math.random(y, y + h - 1)
		fill(DOOR, dx, dy, 1, 1) -- door
		fill(FLOOR, dx - 1, dy, 1, 1) -- floor next to door
		fill(FLOOR, dx + 1, dy, 1, 1) -- floor next to door
	end
end

divide_vertical = function(x, y, w, h)
	local ratio = math.random(2, 8) / 10
	local h1 = math.floor(h * ratio)
	local h2 = h - 1 - h1
	if h1 >= 2 and h2 >= 2 then
		fill(FLOOR, x, y, w, h1) -- bottom area
		fill(WALL, x, y + h1, w, 1) -- separator wall
		fill(FLOOR, x, y + h1 + 1, w, h2) -- top area
		
		divide_horisontal(x, y, w, h1)
		divide_horisontal(x, y + h1 + 1, w, h2)

		local dx = math.random(x, x + w - 1)
		local dy = y + h1
		fill(DOOR, dx, dy, 1, 1) -- door
		fill(FLOOR, dx, dy - 1, 1, 1) -- floor next to door
		fill(FLOOR, dx, dy + 1, 1, 1) -- floor next to door
	end
end

function init(self)
	math.randomseed(os.time()) math.random()
	local x,y,w,h = tilemap.get_bounds("#dungeon")
	fill(WALL, x, y, w, h)
	divide_horisontal(x + 1, y + 1, w - 2, h - 2)
end

Itā€™s based on the idea that you repeatedly, recursively and randomly divide a rectangular area either horizontally or vertically until you canā€™t divide it anymore. The divided areas are filled with floor tiles and you draw a one tile thick wall between the divided areas. You also randomly place a door in each created wall. It produces results such as this (excuse the crappy tileset):

Getting a result like the ones above are easy and there are a ton of different algorithms that generate better results as well. The tricky part is making the maps interesting. How do you place items and monsters? How do you make the map visually pleasing?

I read an interesting article about an algorithm for map decoration that helped with turning the above map into something a bit more varied (I canā€™t remember where I read it thoughā€¦). It was a rule/pattern based algorithm that identified areas on the map where tiles should be decorated in different ways. It turned isolated wall tiles into pillars and it added spider web to room corner tiles.

10 Likes

This is my favorite method for generating ā€œdungeonā€ maps:

Level%20Gen_4

The script basically ā€œwalksā€ randomly around a grid placing floor tiles with wall tiles around them. Itā€™s simple, you never have to worry about inaccessible areas, and itā€™s easy to tweak with whatever parameters you want to get your preferred dungeon style, from natural cave shapes to more traditional boxy dungeons, to whatever.

I got it from an article by Vlambeer (itā€™s what they did with Nuclear Throne). Their old blog is down now, but luckily I saved a copy of the page where they explain in more detail how it works:

Wasteland King Level Generation.zip (1.5 MB)

15 Likes

Many thanks @britzl and @ross.grams. I used to think that proc gen is a really tough thing, and (almost) impossible to achieve to achieve for learners, and you two changed my mind.
One question though, which erupted in my mind recently - is proc gen affordable on mobile devices?

Procedural generation is not a new concept - it was actually used by early games to get around memory limitations. Itā€™s much more efficient to generate the game world from a seed (with the added benefit of it being possibly random across playthroughs) than to create the maps yourself and have to store all that data.

So no, you shouldnā€™t have a problem on mobile devices, as long as you donā€™t try to generate something ridiculously large and complex.

Edit: For instance, The Elder Scrolls: Daggerfall released 1996 had a procedurally generated map that spanned over 160 000 kmĀ². Hereā€™s a comparison of different well-known large game worlds. Daggerfall pretty much dwarfs them all.

4 Likes

So, that clears my another doubt. many thanks @Klear
BTW @ross.grams I tried to implement the nuclear throne concept today, but failed miserably. Maybe it was because I failed in understanding what algorithm was I to obey. Can you help me with this in more detail?

Sure! (And Iā€™m sure others will have helpful feedback too.) How are you trying to do it and what problems did you have? Posting your script or a zip of the project would be good.

I havenā€™t read the article, but based on the sentence ā€œwalks randomlyā€ itā€™s clear that the algorithm is quite simple. The basic idea is this:

  1. Fill the map with wall tiles
  2. Pick a position on the map, maybe the tile in the centre of the map
  3. Set the tile to a floor tile
  4. Pick a neighbouring tile at random, probably only orthogonally
  5. Repeat from steps 3 and 4 until you move outside the bounds of the map or after a certain number of iterations

Hereā€™s a live demo: https://britzl.github.io/PCG/ (space to regenerate)
And the code: https://github.com/britzl/defold-pcg/blob/master/randomwalk/randomwalk.script

5 Likes

Itā€™s still nice to see Just Cause in the map comparisons :slight_smile:
It has been the same size (1024km2) from JC1 to JC3 (and probably the next one as well).

The main problem was never to create large worlds, it was to fill them with interesting content. (1024km2 is a big space!).

We built a world proc-gen tool to create villages and road networks in a believable fashion, but we never really got very good results with that, so most of that was hand placed content in the end. We randomized simpler things like road decorations (lamp posts, fences etc), garbage cans, and some biggere entities (e.g. houses). And for the terrain, we used terrain materials/brushes to randomize where the dirt/grass/etc textures should be, and what type of grass/stones/trees/etc should be placed.

I digressā€¦ sorry ^^

What I wanted to mention was to look for procedural dungeon generator, and also roguelike/map. There are tons or variations out there.
Hereā€™s one result with a long list of generators.

10 Likes

@britzl Thanks for this superb example. I was thinking of giving it another try today, but armed with this, I think now I can finish my game first and then give a huge amount of time in making my own and learning . BTW the problem which I was mentioning was that how do you know which wall tile should come next to floor time.
Let me make it more clear. The generator is easily tweakable For top down games yes, it is perfect, but for isometric games, for example the tileset I mentioned before, how do I know where should I place the diagonal wall tile and where the flat ones?

The tileset you linked isnā€™t isometric, but it uses this kind of tilted perspective where you see the side of north facing walls (Iā€™m sure thereā€™s a name for this). Anyway, you need to find a way to translate your 2D grid of wall and floor data into tilemap data that corresponds to the look of your tileset. Navigation wise thereā€™s no difference. The north facing walls where you see the wall sides are still walls right?

My previous suggestion of a decorator function that inspects the top down wall and floor grid and translates that into the look of your tileset is still the way to go to be honest. You do this by iterating over each x,y position of your map and for each tile you also check neighbouring tiles and based on wether they are walls or floor you decide which tile to use from your tileset.

I did some additional work on my PCG project yesterday evening:

Demo: https://britzl.github.io/PCG/ (space to regenerate)
Code: https://github.com/britzl/defold-pcg (generic map and decorator)
Tileset: https://scut.itch.io/7drl-tileset-2018

The code needs a bit of cleaning up and some comments, but perhaps you can get inspired by it when you create your own solution.

5 Likes

Thank you @britzl, over the past week, I have been trying to adapt to what procgen really works in scripts using your examples and now I think I have been to understand it. Thanks again for your valuable help @britzl and @ross.grams

Happy to hear that the examples and suggestions helped. If you need more help just ask! Itā€™s quite fun to play around with and Iā€™d be happy to create some more demos.

1 Like

Finally, gave this another try this week, and was finally able to implement my version of randomwalk algorithm. A bit slow, but caters to my need. Thanks again @britzl and @ross.grams. Yet there is a big logical error in here, and whatever I do I cant seem to be able to tackle it. Printing and debugging doesnt work, and Iā€™m out at sea for this. Any ideas how can I counter it?
BTW here is my creation :slight_smile:

3 Likes

. . . What is the error?

2 Likes

ā€¦Umm the error is this
The red outlined floors are meant to be spawned beneath a floor but its not so, the get3x3() function in the sample project doesnā€™t work :cry: :
have a look here


Here is a sample project with all my proc gen scripts(space to regenerate.) (it takes a few regeneration to show the error)procgen.zip (334.9 KB)

Thanks in advance for your valuable helpā€¦

OK. So the problem is in map.lua, in the create_walls() function (no big surprise there :slight_smile: ). You just have a bunch of extra conditions that you arenā€™t using and donā€™t need. Most of them were commented out, but one was still adding walls, and that was the problem. Line 162. It looks like you were halfway through making some new features and just forgot to disable that code.

1 Like

This is a textbook case for using the debugger to step through the code to find the error! @TheKing0x9 you should really familiarize yourself with the debugger!

2 Likes