How I Created my Mazes for MAZES
Today I'd like to talk a bit about some of the things I experimented with when creating my levels for MAZES.
I'll say right away that development on this week's game has not gone as smoothly as last week's game. Whereas last week I always felt perfectly on schedule, I definitely feel a bit behind on this week's game. Part of that was due to the time spent re-acquainting myself with GameMaker after a while away from the engine -- and part of that was due to a lot of time spent learning and experimenting with some of the things I'll talk about next. The next 24 hours will certainly be busy if I plan to get this game across the finish line before Sunday night!
One of the areas of development I've always been interested in is procedural content generation -- using an algorithm to create the levels in a game rather than crafting them by hand. This is especially relevant for me in terms of this particular game. One of the biggest problems with being the creator of a maze is that you can never really have the experience of playing it yourself without knowing the solution -- since you're the one who actually put together the maze, you know the layout intimately, you know all the tricks. It's a true creator's burden to bear. For that reason, I was very attracted to the idea of somehow implementing procedural maze generation into this game so I could sit down and play my own game without knowing all the solutions beforehand!
There is a ton of content available on how to do procedural generation for a variety of game types -- it would be impossible to learn all the important concepts in my limited time frame. But I thought this could be a good project to at least introduce myself to the key ideas and try to get something very rudimentary up and running.
At first, I thought it would be fun to try and map out my own maze generation algorithm before I spent too much time researching. At first, what I thought I could do is basically create a connected graph in GameMaker, and then replace each node in that graph with a "room" of pre-assembled game content. This turned out to be a bit more difficult in GameMaker than I had anticipated -- in Unity, you can create "prefabs", which are little collections of pre-arranged content that you can treat as one object. Each prefab would be like a lego block that I created -- but the overall arrangement of those blocks would be procedurally generated, stitching together the overall maze layout.
However, as far as I can tell, there is no good prefab analog in GameMaker. I still thought this was the best way to go, though, so rather than changing my approach too much, I decided to create my own little prefab system. I basically had an object in GameMaker represent a room, and I used numbers and other symbols to tell that object what kind of room it should look like. I then implemented the logic to convert those numbers into in-game floors and keys and open spaces, which would draw at the object's current location. In the example below, each "1" gets translated into a solid block, and each "_" will just be treated as empty space.
So now if I go ahead and create a bunch of rooms with just this one template, I get an overall construction that looks something like the image below. Obviously this isn't really a maze yet, but I thought that I could craft some logic to put these rooms together in a way that actually made it tricky to navigate.
I thought this was a good starting point for now -- I could always come back and craft a bunch more room templates later. Next, I set out to create my graph. I made some basic code to help me visualize what the graph would look like. First, I'd pick a starting point, and that starting point would know which node were above, below, to the right, and to the left of itself. Each green dot in the image represents a room of the maze, and the adjacent rooms to the starting position are color-coded.
I then explored outward, randomly choosing one of these adjacent edges and creating a path to it. I would then repeat that process for the new outermost edge and continue exploring. In the end, this process created a randomly shaped connected graph something like this:
Okay, so this at least gives me a valid path to follow. The maze is obviously not very complicated, but now I think I can go back and choose some random points along this path to branch off and repeat the process -- as long as I don't let my algorithm visit too many previously-visited nodes and thus make too many of the paths cross-connected, this will create a network of discrete pathways to explore.
So at this point, I had some kind of basic framework for creating levels -- they still weren't very good, but I had the codebase in place. At this point I wanted to do a bit of research to see what sort of articles I might find describing work that other people have done on this kind of stuff -- maybe I'd find a few simple tricks to improve my maze complexity. Maze generation struck me as an area that was probably really well-explored -- and as I started to look I easily found a ton of great content.
One of the articles I saw early in my search was this one by Lucas Nazato. It provides a great outline of how to do maze generation in a more intelligent way than I did, and it links to some sample code. I'm still in the process of playing around with this tool a little bit to see what kind of results I could get -- the maze generation functionality was great, but it was a bit tricky to integrate this with my new prefab system without changing things too much.
One of the sources referenced in this article was a Youtube series by a creator called Heartbeast -- I wanted to call out his work in particular, because it was Heartbeast's tutorials that first showed me how to use GmaeMaker. He does an exceptional job creating content for beginner GameMaker users -- if you are at all inclined to learn more about developing games with GameMaker, Heartbeast's tutorials are must-watch. You can find his content on Youtube here.
Aside from all the time I spent trying to create my mazes, there were also some other gameplay elements I wanted to implement -- I spent some time creating locked doors which would require the player to find the corresponding key to open.
I also spent some time creating some basic enemies -- I remember back in my old maze books, I usually had my enemies represented by basic geometric shapes. Usually, the more sides they had, the more dangerous they were. Here's a sneak peek at the "square" enemy:
And that's where I am at as of right now! I haven't quite figured out how I'm going to integrate all of my maze generation ideas into a finished product just yet. I'm getting pretty short on time, so I may have to make a judgment call and just create some hand-crafted mazes if I can't get my procedural generation working well by the end of the day. All this procedural generation stuff was fun and interesting, but it might have to wait until another day for me to fully realize its potential in this game if I can't get some good results pretty soon.
All right! Still lots of work to do before I can call this game finished. Wish me luck!