Theseus: Return to the Labyrinth

I made a thing! Theseus: Return to the Labyrinth is a game that I, along with a few friends, made over the last two weeks for Pirate Software Game Jam #14. Playable now on itch, source code public on Gitlab.

The game features you playing as Theseus running around a procedurally generated maze leaving a line of thread as you go, attempting to map the maze out entirely. The Minotaur is also wandering angrily, and will try to follow the thread in order to get you. If you get caught your thread will be lost, but if you can make it back out of the maze your thread will persist and you can come back to continue exploring.


I made this with four other friends (including Aleks and Lia who I’ve been designing Hexcentric with). I was the most experienced with engineering and Unity in particular, so I got to do some tech-lead and architecture work. We also had some artists on the team, so we have non-programmer art which is nice. Much fun and much learning was had by all.

The two main things I built were the maze system and the Minotaur AI.

The maze turned out to be surprisingly easy at first, I implemented Eller’s algorithm fairly directly. There were some less-than computationally efficient shortcuts with data types, but it’s a pretty small maze so I could get away with it.

After the initial implementation, I started tweaking things. First, I relaxed the part of the algorithm that prevents loops; this was pretty trivial. If I had all the time in the world I would have liked to add options for controlling straight line hallway length and how interconnected different regions of the maze are. Alas, none of that makes sense for a game jam.

The second tweak I made was to enable regenerating a new maze for the next run. Again, trivial: just run the maze gen again. However, we also wanted to maintain the layout of the parts of the maze with thread in them. This turned out to be much thornier than I was expecting. The crux of the problem is in keeping the whole maze connected in some way, without accumulating so many connections around the persistent threads that after a few runs the threaded path is fully connected. After several rounds of tinkering, I made do with simply re-making any connections that had thread in the previous maze. This didn’t destroy connections, so it preserved the connected-ness of the initial maze generation. It also didn’t collect more connections over time unless the player laid more threads. Room for improvement, but it works.

This did however steal valuable time away from working on the Minotaur AI, which wasn’t functional until the last few days of the jam. This was unfortunate, because the Minotaur was the main area for balancing and tuning to make the game fun, and we had very little time to experiment. If I were to do this again, I wouldn’t touch maze regeneration until the Minotaur was behaving in a fun way. This would probably not doing maze regeneration at all, but that would have turned out better overall.

I do like the framework for the Minotaur though, and I have ideas for improving it. In short, there were a number of different “brains” for each behavior state. Each time the Minotaur moved, it would query the current brain for what move to make, as well as what brain to use next. Most of the time the brain would say to keep using the same brain, but under certain conditions it would give a different brain, to prompt a state change. This does mean manually implementing every state transition, but it suggests that state transitions can and should depend on what state they’re coming from. The Minotaur should be more ready to charge if he’s actively following the thread than if he’s just wandering, for example.

I made three states: Wander, Charge, and Thread.

Wander initially picked a random direction and went there, but this produced a lot of back and forth movement in very small areas. I changed it to pick a random position in the maze and walk there, which creatures much more directed behavior and keeps him from getting stuck in one part of the map.

Charge runs directly towards the player if they are visible, or towards the last seen player position if they break line of sight. This was intended to allow the Minotaur to charge past the player as they were escaping, but this needed a lot more tuning and possibly different player movement options for it to feel as aggressive as I would like it to be.

Thread was the most complicated. In this mode, the Minotaur will follow thread that he discovers on the ground, unless he has already followed that particular thread. This gets tricked up in loops, but not in quite the way that I wanted the Minotaur to be trickable. Version 2 of this would likely have some concept of “paths” that had been explored, rather than just individual thread tiles, so that he would be able to respond to dead ends of thread more intelligently.

There were also many systems I didn’t implement: the story system that plays between runs and the setup actually laying the threads was built by Aleks, player input, the animation system, and audio in general was entirely handled by Hunter, and Jens and Lia handled all the writing and art. It was certainly more than I could put together in a week, and I’m proud of what we came up with.

Previous
Previous

Bread

Next
Next

Physics Tooling