I’ve done (and won) NaNoWriMo 8 years, including most recently in 2017, but I’m not doing it this year. It’s harder to prioritize it when I know I can do it (including 100k+ words in 24 days in 2011), so I decided to take the spirit on NaNo and apply it somewhere else.
So, given that the point of NaNoWriMo is to write that novel you never quite get around to writing, I decided to work on a game idea I’ve had bouncing around in my head. My goal it to get at least a playable prototype done. The graphics won’t look good and only the basic mechanics will be there.
I loved the Command & Conquer series of games, and always found myself more interested in building bases than the combat. And Creeper World 3 was mostly about building bases to take out the creeper. But they’re always on limited maps.
So what I want in a game is some sort of base-builder/RTS/Tower Defence hybrid. Right now I’ve got some general ideas, including a science fiction setting, and that I want it to be hex based.
I’ve got a post that I’ll update as game mechanics evolve, but it’s mostly notes and thoughts rather than a full-fledged design doc right now. Find it: here.
Part of the spirit of NaNo is that public word count, so I’m going to publicly document my progress on this blog. Included at the end of this post is links to each day’s update, which I’ll try to keep doing.
Another relatively slow day, programming wise. I did get a first pass of unit movement working. It’s janky and needs more work, but a unit now follows the path set out for it. I think it’d be nice if it followed the path somewhat smoothly rather than teleporting between hexes, but that’s certainly not needed to get a game working.
There weren’t many examples for how to do move a sprite along a path in cocos2d, so I resorted to looking at the source code for how things are done elsewhere. I ended up subclassing the Action class, and implementing the step() function. I’m still not sure how to make the action stop running when I’ve exhausted the path, but it’s a good start. I definitely don’t want to leave the action running forever in the background.
I worked more on unit movement today. I can now actually make a unit cover the distance between the start and end hex cell, but I haven’t figured out how to make it follow the path I spent all of that time figuring out. Currently, I’m using cocos2d’s MoveBy action to move by a certain amount, and this is certainly the correct start, but I can’t figure out how to do what is essentially enqueuing multiple moves taking place one after another, so that a unit can follow the path between hexes, but smoothly move hex-to-hex.
Cocos2d may have support for moving a sprite along an arbitrary paths, and it might for Bezier curves, but the documentation isn’t very clear on it, other than it’s possible existence. I’ll look into how cocos2d supports some of its actions, potentially I need to subclass it and define my own step() function to do one step of my path.
Unfortunately, not much to show for today. I spent too much time troubleshooting my A* implementation, and then finally realized that it was all correct except that I was using Python’s PriorityQueue wrong.
The next step will be using cocos2d’s movement facilities to move the sprite for a unit along the path A* generated. At some point I’ll need to add in the new terrain generation algorithms, so certain units have something interesting to drive around.
I started work on basic unit movement, as a start to adding in enemy creeps that defend against. Initially, I’ve made a unit which I’ll call a “hover tank” for now. Movement is simple, it teleports instantly, but I’m going to look into cocos2d’s movement and figure out how to actually have it move over the terrain.
Right now, units can be placed on top of networks, but not buildings. This seems reasonable. When it comes time to have them move properly, I may need to figure out how to deal with this. Perhaps all units can be air units and fly over buildings. The other option is to pathfind around buildings, but I was probably going to have units cover the distance directly from the start hex to the end hex. Or I may decide to have it hex pathfind and move hex-by-hex. This won’t use the cocos2d movement system, but it might actually be stylistically better.
Units have a vision area around them, but I may also want to have a safe area around a unit to keep enemy creeps from spawning. Like many things, this will need testing to see what’s fun.
After having gotten basic OpenSimplex noise working, I started working to make it more like real terrain on the scale I was looking at. This involved taking integer x/y coordinates, which are based off of the hex’s offset coordinates rather than cubical coordinates I use everywhere else, and then some factor apart that’s less than 1. I’m calling this a damping factor, because it’s damping how often the noise is sampled. Maybe this is a bad name, but naming things is hard.
Below, I included 4 images showing different damping factors. I’ll need to play around with this a bit more, but I think at the scale, something around 0.05 is going to be the right amount. I’m also going to need to make sure city cores (especially the start core) aren’t drawn underwater, because I don’t intend building to be able to take place underwater. I’ll also probably tweak my transitions between the different terrain hex sprites. I’d like this all to be configurable at some point, and the settings are exposed in the settings.py file right now.
I also fixed fog-of war drawing. I’d accidentally put the the actual draw call inside my for loop, so it was drawing every hex one at a time, rather than a all together in one batch. And once that was fixed, I could move on to further implementing it. Buildings can’t be built under the fog of war, and things that are under the fog of war lose their information. There are a couple minor bugs with redrawing things under the fog.
I’m not sure I’m going to keep this. Protection towers still run and protect the area if they’re being supplied energy, but other than seeing the protected area disappear, there’s no other way to know if they’re working. I might also go the other way and hide the safe area, or not allow it to update its status in fog-of-war areas. It’ll depend on what’s actually fun to play.
I got the new form of chunk generation working in my terrain-test branch. I’m probably going to bring this over into the main branch, but what I wanted to work on was the actual terrain generation. The first chunk generated (hexagon coordinates q=0, r=0, s=0) for it’s “center” or anchor will always have the starting city core at it, but I also want other city cores spread across the map. At what distances apart they spawn will need to be determined experimentally by playing, and if I add difficulty settings, they could affect this distance calculation.
The first thing I tackled was enemy core generation, and my first pass at an algorithm showed the I need to make things more random. I’m using a hash function with a seed appended to it, but the results were a little too deterministic for my liking.
This is exactly why I wanted to create something to give me a larger view of the terrain map, because I might have missed this regularity otherwise.
I’m using Python’s hash function on a tuple containing the hexagon coordinates (q, r, s + seed) and then checking if a hex should maybe have a core added to it if the hash is % some number. Obviously this isn’t working yet, but it’s a fine first pass.
I also started working on terrain hex generation. Ideally I’d like blue hexes (water) to cluster together a bit to give water bodies, and the other colours to have some other nice distribution. The first pass uses OpenSimplex to generate the noise, due to Perlin noise having some artifacts, and then binning the values directly to sprites. I also spent too much time reading about procedural terrain generation and not programming.
Again, the first pass here was less of what I was looking for, but it at least works and I can tweak the normalization function until I’ve got something better to look at with the correct proportions of water hexes. I do plan on having water be a barrier of movement to certain units, and stop building of normal networks.
I didn’t make very much progress today, especially not as much as I’d hoped. I needed some offline time, so less development happened.
I’d like an overview of a much larger map to test terrain generation algorithms on, so I decided to make something that could show me much larger maps. Cocos2d is just too slow with this many sprites being displayed, so I decided to write a simpler version using PIL (actually Pillow).
Because I wanted to keep the chunk size the same, but have a much larger viewport, my chunk generation code doesn’t work properly anymore, it generates the 9 chunks centered in the middle and that’s it. Getting chunk generation to fill the a much larger took more work than I’d anticipated due to bugs in my existing chunk generation, which I’d have needed to fix if I end up wanting a full screen view, rather than a windowed view.
So I experimented with hex tiling, finding one that worked really nicely, but means that the center hex is no longer in the center of the chunk. If I decide to go forward with this, I might start indexing the hexes based off of their top left hex.
Today sort of felt like a bit of Yak Shaving, after making good progress on other areas. But I want the terrain to be more than a call to randint(), and getting the chunks nailed down will allow me to move on to proper procedural generation of the terrain.
At the end of the day, I hadn’t even got the drawing working for this properly. Chunk generation wasn’t working properly, so it was commented out. But at least I could draw ~56k hexes in under a second. Once I get this working properly, I can use it to refine my procedural generation algorithms for the terrain, which will include enemy spawns (enemy controlled cores) and varied terrain types. I’d like the terrain hex colours to actually mean something by using them to indicate different types of terrain.
I’ve committed the work in progress code to a branch.