Recently, I wanted to learn to make generators that are able to create some small seamless worlds for a game. Long time ago I would think: how on earth people do that? Here's what I came up with now...
The generated map has some deep sea, large amount of green land with little lakes (shallow light blue water), differently sized seas here and there (deep dark blue water), some cold areas at north covered with snow and some beaches covered with sand on south. I think it's quite interesting terrain, forming one cool island 🙃
Purely accidentally I found a good lecture about PCG (Procedural Content Generation) from professor Dave Churchill, Memorial University of Newfoundland (Canada). I found him long time ago somewhere, he's quite famous for his work on AI for custom StarCraft bots. To my surprise, he released a video lecture about this PCG topic recently, right at the moment I was interested in it! Here's his lecture:
Procedural Content Generation by Dave Churchill (1h 22m)
Long story short, there are typically two ways to generate 2D maps for terrain:
- Cellular Automata
- Perlin Noise (most popular implementation of a more general Gradient Noise)
Both are quite interesting and very customizable, but the second one is quite harder to implement, therefore is harder to make edits. So I decided to use Cellular Automata.
Of course, there are much more knowledge requirements to implement all this, like you should be quite good with programming in overall, with tooling and stuff, otherwise this single video won't be enough to do the similar. But if you're programmer already and just want to make PCG generator, then this video should be a good starter.
There are some similar yet different steps:
- Generate Sea (blank 2D map of one default value called Sea)
- Generate Noise (random values throughout this 2D map which make little dots on this map of Sea)
- Generate Water (many little steps of Cellular Automata algorithm, making dots come gather into whole areas)
- Generate Weed (a whole another cycle, similar to previous, but for another type of a Tile, Weed)
- Generate Sand (yet another CA cycle, for Sand Tile)
- Generate Snow (another CA cycle again, for Snow Tile)
Here's a gif of all the generation steps (but Sand and Snow combined into one animation step):
You can notice that both the left and right edges form a single shape, making the island pretty much seamless on X-axis. In purely naive implementation it's not seamless, but with a little tweaking it's possible to make generator generate seamless maps fairly easy. It's more clear when you duplicate the image on the sides:
Looks almost like a surface of a miniature planet, huh?
TLDR: No, it's implemented in TypeScript currently.
From my previous article, Making Custom Game Engine, I said about my plans regarding AssemblyScript, but I've spent too many hours trying to make AssemblyScript modules work, using only the native WASM support by Node and browsers. I found out that AssemblyScript modules are little more complex than pure WASM modules, so it can't "just work" like normal WASM modules, by many many reasons:
- "Module not found" when exporting a class
- Default function arguments lead to unreachable runtime error
- Unable to use "this" in functions
- etc etc
Instead, you have to pass it through the AssemblyScript Loader first, to get rid of most of these errors.
My bad I started trying to make it work without the loader in the first place, but I had my reasons: it's not just additional initialization code I wouldn't like to see in a project, but I'm also unable to properly translate AssemblyScript types into TypeScript types with the loader (so anything coming from the module has any
type).
So I gave up and moved away from AssemblyScript for a while. I'm just a normal person, and after so much frustration with it I can't just look at this AssemblyScript, its loaders, etc. I decided to write my procedural generation with just TypeScript for now.
It is likely I'll recover from this frustration with AssemblyScript and will try it again someday, properly with the loader next time. It will also be a good test for AssemblyScript: how easy it is to rewrite existing TypeScript code into AssemblyScript, preserving all the types and functionality? We'll see.
This time I decided to make my article a bit more entertaining, so here's a tiny demo of this generator:
I plan to write about other parts of the game I'm making time to time, - there are huge amount of different areas! And I have many ideas particularly for this generator. I'm not sure how complex the generator will be in the end, but here are some other things it might (and will likely) include:
- Rocks and/or Mountains
- Trees
- Rivers
But for now...
That's it!