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.
As it turns out, the major issue with chunk generation producing wrong chunks was an off-by-one error, and it only happened in one chunk generation direction, which is why it appeared to intermittent. The fix was simple, just mirror what I’d done for the other direction.
With that out of the way, on to updating the various draw functions to only draw what’s inside the viewport. The first task was a fix for guessing at a viewport before it has already been created, because that’s how my code works with cocos2d.
Only drawing in the active viewport turned out to be relatively easy, save for a silly mistake in copying and pasting code that left me wondering why it wasn’t working properly. Normally this is the sort of thing I’d have amended the commit to be the fixed version, but that seemed like it wasn’t in the spirit of what I’m doing.
Above is an image demonstrating this pruning in action. There are more buildings, more terrain hexes, more network connections and more safe area outlines off the bottom of the image that aren’t being shown. This uses the code I wrote yesterday to determine a superset of hexes covering the viewport, draws the hexes from that layer that are in the set, and prunes the rest. This means that the game can scroll more than a few chunks without slowly to a crawl.
Other than some alignment issues on certain chunk boundaries creating a visible seam, which is certainly yet another rounding error, I think most everything I need to get done on the base display side is done. Now to start implementing things that require the basics be working.
I decided to implement fog-of-war next. It sort of works, but slows the game down to a crawl when it’s actually being drawn. I don’t know if I’m doing something wrong, or if I’ve hit a maximum number of layers that cocos2d performs well with, so I disabled it for now. Not being able to build buildings and networks in areas that aren’t visible has not been implemented yet.
From a design standpoint, I’d like the fog of war to be partially transparent, so that things underneath can be seen, but setting opacity for the fog sprites didn’t seem to do anything.