Sprint 1 - Adam-Poppenheimer/Civ-Clone GitHub Wiki
The 13th of November, 2017 to the 24th of November, 2017
Product Backlog Tasks
- The game consists of rounds. Each round gives each player a single turn. Player turns are resolved on at a time.
- Players can select cities to view details about them.
- Cities have people that consume food and gather resources, which players can assign to various slots or leave unemployed.
- Cities generate resources every turn, either for their civilization or themselves.
- Cities with enough food will grow, producing more people.
- Cities with enough culture will expand their borders, giving them more territory from which they can extract resources.
- Buildings and terrain can both offer places for people to work.
- Cities can produce buildings with production.
- The map consists of a rectangular hexagonal grid of varying sizes.
- The map consists of terrain types that modify the game.
- The map consists of terrain shapes that modify the game.
- The map consists of terrain features that modify the game.
- Different hexes produce different resources based on their terrain types, shapes, and features.
Basic Design
Story
The story is the overarching premise of the original game, but isn't likely to have much impact at this stage in development.
Mechanics
There is a single civilization under the player's control. That civilization possesses a single city. This city has most of the basic functionality of a city in Civilization 5: it generates resources, grows new people, assign people to various slots, expand its boundaries, and construct buildings to affect it and its civilization. The map will be simple and predetermined, though it will contain terrain types, shapes, and features. There will be no units and no additional cities. Time will be divided into turns.
Aesthetics
Utilitarian for now. There will be some visual indication of which terrain is which, and a way to figure out what its shape and features are.
Technology
The game will be produced in Unity 3D 5.6, with Visual Studio 2015 as an IDE. Version control and project management is mediated by GitHub. The project includes the dependency injection framework Zenject and the mocking framework Moq.
Risks and Mitigations
- I may have budgeted too many product backlog tasks, and I might get overwhelmed by the deluge of work.
- I would much rather have more work than I can finish than less work than occupies me, at least for now. As long as I'm careful about prioritizing my tasks and I address all of the really important things, it shouldn't be much of a problem to leave some things unfinished. Sprints are designed to fail gracefully in that circumstance.
- If I don't establish a clean and extensible architecture now, when I'm working close to the core of the game, then it's going to make it very hard to efficiently extend the program later on down the line.
- There are several things I can do to address this. One is to use Zenject to create a pretty loose architecture, concerning myself only with the dependencies on individual classes. Another is to set out the skeleton of what I think I'll need now and leave it largely as a collection of stub classes. Ostensibly the more modular I make the code the less I'll have to worry about a messy architecture.
- This sprint seems very UI heavy. For this reason, I might want to regression test my UI, but that is a category of activity I've never done before and one that might prove time-consuming and brittle.
- If I make sure to carefully define my interfaces to reveal the verbs necessary for the UI to function and then mock the interfaces that contain those verbs appropriately, I should be able to unit test my UI without too much difficulty. That'll be especially easy if I can separate the UI logic from mouse and keyboard events, which I pretty much know how to do already. Worst case scenario, I can make the cleaner UI a lower-priority task so that it doesn't get in the way of core game logic.
- Buildings might be very complex, and their creation might prove to be a far more difficult task than I'd originally supposed.
- I can begin with a fairly simple implementation. Since I'm not really taking civilizations, units, and whatnot into account, I can focus on the most basic things that buildings provide to cities: worker slots and resource modifiers. Anything beyond that I can leave to future sprints. I can prepare buildings for a more data-driven approach by designing building templates as ScriptableObjects and providing a class that gives cities access to and information about those templates. I'll also need to create a modifier system like I made in Merchant Republics. If I do that intelligently enough, I should be able to decouple cities (though probably not their UI) from the information contained within building templates.
- It's not clear what the architecture of the game is supposed to look like, nor is it clear that what I'll build for cities and the terrain grid is going to be sufficient to support the rest of the game.
- It might behoove me to focus less on some grand architecture than on some basic information flow systems on unit modularity and decoupling. If for any particular class I'm only concerning myself with its immediate dependencies, I might not need to worry about big architectures as much. I'll have to see how extensively I can separate units of logic from each other.
- Unit tests might consume an inordinately large proportion of development time and slow development to a crawl.
- Mocking and my experience with unit tests will certainly improve the speed at which I can develop such tests. Making individual methods simpler may also help. But at the end of the day, this is a very important time to introduce good TDD principles, since I more or less know the requirements of the system ahead of time. Tests will take a lot of development time, and that's just something I should come to terms with. It'll be important in the long-run.
- Serialization of maps and cities will be very important, and if I can't find a clean way of managing it that's tolerant to codebase changes then I'm going to have a hard time creating maps and test cases later on down the line.
- Since I absolutely know I'll need to address this challenge beyond any shadow of a doubt, it makes sense to take serialization into account even this early in the development process. The only difficult aspect of serialization I can think of is connecting the city's people to its population slots. That'll require remembering which hexes had people on them, which might take some doing. But even then, I can always store the HexCoords of the slot. It shouldn't be too difficult. I just need to do it.
- The sheer amount of units and buildings and their possible complexity will require a robust, data-driven unit and building definition system, which could complicate things substantially.
- I can abstract the complexity of this system from other modules by creating a class that assigns changes to various gameplay elements (cities, units, civilizations) with the call of a method. That way, as buildings and units become more and more complex, I can extend those systems without substantially affecting how cities go about their business.
Review
I managed to get a lot of the basic behaviors of a city down. Cities grow, expand, distribute people to the various slots they have access to, and can construct buildings. I have a functioning UI and a lot of unit tests on the simulation side of things. There are, however, bugs with my implementation. Cities don't appear to distribute workers as one would expect. There are some missing unit tests in the simulation and a lot of missing tests in the UI. The notion of a civilization doesn't currently exist, nor is there any way to modify a city's production in a percentage-based manner. There are turns and a hexagonal grid, and the yield of a tile is dependent on its terrain type, shape, and features. There's no clear way to determine which tiles yield which resources, either in the city view or the default view. There might also be problems with the current UI implementation, since UI logic is currently distributed between message-based UI through Zenject and UniRx and my own lackluster state machine model.
Retrospective
What went well?
- I managed to address most of the core behavior necessary for cities to function, and did so in a focused and prioritized way.
- I made considerable use of third-party tools (Zenject, Moq, UniRx towards the end) to improve my development process, rather than building my own lackluster tools.
- I managed to properly mock dependencies and modularize my simulation code to a degree I don't think I've achieved to this point. This has made my codebase a lot easier to maintain, since modifying one field on one interface no longer necessitates the modification of several mocks. It's also allowed me to fiddle with implementations in one module without affecting the validity of tests in others.
- My programming abilities have noticeably improved, not just during this sprint but since Blobgistics. It's becoming clear that I'm making progress and becoming a better programmer, which is the whole point of all this.
- Despite my focus on testing, development went at a fairly reasonable click. Mocks and modularity made the process go faster than I'd expected.
- Through use of Zenject and UniRx, I'm developing much more intelligent ways of handling UI, especially the event-driven side of things. Signals and Observables are proving powerful tools for constructing UI code. I might want to go even further and search for a more formal UI framework to use.
What could be improved?
- I've not adequately unit-tested my UI, nor did I have a clear plan for doing so during development. Given how much logic will end up in the UI, I need to come up with a better plan for regression testing it.
- I've been struggling somewhat with the new full-GitHub project management. The projects I've been creating have not been particularly useful, nor have my issues been particularly well-defined. I need to figure out how to use the tools GitHub provides to better organize my project.
- I've been misusing Git as a version control system. Rather than splitting off branches for my major tasks, I've just been committing to Master. While this is fine for a personal project, it'll be unacceptable in a collaborative environment. I need to get better about splitting branches off for each Sprint, and maybe even for each Issue. I also need to more rigorously make commits that resolve specific issues and reference the issues they're supposed to solve. I might even consider adding pull requests to my workflow, though that seems unnecessary for now.
- I completely forgot to return to my Risks and Mitigations section from the Sprint document. As such, several risks went almost completely unaddressed. I need to keep an eye on the prep work I do for each sprint, so I can assess the importance of my work properly.
- Beyond regression testing, I don't have a clear discipline for identifying and resolving bugs. The Issues system in tandem with unit test creation has some promise, but I still need to develop more practical experience with formally resolving bugs.
- I haven't addressed serialization at all, despite its importance. There's a lot to be said about focusing on the runtime simulation first, but I do need to get serialization working sooner rather than later.
- My in-editor experience is pretty lackluster. I don't have a way of constructing cities, or a particularly good way of painting terrain. That'll be a necessity later on down the line.
- My current codebase has essentially no documentation beyond the unit tests. I need to make documentation, even fairly basic stuff, a running priority so that I don't need to spend several mind-numbing weeks at the end of the project performing documentation.
- I don't think my current namespace structure is conducive to proper organization. The Cities namespace is far too large for its own good, and I'm not sold on storing the UI code adjacent to the simulation code. I'll need to spend some time uncluttering my namespaces during the next sprint.