Developer postOriginal post (opens in a new tab)

Diary Page #11: Systemic Architecture (Part 1)

Dear Scholars,
It is time to finally give you some behind-the-scenes look at what makes Selenwald tick - especially that it has been a while since the last entry and you deserve something more than just a status update. This entry will be first one of a small series on the code architecture because the topic is too big for a single post.

The first thing I'd like to write about is the reasoning behind what has been in the works for a very long time. As I mentioned in the past, I have started a massive code rewrite exactly a year ago. It was never supposed to be a minor refactor or a mere clean-up but rather a complete change of the underlying architecture. Scary? Sure! Needed? Absolutely.

But why?

  • Technical debt: Back when my main development goal was to get a publisher or an investor, my priority was to create a publisher demo that's representable enough to communicate the vision and show a glimpse of the final product. During that stage it is natural to opt for quick and dirty solutions rather than future-proof ones because real production would start after money arrives and all the bad could would be scraped then. The problem was that the fundraising process lasted several years for Selenwald (and ultimately failed because of the simultaneous worsening of the health of the games industry). This has accumulated a tremendous amount of baggage as I needed to keep focusing on short term goals for such an extended period of time.
  • Non-modularity: When the prototyping stage of Selenwald's development began, I didn't fully grasp the systemic depth this game would ultimately achieve. Its immersive-sim characteristics, with various systems interplaying in a simulative manner, have later become one of its most captivating features. Yet, the initial code architecture wasn't fit for this. The logic was highly specific, turning any addition or interaction between systems into a spaghetti code maze which has become bug-prone, hard to maintain, and increasingly complex with each new system.
  • Non-moddability: While modding support isn't something I want to have right upon the game's release, I absolutely want to add it at one point. Selenwald is one of these games which are perfect for modding due to their systemic design and sandbox gameplay. Thus, the code and especially the data structures should follow some good practices that would enable relatively frictionless modding integration. The old code wasn't terrible in this regard but had a lot of room for improvement.
  • Complex content authoring: Related to all the points above, even creating a single new enemy was fraught with complications and error risks. My original reliance on prefabs, one of the basic building blocks in the Unity engine, became a hindrance for a game as procedural, systemic, and aspiring to moddability as Selenwald. For example, any creature (human or not) would have lots of behaviour scripts attached to various bones and set up manually. This coupling of logic with the animation rig was needed for many systems but the workflow was just very cumbersome even after Unity introduced Prefab Variants and Nested Prefabs. If you wanted to take a finished creature and create a similar one with all the systems intact but a different model (especially with a different rig) - it just wasn't possible to do. You would have to manually setup the new creature again every time or create complex editor tools that would partly automate the process (but still create completely new prefabs that have no relation with the ones they were based off of). I knew a paradigm shift was needed.

    The Core

    But what this "architecture" thing even is? It's basically code that serves as a foundation for everything else, the relations between data structures and systems, as well as rules for writing specialized logic. Some of it is defined by the engine - Unity in this case. For example, the base building blocks in Unity are scenes which contain game objects, which in turn can be saved as prefabs to serve as reusable pieces you can edit in one place and see the changes reflected in all their instances. Game objects (and thus prefabs) contain code blocks called components - either built-in (like Rigidbody, Mesh Renderer, Light, etc.) or custom scripts. Another important element of the whole ecosystem are scriptable objects which (among other uses) are a very handy way to store data that can be shared among various unrelated prefabs.

    The way the engine is structured imposes a specific workflow. However, sometimes it is necessary to think outside the box and create your own foundations that are still connected to the engine's way of handling things, but offer a different interface at a higher level. For example, it is natural to have every level on a different scene as this automatically handles loading appropriate assets to memory and unloading them when the level changes. But there are some cases where the entire game could sit on a single scene. This can be a viable approach in procedural games like Selenwald because the way of loading and unloading data needs to be handled in a custom way. After all, we don't know which assets are needed at a given level until we actually generate it. This is just a simple example but sometimes other seemingly critical parts of the engine become a hindrance and need to be reinvented. In Selenwald's new architecture I've almost entirely moved away from prefabs. This is something unthinkable for most Unity developers but this was a truly liberating change for this project. Why? I'll get to the technical details in the next entry. Don't worry - this time you won't have to wait several months for it! I'll do my best to write it before the end of December.

    Anti-Vacation

    Finally, I'm excited to report that I'm soon taking a vacation from my day job. And it's going to be a long one because I haven't used almost any of the paid time off available for me ever since I started the job in April. It's going to be a whole month, starting on December 7th and ending on January 6th. I honestly totally forgot about how much vacation time I have available so this came off as a bit of a surprise. Of course, I'm going to spend most of it working on Selenwald which excites me a lot because the last time I was able to spend an extended time working exclusively on it was December 2023, so almost an entire year ago. I hope I will use this opportunity to catch up a bit and finally get the demo close to the state which is functional enough to get some playtests rolling - something I had originally hoped to already have by now. I still can't give you any reliable date for the demo release but it's definitely getting closer and closer!

    Also, a small reminder that if you want to support the development process, you can do it at Ko-fi.

    Thank you for reading,
    Wiktor