1. Starcom: Unknown Space
  2. News

Starcom: Unknown Space News

Kepler Patch 21531

Minor updates:
  • If the player is in Planning Mode in the shipyard they will be able to load blueprints that they do not have the tech/resources for
  • "Exit station" and "Delete saves" alert/confirmation dialogues now allow selection of a button when using the controller
  • Several more minor typos

Weekly Update: Apr 25, 2025

If you missed the announcement from earlier this week, the default build has been updated to Kepler 21530, mostly with changes for improved Deck / Controller support, with a few other patches.

[h3]Deck/Controller Improvements:[/h3]
  • Improved Save & Load menus to enable all functionality via controller inputs, including toggling selections for delete, deleting old autosaves, etc.
  • When text scaling is set to maximum (set by default on the Deck) all text should be a minimum of 9 pixels high at 1280x800 resolution.
  • Eliminated areas where scaled text overlaps other UI elements
  • Dropdowns now keep current selection in view when using controller
  • All text input fields should bring up the virtual keyboard on the Deck
  • Now possible to toggle highlight, fog in map mode
  • Research panel correctly keeps research selection in view
  • In cases where there are two scrollviews visible (such as the ship log), right stick scrolls the first, dpad scrolls the second.
  • Cargo screen allows for quickly analyzing items and proceeding to the next
  • Improved input handling in the shipyard
  • Chromaplate swatch customization now works with controller
  • Crew help should correctly reference controller inputs instead of keyboard if the controller is being used

[h3]General fixes:[/h3]
  • If the player has set a "design goal", the trade screen previously did not show shortfalls in the faction's own reserve currency. Now it does.
  • Rendering optimization when in map mode
  • Several minor typos

There's also a small patch, currently on the opt-in beta branch that I expect to deploy to default in the next few days. This will fix:
  • If the player is in Planning Mode in the shipyard they will be able to load blueprints that they do not have the tech/resources for
  • "Exit station" and "Delete saves" alert/confirmation dialogues now allow selection of a button when using the controller
  • Several more minor typos

In other news, my wife and I are planning a trip at the end of May where I plan to attend the Nordic Game Conference in Sweden, so there will be a pause in regular updates around that time.

Until next week!
- Kevin

Starcom: Unknown Space improves the UI and controller support ready for Steam Deck verification

Starcom: Unknown Space from Wx3 Labs, LLC has a fresh upgrade out now that brings in numerous improvements to the UI and controller support, as the developer hopes to get it Steam Deck Verified.

Read the full article here: https://www.gamingonlinux.com/2025/04/starcom-unknown-space-improves-the-ui-and-controller-support-ready-for-steam-deck-verification/

Kepler 21530 Controller / Deck Update

The default branch has now been updated to the Kepler 21530 build. The main goal of this build was to prepare the game for Steam Deck verification, although there are a few minor gameplay improvements as well:

Deck/Controller Improvements:
  • Improved Save & Load menus to enable all functionality via controller inputs, including toggling selections for delete, deleting old autosaves, etc.
  • When text scaling is set to maximum (set by default on the Deck) all text should be a minimum of 9 pixels high at 1280x800 resolution.
  • Eliminated areas where scaled text overlaps other UI elements
  • Dropdowns now keep current selection in view when using controller
  • All text input fields should bring up the virtual keyboard on the Deck
  • Now possible to toggle highlight, fog in map mode
  • Research panel correctly keeps research selection in view
  • In cases where there are two scrollviews visible (such as the ship log), right stick scrolls the first, dpad scrolls the second.
  • Cargo screen allows for quickly analyzing items and proceeding to the next
  • Improved input handling in the shipyard
  • Chromaplate swatch customization now works with controller
  • Crew help should correctly reference controller inputs instead of keyboard if the controller is being used

General fixes:
  • If the player has set a "design goal", the trade screen previously did not show shortfalls in the faction's own reserve currency. Now it does.
  • Rendering optimization when in map mode
  • Several minor typos

Technical Design of a Quest System (Part 3)

This is the third and last post in a series on the technical details of the Mission System. But first a bit of regular news: As I've mentioned previously there's a build on the beta branch with a number of improvements to Deck / Controller support, plus some minor fixes. Barring any reports of issues, I plan to promote it to default early next Week.

In the first post in this series I explained how the Mission System works overall. In the second post, I showed how the Mission Conditions and Actions worked.

As I said in the intro, I'm very happy with how the mission system has worked out, allowing me to create interesting and varied story lines and quests efficiently. This last part is important: creating an open world RPG as a (mostly) solo developer is a huge amount of work and I being able to create, test, and improve content quickly is absolutely critical.

But development is always a process of discovery and learning. Even if I generally made good technical choices along the way, I've gained knowledge in the process.

So here are a list of things that I might do differently if I were rebuilding the mission system from scratch. Of course, since these are technical paths I didn't go down, they might have their own hidden challenges. In no particular order:

[h3]Mission Journal Objectives as Content Objects[/h3]

In the existing system, the specific entries that the player sees beneath each mission are created and advanced by MissionActions. They are stored in the player's save, but overall within the game they do not exist as objects that the game's content system "knows about". If I were to revisit this system, I'd probably want to be able to create objectives with their own content ids. Advantages of doing this would be:

  • Automatically adding mission objectives to analytics.
  • Make it more easy to use progress on specific missions as conditions.
  • Allow mission details to reflect mid-game changes in localization (currently if a player changes their language mid-game, mission objectives remain in whatever language was active when they were added).
  • The Creator Tool would be able to present objectives as a dropdown choice instead of a text field, preventing the possibility of typos.
  • Prevents cyclic dependencies. One current risk in refactoring missions is accidentally creating a "cycle" in the mission graph where Objective A and Objective B both have each other as prerequisites and therefore create a soft-locked. While promoting journal objectives to content objects wouldn't prevent this, it would be possible to create a check for it at design time.
Items are content objects, so the tool can present them as a dropdown choice.

[h3]Improved IDs[/h3]

Relatedly, there are some things I don't love about how Content objects are referenced. These objects, such as anomalies, items, factions, persistents, etc. all have human-readable string ids. If there is a specific ship that the mission system needs to interact with, it might have an id like "ARRIVAL.ALETHEIA". While these ids are invisible to the player, they are very useful for scripting, debugging, etc. They consist of the id of the "story" they are part of (stories are the content equivalent of namespaces) and some local identifier, so if the designer has previously used an identifier in some story months ago in another story, they won't conflict.

There are two issues with this system:

The first is that strings are a bit less efficient in some contexts than integers. In most cases this efficiency difference is trivial enough not to matter. But there are some where they do have a real cost. For example, in the combat and AI systems, entities need to know who's a friend, who's neutral, and who's an enemy. In a crowded battlefield, when performance matters the most, this can equate to hundreds of disposition checks per update. To mitigate this, I implemented various caches, but cache validation can be a tricky problem. I am aware of at least one bug (now fixed) that arose from situations where a faction was hostile to the player and would not send new ships while hostile. But the disposition cache was not updating for that faction if they had no ships.

The second and more annoying issue is that there's no easy way for the tool to refactor ids. So if I want to move "ARRIVAL.ALETHEIA" into its storyline as "ALETHEIA.ALETHEIA", I have to search and replace across the JSON content files and be careful not to reuse ids for different types of content (like a conversation and an anomaly).

I'm not 100% sure what the best replacement would be. I think maybe I'd change them to structs consisting of a string that contained the content type, story id, local id and a separate ulong GUID.

[h3]Negation for All Conditions[/h3]

Several times through development I ended up either creating a condition that was essentially the logical opposite of an existing condition, or modifying an existing condition with a boolean toggle that inverted the logic. E.g., early on I added a HasItem condition, then later I added a flag allowing the logic to be inverted so that it failed if the player DID have the item. But in some cases I ended up with the negation as a separate condition: there is a condition to check if the player is near a particular faction and another to check if they are not near.

Instead I think I should have consistently made all conditions negatable by adding a negation flag to the base class.

[h3]Embedded Node Timings[/h3]

As described in the first part, the save state of a mission is pretty much defined by the current node index for each lane. One of the most commonly used conditions is "Delay" and its variants, where the mission is simply waiting for some number of seconds to pass. This is used by storing a time value for the node as a named numeric value in the game's general purpose GameVar system. This works fine and kept the mission state simple, but was a bit of a hack. I think it might have been better to store time values for each node. I.e., each node would know how long it had been since it was entered, both in scaled and unscaled time (scaled time doesn't advance when the player is in modal interactions like the station or dialogues, whereas unscaled time does).

[h3]Allow Missions to be organized as sub-objectives of another Mission[/h3]

In the current system, a mission's objectives are only one level deep. From a UX perspective, it might be better if they could be organized into trees so that the player could see that a group of objectives formed a connected part of a larger mission. From a design perspective, organizing missions into smaller sub-parts that could be iterated and tested in isolation would speed up development and allow for easier refactoring.

Organizing missions into smaller sub-objectives would make design and testing easier, as well as being clearer to the player.

[h3]Area Objectives[/h3]

In the existing system, a mission objective can be associated with a posiiton in space, which is helpful for directing the player to check something out. But there are time when the player is tasked with finding something that could be in a certain area.

[h3]Variable Update Frequency[/h3]

As a performance trade-off, missions are updated around 6 times per second. Missions are not intended to provide the logic for reflex-driven gameplay elements, just the mission-level "narrative" logic. I.e., players will not generally notice if an officer chimes in 2 seconds vs 2.16 seconds after anaylizing an item. But there are some edge cases where the mission might trigger later than expected, such as proximity logic if the player is moving at high speeds.

Alternately, if a node contains some CPU intensive logic but the timing is less important, it might be desirable to check only once a second.

So maybe nodes could have an update frequency override value.

[h3]Questions & Answers[/h3]

As promised, here are answers to some readers' questions about the previous posts:

"I would be curious about the (more detailed) difference between your MissionManager way and what you described as "Singleton" approach. Aren't you in both cases just accessing one part of your code from somewhere else?"

Good question: Yes, that's right.

In software design, it is generally undesirable to tightly couple systems: this leads to brittle designs where changes to one system breaks another unrelated system. But at the same time, you will often need some way for completely unrelated systems to talk to each other. Let's say for example the Mission logic somehow needs to check whether the player is currently talking to the Ermyr.

One possible solution is to use a Singleton. A Singleton is a static object instance that allows any code to reference it from anywhere, and because it's static there only be one instance, so you know everybody's working from the same data. Maybe there's a Singleton that's used to see if the player is currently talking with a particular faction. This absolutely can work and if I recall correctly is how Starcom: Nexus worked.

Any code easily can do something like:

if (ConversationManager.ActiveFaction == "ERMYR")

A second possible solution is some type of Dependency Injection, where if Object A needs Object B to do something, it will have been given a reference to it either when it was created or at the time that it needs it. So the Mission Manager's MissionUpdate object contains a reference to the MissionManager, which exposes all of the Game manager subsystems, including the ConversationManager which is NOT a Singleton, just a property of a GameInstance object. So the MissionCondition's code would instead look something like:

if (update.GameWorld.ConversationManager.ActiveFaction == "ERMYR")

Which is a bit more verbose. So why do it that way if you know there will never be more than one ConversationManager?

Well, first of all, the Mission system really may need to access almost every part of the game: besides normal quest-like missions, it also handles tutorials, achievements and other ad hoc logic. Which means that if you decide the way that the Mission system accesses other parts of the game is via Singletons, then the easiest and most consistent way for any part of the game to talk to any other part will also be Singletons.

This will naturally encourage a dependency on Singletons. And they expect that there will always be exactly one instance of themselves. In the case of a game instance and its related sub-systems, there should never be more than one, but what if there are none? Cases where your singletons might not exist:
  • At the Main Menu, before the player has started or loaded a game
  • Within non-game tool systems, like the Creator
  • Test scenes where you're trying to test or debug one specific area of functionality

So you may need to create and initializing everything to be able to do anything.

I wouldn't necessarily advice aspiring game devs to avoid Singletons. They are convenient, but that convenience tends to encourage dependencies which have a trade-off. Based on my experiences with Nexus I decided not to use Singletons for game-subsystems in Unknown Space. But lots of good games do use them this way. And I still use them for some application-level functionality, such as:
  • Localization
  • Analytics / Error reporting
  • Content Manager
  • Sound Manager


"I'm surprised that the updateMission method that runs all the time actually checks missions for changes from the patch. Somehow I would think that patching the game would patch the player's current save? But maybe I don't know enough about the Steam patch process. Or maybe you're just doing it this way as an extra check to prevent bugs?"

This is a little tricky to explain, because it requires some understanding of the relationship between game logic, game content, and save content.

The Steam game patch process only updates the game's installation folder so that it matches the latest build installation files. This effectively updates the game logic (the rules of the game) and the game content (the game's universe and story which are the same for all players). But saves, which are the result of the player's specific experience in the game, are stored in a separate folder that Steam does not touch. There really would be no way for Steam to know how to update this folder.

Game developers must decide the content and format of their saves, as well as how to patch them. Consider a game where at some point the player finds a shop where they can gain access to upgrades:

In a game update, this is changed so that the shop is locked and first the player must complete a quest where the reward is the key to the shop.

What should a patch do with a player's save in this situation? How to patch this is as much a design problem as a technical one. Does the shop suddenly become locked and the player now must complete the quest? What if they were inside the shop at the time of their save? These are things the designer must now consider with content patches. (This is a major reason why during Early Access named updates were not save compatible)

With Unknown Space's mission system, because mission lanes progress from left to right, any new nodes added at the end of a lane can be assumed to be in the player's future. So if the designer keeps that in mind, they can patch missions at the end of a lane (or a new lane). If the player's save is already at the end of the lane, the mission will immediately enter the patch's new node. Otherwise, it will enter it once the lane gets there.

Would you consider releasing these tools for modding?

Yes. The Creator Tool is already available for testing. The main reason I haven't made it fully public yet is that I'm not 100% sure how much I want to support it in its current state. But if anyone wants a key to check it out, send an email to [email protected]. You can see the pinned topic in this sub-forum for more information on its current state.

Okay, this turned into a longer post than I expected. Hopefully some found this information interesting, until next week!

- Kevin