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.
I decided to tackle the chunk generation issue today. But first I looked into making text draw on the screen so I can debug easier without needing to refer to the console. This will also mean when it comes time to draw the amount of resources, for example, on the screen that I’ve already gotten the hard part figured out.
On to the actual hard part, chunk generation. Somewhere in my arithmetic I’ve got an error, which is causing chunks to be generated (and draw overtop of) existing chunks. I fixed one small bug I saw, but it was minor and didn’t seem to actually fix the issue. Dang.
While working on a fix, I implemented a way to determine all of the hexes inside the viewport, and I’ll probably update all of my drawing code to only draw the hexes that are actually inside the viewport. The algorithm finds the corners, draws a pair of line between each of the top two and bottom two corners, and then iterates over those two lines drawing lines in between.
The image above shows the hexes calculated to be inside the viewport. There’s some extra hexes on the sides and top/bottom, this was done to make sure that these hexes completely cover the viewport. Why does it look like a little too much extra? Because I applied the same amount to the vertical edges as well as horizontal edges, but the top and bottom should be half of the sides. For my current implementation, this would mean drawing 483 hexes instead of 1089 for the 1280×800 window I’m testing with.
Unfortunately, I wasn’t able to find the arithmetic error in chunk generation. The code all looks correct, but something is very obviously wrong. It’s probably a simple off-by-one error, but finding it has proven to be challenging.
As a very quick fix for the worst of the issue, I made it so that terrain tiles can’t be updated, only created. It doesn’t solve the root cause, but it does make the bug have a lot less impact. It’ll still be an issue when terrain chunks aren’t randomly generated but represent part of a larger land form.
I decided to put off fixing the serious issues around chunk generation and scrolling and instead work more on the networks. I created a set of white (as opposed to the orange) network sprites to use for the un-powered networks.
I implemented the A* algorithm, though in the future I may need change it to a Dijkstra’s as there will be multiple energy sources that can all supply a network. This worked fine, but I realized that I have no way of rechecking a network to see if it has been connected. The sprites don’t know how to update. This might be a good time to figure out how cocos2d’s event handling works, and have a network node whose status has changed have a way to tell others to update, though I’m not sure what the best route to take here is.
My first implementation can properly determine if a energy network node is connected when it’s built and draw it as powered/un-powered, but it doesn’t support updating yet.
With the basics working, I moved on to making the network update when connections were added or removed. I did this by finding all of the nodes connected to an energy source, in this case the one city core, and powering them. If they’re not connected, they’re not powered. It works, but it starts getting really slow after about 50 nodes, so I’ll need to put some time into optimizing the graph traversal algorithm to make it usable with large networks, perhaps storing each disconnected sub-graph and only updating as needed.
After that part was working and networks now properly deliver (or don’t deliver) energy to sinks of energy, I implemented protection towers switching on or off, including their safe areas when they lose energy. From a UI perspective, I should also give an indication on the tower that it’s off, not just have the safe area around it disappearing. Network propagation is still slow, taking nearly a second to switch the energy on and off in the below gif. I may need to address this sooner rather than later.
Things are continuing to look more and more game like as I add mechanics, but some of the potential serious performance issues may need addressing sooner than I’d like. Network changes are slow. The issue with chunks is currently less of a problem, as it seems to be more likely to be an arithmetic error than something more serious.
I also tweaked buildings to swap sprites when they’re on or not, giving a visual indication of whether or not they’re receiving energy or not. I also created sprites for off as well as on. Art is still placeholder art, because I don’t want to focus on art until I can properly display it.