Mini Solar System Postmortem

    On September 6, 2021 at 5:05pm MT, Mini Solar System version 1.0 was released. I had initially planned to launch at noon but a last minute bug with volume control of all things, delayed the release until the end of my day. This progress update will not focus on the work I did for the upcoming 1.1 release, but instead will be a post-mortem; recounting the entire development of the game.

Overview

    Mini Solar System started with the working title "Space Game" and development officially started on May 10, 2020 exactly 16 months ago to the day of me writing this. Development really began to speed up in mid February 2021 when I went full time on the project after deciding to defer university for a year; due mainly to threats related to COVID-19.
    There was no real design goal at the start and was initially meant to be a way to display my technical skill and also learn more about the Unreal Engine while I was at it. At the beginning of development I only really knew that I wanted to do something with space, probably some sort of celestial simulation (partly inspired by videos by Sebastian Lague which I have referenced many times on this blog). So with that broad of a goal I guess you could say I succeeded, but the lack of concrete purpose or direction really hindered development at times (I will go more into detail in later sections).
    I also intended on this project to make everything myself from code (my specialty) to art (not my specialty). I think this goal was achieved relatively well and is the reason for a lot of procedural art. This also taught me more about the art production pipeline which was an area of game development I had willingly ignored for some time. I learned the basics of GIMP for 2D, mainly UI, assets and Blender for simple 3D models.

What Went Right?

    The decision to use the source version of the engine right from the start was a double edged sword. The positive side was that being able to change the source code of the engine saved me multiple times. The most memorable being the simple addition of SaveGame tags to properties of the UCurveLinearColor class allowed me to incorporate these gradients into the game saving system with no further effort. Also being able to debug into the engine's source allowed me to solve multiple bugs in record time.
    When I started going full-time on the project, keeping a roadmap with deadlines really kept me on track. By that time I had a pretty solid vision of what I wanted the final project to look like; so writing them down and putting due-dates on them is what I think kept me going and staved off my perfectionism.
    As well as the roadmap, when I decided to work full-time on the project, I started doing weekly progress reports about what I had gotten done in that week. This allowed me to reflect on my work but also, I think, helped me better plan for the future. Putting down my progress in writing allowed me to more accurately assess my abilities. By the end of development I was able to predict quite well how long a feature would take to implement.
    Learning certain aspects of art went better than I would have thought. Especially in the material graph I feel I have an intermediate understanding of how to make materials; after all most of the process is just math. As for learning 2D art in GIMP, that was a bit more difficult. The only analog(?) input that I have for my computer is a mouse so most of the art that I made was geometrically based, using rectangle and ellipse selections to make the bulk of the images.
    Mid-development I started reading "Design Patterns: Elements of Reusable Object Oriented Software". This helped me think more about the structure and design of the code I was writing as well as improved my incorporation of OOP principles. It definitely would have helped more if I would have started reading the book at the beginning of development, but it was certainly a net positive for my codebase.
    Although I wasn't putting my design decisions through as much scrutiny as I would have were I to do it again, I was able to adapt the code and the design of the game fairly well as my ideas for the game evolved. I remember specifically I wanted to be able to highlight individual planets by superimposing a circle on top of them. I then realized that with this approach I wouldn't be able to display as much information as I would like to and there were multiple problems that resulted with this kind of UI. I was able to reuse ~90% of the functionality and widgets in the new design and was able to continue development with only a small road bump.
    I also learned a lot about debugging during this project. I was able to use less log statements overall and instead take advantage of breakpoints, the call stack, memory viewer, and engine debugging tools. I also learned about debugging a packaged game which helped massively in debugging the save and load system.
    Finally, as with most projects, I improved and expanded my resources for problem solving. I can Google more accurately (which as I hear is how most programming is done nowadays) and have found sites like Isara Tech and Orfeas Eleftheriou's blog that contain resources for more obscure topics.


What Went Wrong?

    I've mentioned it many times already, but the largest hinderance to the game's development was not having a plan from the beginning. In hindsight it seems so obvious that this would cause problems, but I think at the start of development I thought I would only be developing the game for a few weeks. This lack of purpose early on meant that I became disinterested in the project itself a few times; I would run into a tough bug (e.g. procedural meshes, multithreading, planetary lighting, etc.) and since I had no plan beyond fixing that bug I would be tempted to move on from the project entirely. I remember very vividly when I left for the first semester of university I continued developing the game in my spare time, but that got less and less frequent to the point where I was making 1 commit to source control per month and at one point I went two full months without writing on this blog. This isn't entirely my "fault" or even a bad thing as, like I said, I had lots of other stuff going on in my life at that time. However, if I had a plan for the game it would have meant that during these times I wouldn't have been entirely in the dark going from one feature to the next.
    Now for the other edge of the "building the engine from source" sword. The bad thing about the source version is that using the bleeding edge version of any software means that it will come with some bugs. Sometimes this meant small qol annoyances, other times the engine would inexplicably crash, and others (though it was much more rare) the source just would not compile. Ultimately though these problems are my fault for using the master branch and not one that has been thoroughly QA'd. I could have avoided all of this headache by not upgrading the engine so frequently.
Feature creep was a real problem. When I had the time to devote fully to the project I was coming up with ideas left right and center to make it better, which was a good thing at first. The more it went on though, I realized what was happening and was lucky enough to nip it in the bud by making the roadmap on Trello. I think now that having a roadmap with deadlines is essential for me to be able to the best work I can.
    As a "time saving" measure, I Frankenstein'd a system that I have now realized will take me more time to rewrite than if I had refactored earlier. I'm talking about the settings asset system where, when I was beginning the saving and loading system, I had to make some changes to in order to get them to save correctly. What I should have done is strip out the UObject part entirely and just have them be structs. They are more efficient, easier to save, smaller in memory, and would have accomplished the same thing, that is to provide property values to other assets. So I got into a kind of sunk cost fallacy where I didn't want to scrap the old system where I has spent so much time, yet it needed changes regardless of what I felt. The sooner I would have seen this, the sooner I may have fixed it, but it still remains in the game as of writing.
    Mid-development I kept realizing that I didn't have standard functionality planed out at all. Things like a loading screen/ level transitions, a settings screen and what to adjust, and biggest of all planning the saving/loading system. Oh. My. God. The save system. If I'm being entirely honest I probably spent a third of the development time on the save/load system alone. Mostly because I didn't plan for it, so when it came time to implement it entire parts of the game needed to be changed. In future, if a game has anything that needs saving between level transitions or between game sessions, that needs to be planed out before-hand. Luckily things like the loading screen I have figured out and can essentially reuse on any future projects.


Risk Management

    With every project I take on, I like to be a little over my head. Making a game where I already know exactly how to do everything sounds boring so I always make sure to push myself. With Mini Solar System I did a few things that were firsts: I made my own art, I dove into multithreading, I used procedural meshes for 3D art that I wouldn't be able to finish in a timely manner. For my foray into art I made sure what I was doing was manageable, doing simple geometric UI and asteroid meshes that didn't need a specific shape. All of the technical risks that I took were very calculated; I looked at what I wanted to do and what resources I had on hand if things went south. These risks were always worth taking as they were difficult challenges and pushed me to learn while not suffocating in their complexity. On the whole, I think I did a solid job of writing achievable deadlines (when I finally made the timeline) and hitting them very consistently.


Mid-Project Changes

    It's hard to outline changes that happened "mid-project" because the entire plan for the project and the end result I had in mind changed a few times before I finally made the timeline. I had ideas for a space exploration type game, an outer space combat game, a celestial survival game, but when I made the timeline I finally decided on a celestial simulation. That's not to say that the other ideas were bad, just that I was able to more easily follow and not wander off the path of a set design.
    Nothing major really changed once I started working full-time on the game, so it's kind of hard to talk about any changes without getting into specifics. I will touch on one feature that did change mid-development; that is the asteroids, more specifically their meshes. When I first started planning out the asteroids I had the goal of optimization; making as many simulated rocks as possible with as little performance cost. I decided to do them in Unreal's new Niagara particle system because it had been advertised as being so much better than Cascade in terms of performance and features. Then to make the meshes I planned on having the mesh itself be a cube, then using tessellation and world displacement to shape that into an asteroid looking blob. The Niagara half of that plan went smooth as butter, but the shader I spent days and days on (maybe weeks? I don't exactly remember) and it still looked like utter garbage. The problems were that 1) lighting is calculated based on mesh faces and completely ignores tessellation, and 2) I was trying to use a noise texture to make the offsets but it resulted in sharp spikes along edges and did not play nicely with the engine when trying to generate the textures. I assure you, reader, I tried my damnedest to fix these problems, I really did, it's just that replacing this complex shader with a few premade static meshes with LODs for performance was too easy. Maybe one day I will try again, but that day is not today.


Conclusion(s)

    I think that I should keep learning the art pipeline with Unreal. Probably more in my free time, but regardless I think it is a helpful skill to have and makes me a more well rounded developer. Knowing how to do certain things or knowing how to fix an art issue from a programmer's perspective could be invaluable to a future team.
    Taking full advantage of source control is something I need to continue working at. For example, using branches is something that I feel I need more practice in. I also need to continue to make myself commit to source control as soon as something works and has been tested. For some reason, in mid-development I was doing one commit a day regardless of how much I had changed. This resulted in two days in particular where I lost lots of progress because I wasn't giving myself checkpoints.
    The last but most obvious conclusion is that I need a plan before starting a project, no more guessing, I want to know what to do and what to expect before a project starts. This one change alone would have saved me personally a lot of headache and most likely would have meant that this could have been a five month project instead of a year and a half one. It also would have kept the codebase primed for updates. Right now I am working on 1.1 and it is mainly fixes and rewrites instead of new content. Along with this I need to plan my code more thoroughly. Meaning UML diagrams of what classes and pseudocode of the functionality that a system will require. Too many times in this project I would add a class only to realize it's functionality already has a better home, or hold off on creating a new class only to add one too late and have to do a time consuming refactor.

Comments

Popular posts from this blog

A New Chapter

Polishing the Foundation