Full notes
Full Untethered update
Read the full published notes in a cleaner layout. The original post stays linked below.
Repeated intro
Greetings!
What changed
- Gameplay
- Server
- UI and audio
- Balance
- Performance
It's me again with another devlog. As you may know, I spent the entire month of February working on the "great refactor". This is a bit of technical discussion, so apologies if it's not really your thing, no exciting new features this week I'm afraid. However, I know there's a fair few people who are probably interested, so I figured I'd write about it anyways!
The Problem
When I started working on Untethered over three years ago, it was a very different game. It was a raft-like survival game, in which debris floated past and you would grapple things to expand a small space station. Over time, the direction shifted, and I started choosing mechanics to encourage more complex station building, since I felt that was the aspect of the game that was the most fun. The world moved away from a more open world, free movement situation, and towards what it is now: discrete orbits that require a certain amount of fuel to transfer between, while the player sleeps. This allowed me to expand the scale of the world, with wide range in orbital altitudes, and the possibility for interplanetary travel in the future.
So what's the problem? Well, when I started, I wrote all the logic for the equipment in the stations (the solar panels, racks, interfaces) within class structures that derive from MonoBehaviours. That means that any rack, say the Electrolysis Unit for example, would contain all of its logic within a component attached to the game object. However, when I introduced orbital transfers, a problem arose. All of this logic would cease to operate as soon as that Electrolysis Unit was left in another orbit and the game objects are despawned. This means that any station that isn't presently loaded just freezes until the player returns to it. That's a problem when the game starts scaling up because of all the systems that run over time:
Electricals - the power should go out if the player fails to provide enough batteries
- EmoteThermal Controlremote stations should overheat/catch fire if the player fails to adequately cool it
Fluids - the fluid network should continue to operate even when the player isn't there, so they can run large-scale cryogenic production, fluid labs, etc.
Science - labs shouldn't operate when there isn't power, or another resource required by that specific lab
And many other minor details.
So, there's a simulation issue. How do we fix it?
The Solution
We need to ensure that stations continue to operate, even when their MonoBehaviours don't exist. The solution I chose was to create a "backend" station, which gets interfaced with by the player via the "frontend". The frontend exists in the upper assemblies of the project - for simplicity I'll call it Project.Systems. This is where the MonoBehaviours exist for each rack, for example "ElectrolysisUnit". I then created a new aseembly called Project.Data, which contains all of the backend simulation classes. So the counterpart of ElectrolysisUnit would be ElectrolysisUnitSim. The sim class is detached from Unity entirely, it just contains the basic logic required to run a virtual electrolysis unit - listen for power changes and split water molecules based on the power availability. The frontend class then just listens to the sim class - setting its gauges accordingly, and then telling the sim class to change its power state if the player flips the switch. This means there's a clear divide between logic and interface, meaning the sim layer of a station can theoretically operate without the presence of a frontend. Exactly what we need.
Implementing the Solution
As you can probably expect, this is was massive task. There were two sides to it:
Station management - the station control systems (electrical, thermal, fluid, fires, etc) were embedded within the MonoBehaviour station controller. I had to move all of this logic to exist as components of the new StationSim class, outside the world of Unity.
Element logic - every construction element in the game that has any sort of time-based logic (labs, batteries, solar panels, cryogenic liquefaction units, electrolysis units, etc) needed to have its logic rewritten in the sim layer.
Additionally, while doing this rewrite, I figured I should make some architectural changes as well, since the MonoBehaviours previously relied heavily on inheritance. For example: ThermalLab -> ThermalElement -> Rack -> FailureElement -> ElectricalElement -> ConstructionElement -> InteractableObject -> MonoBehaviour. I won't get into why, but that's pretty a pretty messy chain. So, when rewriting logic in the sim layer, I opted to use a compositional design, using interfaces and component classes to keep logic extremely modular and optional. There's a tradeoff with the amount of boilerplate required to get this to work though, so I'm still trying to find a balance.
What's Next?
Well at the time of writing this, the great refactor isn't complete yet. It's almost there, but I'm still finding bugs and things that I've overlooked. But when it's done, it will be a weight off my chest! It opens the door to other features:
Precise station simulation for remote stations
More accurate time skips, like when sleeping or travelling long distances
More avenues for optimisation, like despawning sections of large stations
More control over tick speed and simulation performance
Predictive station simulation, potentially for things like an advanced warning system
Once it's done, there's just a couple of small features I need to complete (along with a bunch of testing) before I can enter the alpha phase, hopefully at the end of March...
See you then!
Luna
PS: I'm hiring a tech artist, so if you're a TA and based on Victoria, Australia, go check it out! Or if you know someone who might be a good fit, send them the link :)
Source
Changelog.gg summarizes and formats this update. How we read updates.
