The story of Apanga
Early in 2014, I started building a game of epic scale called Apanga. It’s not much of an exaggeration to say that I started building a new world. But I didn’t finish. I made mistakes along the way and, after two years of solid work, I made the painful decision to stop building it. I’ve decided to record a few of the good and bad things that happened along the way. This post is about some things that went well; in the next post, I plan to write about the things I’ve learned.
The vision
I love fictional fantasy stories and worlds because, made well, they’re places you can get lost in, places that can amaze you. If you like Game of Thrones or The Lord of the Rings, then you know what I’m talking about. I get excited about Elder Scrolls games and maps like this:
I also love Minecraft because it offers a nice balance between creativity, exploration, and cooperative play. My vision was to combine these elements into a single game.
The theme was procedural generation across the board. I envisioned a single, massive world where players could own land and control the edit permissions in a unix-like system so that their friends could play with them without the fear of griefing.
I planned to procedurally generate quests so that if any one player completed a quest, then it was considered done for all players. By procedurally generating them, this would be scalable. By making every consequence in the game permanent and visible to all players, the game world became more real.
If procedural generation across game systems was the technical theme, then the theme for players was a living world. I wanted to create non-player characters, inspired by the inhabitants of Dwarf Fortress, who had dynamic relationships with other characters, countries, or groups throughout the game world. I wanted them to feel more real than the characters in most games. I wanted to give them the freedom to make their own decisions as much as possible within the loose constraint of keeping the player’s narrative engaging.
Yes, the vision was daunting. In retrospect, it was too much for a single, unfunded developer. However, even in retrospect, I do believe it’s a large but reasonable scope for an indie game. Now that I have two years of focused experience on this vision, I think it is quite realistic for a small, talented team with a little funding. In fact, I’ve been seeing some incredible indie games being built along similar themes, including ECO, Moonman, and Stellar Overload. Voxel Quest is another great but unfinished indie game that is sadly on hold due to a vast scope and limited resources.
The tech stack
I started developing the game to be playable on Windows and macOS, and to run with servers on linux boxes hosted by EC2. This meant developing across three platforms. I used cross-platform languages to make this easier. I estimate that maintaining the code across 3 OS’s required around 5% of my overall development time on a sustained basis after I spent a good chunk of time optimizing my dev cycle to account for all platforms.
I started writing in straight C. I could write multiple posts about this decision — mainly that it has more benefits than you’d expect. Some coders might think it’s crazy, but C is easier to work with than many other languages — most notably its more onerous sibling, C++. It’s also perhaps the most cross-platform language available.
However, the bottleneck on my coding time became work on conceptually minuscule issues, which can happen in C. I chose to integrate Lua because its just-in-time compiler implementation is fast, it plays well with C, and it’s a beautiful high-level language. If you’re curious about Lua, I’ve written a few tutorials for it. After that, my time bottleneck was usually a combination of self-education and struggling with OpenGL. I could write multiple posts about what’s wrong with OpenGL.
Along the way, I wrote over 28,000 unique lines of code. That works out to just under 300 lines of code per week. I’m proud of that number because I worked in terms of functionality and put a great deal of effort — with good reward, I think — into keeping those numbers small.
I was not happy with most of the third-party libraries I came across. I like small, clean, independent interfaces. So I built many fundamental libraries. I built pure-C container classes; a json encoder and decoder; a general LRU cache library; an asynchronous tcp/udp library; a cross-platform library for loading images, rendering fonts, and playing audio; an actor-model framework for multithreading in C. Each of those libraries is open source, works on Linux, Windows, and macOS, and unit tested. I built the unit test framework I use within C for each of those. I also built a low-level C wrapper system to avoid and help debug memory and thread errors, as well as my own version of a package manager for C libraries.
Those are the libraries that went directly into Apanga. There was no game engine. As somewhat related side projects, I also built a procedural tree generator, a linear algebra optimization library, and a simple text editor. To be honest, the text editor was just for fun.
I’m listing all of these subprojects with mixed feelings. I’m proud of how much I accomplished. I feel defensive of the inevitable “why not just —” questions I anticipate from other developers. Most of all, I feel sad that all of this work has ended up being seen by almost no one at all.
The game
The final state of the game looks a lot like a low-quality Minecraft clone. To be clear, I did not want Apanga to feel like Minecraft, although I did want to reuse a system of large editable blocks as a basis. The fact that the game is not currently well-differentiated is one of the main lessons I plan to discuss in a follow-up post.
One of the first steps I took was to implement a Perlin noise generator and use that the create a very rough landscape. This was the beginning of world’s procedural generation:
Early on, I had to figure out how to best map textures to the basic building blocks of the world:
Each block was associated with several different coordinate perspectives. The most intuitive were the north/east/west/south/up/down directions, but there were also world and texture coordinates. Here’s a real-world block I hacked together to help me reason about the code:
Here’s a short gif of walking around in the game:
I had always planned to work with an artist on the textures. Not one of my strengths.
Here’s a short demo of world editing:
Those changes are sent in real-time to the server and broadcast so that other players see the edits simultaneously.
Next is a visualization of the local walking grid. It’s usually only tracked internally, and is used to help the AI-controlled characters move intelligently:
This graph would be used by, for an example, an evil bunny that follows you around at one point:
The bunny is intelligent enough to walk up windy stairs or around walls, but it tries to avoid cheating by not being able to use the player’s location unless the player is nearby and visible. Visibility detection uses a ray-marching algorithm executed along the bunny’s line of sight.
For fun, here’s a small collection of buggy or used-in-debugging screenshots that I took during the development of Apanga:
One of my favorite Apanga experiences was when my then-two-year-old daughter asked me if she could play “the tree game.” It was a completely unprovoked question, and I was surprised that she remembered it. She hadn’t yet seen the bunny in the game, and when she figured out that it was chasing us, she squealed with delight. That moment of heartfelt joy was the best feedback I’ve ever received on Apanga. I still feel great when I remember it.