Sprint 21 - Adam-Poppenheimer/Civ-Clone GitHub Wiki

Goals

  • The ability to choose the map type you're playing on when starting a new game, plus configuration options for that new game.
  • City name generation.
  • The ability to fortify.
  • The ability to pillage.
  • Unit maintenance.
  • Fog of war that can be suppressed in the map editor.
  • Some sensible way of displaying farms.
  • A visual indication of how big a city is, with higher-population cities having more buildings spread across a larger area.
  • The notion of explored and unexplored cells, with the ability to suppress exploration for the map editor.

Risks and Mitigation

  1. I've had lots of problems with fog of war in the past, with visibility changing in unusual and unpleasant ways. It's possible that my implementation of vision is too buggy and ineffective to extend.
    • My vision and fog-of-war system is based on the Hex Map Guide. I could probably return to that to see what I got wrong. I have some suspicions that my vision system is poorly organized, so I might spend some time correcting its problems before I try to do anything new with it.
  2. Map generation is a fairly delicate affair relying on a bunch of configuration variables carefully balanced against each-other. If I give players the ability to manipulate those configuration variables in order to customize their experience, it's very likely to generate poor results.
    • Probably what I'd need to do is create a set of pre-defined higher-level player options and then back those options up with more specific configuration data. So I might have a "Resources" category with "Low," "Medium," and "High" as options. Each of these would then represent a YieldAndResourcesTemplate configured at design time. The key is not to give the players direct control over the configuration variables, but rather a set number of preconfigured options.
  3. The nature of hills makes farmland tricky to implement. It's entirely possible that my existing triangulation strategies won't be of use getting farmland to appear on hills.
    • I don't think I can just swap out a farmland texture, even a very carefully-constructed one, and call it a day. That'll make farm edges look awful, which is not a desirable state of affairs. I might need to re-engineer hills, which will also force me to re-engineer roads. I can address the terracing issue possibly by getting rid of the terraces around hills. Civ 5 doesn't have them, so maybe I shouldn't, either. And maybe I can come up with a triangulation that works to create acceptable-looking hills and is more conducive to farmland solutions. Alternatively I can spend some time searching for better solutions online. There might be helpful advice or useful techniques I can repurpose to my own problem.
  4. It's not entirely clear to me how I'll visually represent cities. Vaguely building-looking blocks might be sufficient from a distance, but it's not clear how I'll assemble those blocks in a visually pleasing way.
    • I have some idea of how Civ 5 did cities. Apparently they arranged buildings in the center on a grid, and then arranged buildings on the outskirts radially. I could probably do something like that. I might have to deviate from my feature placement system a bit, or else repurpose it considerably, but that's not much of a loss. Feature placement isn't a very sophisticated part of the codebase and could probably use some improvements anyways.
  5. I've never handled terrain discovery in a game before. That might make it difficult to reason about possible solutions.
    • The Hex Map Guide has a section on terrain exploration. I don't know how well I'll be able to apply its lessons to my codebase (especially given how much mine has changed) but it should help. At the least it'll give me some shaders I can make use of.

Review

I began by incorporating map generation into the New Game panel, a task that took a bit longer than I'd expected. It's now possible to start a new game on a randomly generated map without having to go through the map editor. I also added some first approximations to various game options (like size, sea level, and player count).

Next I spent perhaps too long removing HexMetrics, a static class with a bunch of static variables that really should've been incorporated into various inspector-friendly config files. Doing so required a fairly large number of fairly small changes.

At some point I made another attempt at creating a better farmland solution. It didn't lead to anything useful and I ran out of ideas. Thus even though farmland visualization is a high-priority issue I ended up setting it aside to pursue other tasks I knew I could do.

After that, I went to pursue some unit placement bugs I was having and stumbled into a very bad part of the codebase. The logic that handles unit placement, terrain costs, and movement was a tangled nest unpleasantly coupled to the Promotion system that was causing all sorts of problems. I opted to try and repair it and ended up spending about 3 full days consolidating UnitPositionCanon and UnitTerrainCostLogic and improving my promotion system so fewer classes had to couple themselves to it. Promotions now inform a series of Summary classes on units that things like movement and combat use to determine what's going on. In essence, both point at IUnit or its components rather than each-other. It's not clear how well-spent that time was.

Next I spent some time working through visibility, another system with a lot of problems. I added a Visibility namespace with a VisibilityCanon class that determines which cells and resources are currently visible based on a number of configurable policies. MapEditor now has several options for visibility that are handled correctly. Improving that system also allowed me to stamp out a bunch of bugs, like resource node visibility being incorrect.

After resolving visibility problems, I turned my attention to the performance of map generation. This was a fairly considerable task, though in ways it was both easier and harder than I had suspected. It turns out RiverGenerator was the big performance culprit, taking up a majority of the time spent generating maps. I was using an inefficient pathfinding algorithm and executing it on too large a dataset. Resolving both of those issues was straightforward and made things a lot better. Grid creation was slow as well, so I ended up switching HexCell from a MonoBehaviour to a normal c# class and substantially reduced the time it took to instantiate and destroy the cells in my grid. Less dramatic but still useful was the time I saved in homeland balancing, emerging primarily from cell scoring and yield estimation. I managed to reduce calls to TechCanon considerably and improve performance a bit there, too. All in all I've substantially improved performance, to the point where it's practical to create huge maps from both a player and a design perspective. There's still more to be done, but it's a good start.

Lastly, I added in the generation of city names. I did this by creating a CivilizationTemplate that contains the civ's name, color, and a list of valid city names. This required changing some UI around (a task that I didn't quite finish) but has given me a platform onto which I could add unique units, buildings, or bonuses without that much difficulty.

Retrospective

What went well?

  1. The performance of map generation, while not perfect, is now in a much better place. It's practical to generate even the largest possible maps.
  2. While trying to improve performance, I several times caught myself going down unwise roads and aborted before I got too deep. A less-experienced me might've wasted hours pursuing fruitless paths.
  3. Unit placement and movement costs are now finally in a reasonable state. The responsibility is now encapsulated in a single class that isn't tied to the promotion system.
  4. I've substantially improved the organization and architecture of lots of pieces of my codebase. There are several important classes that are a lot cleaner and easier to reason about.

What could be improved?

  1. Farmland remains a very difficult problem without a lot of clear solutions. I might need to devote an entire sprint of studying and experimentation to resolve the farmland conundrum. I'll also need to fight the temptation to completely rebuild map triangulation until it proves absolutely necessary.
  2. I don't think my work was particularly focused this sprint. I ended up missing a bunch of the goals and spent a lot of time working on architectural or organizational tasks instead. I need to more carefully focus my efforts next sprint so that I don't go off on tangents of dubious value.
  3. It's not clear to me that the issues I addressed in this sprint were the highest priority tasks the game needed resolved. City name generation, the removal of HexMetrics, and things like fortifying and pillaging don't feel as important as, say, exploration or the idea of social policies or wonders. I might need to think more carefully about which of my long-term goals are actually important and which are merely nice to have, so that I can always be working on the most important things that need to get done right now.
  4. While I was able to improve map generation performance substantially, I feel like my performance toolset is limited. That was particularly evident while trying to improve yield estimation. I might consider studying performance and algorithm efficiency in more detail so that I have more solutions I can deploy.