Skip to main content

How We Write Missing Stars

Hey folks. I'm Adam, Annaliese route writer on Missing Stars. I was formerly known as "Ultra_HR", but since my first (and last) blog post, I decided I didn't like that name any more. For the record, the "HR" stood for "horseradish", not "human resources". No, I'm not sure why that was my username for so long. I just use my real name now.

Since then, I've also become the writing lead for Somnova Studios. I thought I'd share some of the things we've done since I took on that role to make working on Missing Stars as easy as possible for everybody (or, at least, not a total pain) and to make it a way better experience for readers.

Managing a massive story as a large volunteer team

Missing Stars is a pretty big visual novel, especially for a volunteer team. Act One alone has a word count of over 250,000 (251,848 at the time of writing, if you're interested). We don't have a projection yet for how long the full release of all four acts will be, but based on this we expect it'll be... pretty big. This means there's a lot of information to manage. We have dozens of characters, and they just keep saying stuff that we need to keep track of — plus, we have a whole fictional school with a historic background under our command.

In the past, keeping track of all this was done on Google Drive, with information split between way too many different Docs and Sheets, in different directories, sometimes with multiple copies of the same document in separate places and no real clue to which should be considered the source of truth. These were dark times. We considered implementing a more strict Google Drive directory structure for info management, or perhaps a private installation of Mediawiki, but these all seemed lacklustre to say the least, and too prone to issues of human error — and neither offered an effective way of also tracking our work, which is something we really lacked. Then we discovered Notion!

Notion is our secret sauce

The main page of the Missing Stars wiki on Notion.
The main page of the Missing Stars wiki on Notion.

Notion is an incredibly versatile tool, and its flexibility is what makes it great for managing projects like Missing Stars. All in the same workspace you can have beautiful documents for content, databases for managing information, kanban boards and timelines for tracking work, galleries for storing images and other creative assets, and a bunch of other stuff — all with data structured in a way that makes it easy to filter, search and interconnect. And no, this post is not sponsored by Notion — I just love it this much.

With Notion databases, different pieces of information can all be properly linked together, which is so much nicer than using a bunch of Google Docs with only manually inserted hyperlinks to connect them. In the "Characters" database above, each character has a load of database properties to store their vital info. Some of those properties can be relationships to other databases, for example the Sprites database where we can directly link a character bio to all the sprites for that character.

Animated screenshot showing the relationship between Anna's bio and her sprite database.
Anna's bio contains properties for vital info, but also a direct database relationship with all her sprites.

In the Sprites database, we can, for example, create views that filter the sprites by character based on that database relationship:

Animated screenshot showing navigation between different characters' sprites in the sprite database.
Yes, Notion has a dark mode.

Being able to quickly look up a character's sprites makes composing scenes so much faster.

We also use Kanban boards and timelines to track work. For example, when a writer wants to request an art or music asset that would enhance their scene, they do so by using the asset requests board:

Screenshot of a Kanban board showing various creative asset requests.

We've created templates in this database that writers can use when requesting an asset, to make sure they know what information they need to provide. Once they've raised the request, they can track it through its production lifecycle until it is ready to use by looking at which column it's in.

The same database can be linked to elsewhere with different filters — for example, on the art team's page, it is filtered to only show asset requests relevant to artists!

How we script scenes

Besides information and work management, a major problem that Missing Stars had that I considered a showstopper for releasing anything beyond act one was the format of our scripts.

Sprite naming was opaque and difficult to work with, for example an Anna sprite might be named anna U_P2_E4. This gives you some information about the outfit (U = uniform), and poses are generally few enough that numbering them is not a big deal. However, also numbering the expressions, which are more numerous than poses, instead of giving them semantic names means that when composing a scene you'd have to constantly cross-reference what you're writing with a list of available expressions and what they actually look like. Eventually you might be able to memorise the number codes for a small number of characters, but we have dozens of sprite sets, and we're adding more, so this wasn't feasible in the long term.

We were also combining the attributes of a sprite all into one image tag, which is no good. It meant that every time we wanted to change expression, we had to remember the previous pose that was used and repeat that in the script. In Ren'Py, the engine we use, making effective use of the image tag system to separate out distinct attributes of a character's sprites — in our case outfit, pose and expression, with a few extras here and there such as whether Anna's using her earbuds — means you must repeat yourself far less often.

Then there was positioning sprites on-screen and moving them around. This was previously done using a custom transform that had to be copied and pasted every single time we wanted to show, move, or hide a sprite. Those transforms looked something like this:

show katja U_P1_E5:
  xalign 0.85 yanchor 1.0 ypos 1080+425 alpha 0.0
  easein 1.0 xalign 0.55 alpha 1.0

Yep, every time we wanted to show a character we would have to copy and paste a chunk of code like that, and manually calculate what numbers should be used to position them correctly. Blocks almost identical to this appear over one thousand six hundred times in the original release of Missing Stars: Act One. This is a source of personal embarrassment for me — I'm a big believer in DRY (don't repeat yourself), and we were flagrantly ignoring this basic principle of good programming. I wasn't sure how or why we had decided to do it this way in the past, but when I became writing lead, I knew this had to change.

Ren'Py script is a lot like stage direction

Previously, writers would only write the dialogue, and would give a rough idea of the direction they wanted in comments — "direction" in the context of visual novels meaning the composition of sprites, backgrounds, music and sound effects. It would then be up to a programmer to interpret these comments and turn them into valid Ren'Py script. This was totally unnecessary! If you get the foundations of your project right, and set up standardised, reusable transforms and transitions, any writer, regardless of technical ability, should be able to write a good script that will just work in-engine without having to copy and paste anything. You don't need to be a programmer or have any programming knowledge to write Ren'Py scripts, provided that your project is set up right!

I wanted to make sure that it was easy for our writers to do as much of their own scene direction as possible. My ideal was to make it so that our scripts would look close to plain English rather than code — something akin to stage direction was the goal. I spent a lot of time setting up a load of transforms and transitions that could be used with as little as two words of script. Setting them up this way also meant that if, for example, we decided we wanted to increase the speed of our transitions throughout the project, we would only need to make a change in one place, rather than searching for hundreds of instances of a block of transform code.

The easiest way to demonstrate our script format is with an example. Here is how a moment looked in the original release of Act One:

## >Beatrice_happy.png, normal scaling now, centered
show beatrice P1_E4:
  xalign 0.4 yanchor 1.0 ypos 1080+425 zoom 1.0
with SDis
##
n "Still drained from last night, I work to pull the covers up as far as I can."
erik "\"Mind if I sleep some more?\""
## >Beatrice pout
show beatrice P1_E5:
  xalign 0.4 yanchor 1.0 ypos 1080+425
with SDis
##
n "That pout of hers hasn't changed in the slightest."
## >Beatrice small smile
show beatrice P1_E2
with SDis
##
beatrice "\"But we have breakfast!\""

And here's how that same moment looks now:

show beatrice confused at midleft with sdis
"Still drained from last night, I pull the covers up as far as I can."
erik "Mind if I sleep some more?"
show beatrice worried with sdis
"That pout of hers hasn't changed in the slightest."
beatrice happy "But we have breakfast!"

Damn, it's so much cleaner, isn't it? You may notice some other optimisations:

  • We don't unnecessarily use the n character for narration (in Ren'Py narration can just go in quotes without a character code).
  • Characters are properly associated with image tags, so we can change character expressions in-line with dialogue like beatrice happy "But we have breakfast!".
  • The quote marks that visibly surround dialogue in-game aren't required in every single line of dialogue anymore — they get dynamically inserted based on how the character is defined.

In the rest of the code there are many more such optimisations all over the place, as well as other useful functions and technical labels that I created to bring us more in-line with the principle of DRY. I won't cover all of them in this blog post — but I might write another one in the future specifically about our script format, and cover some of the more fun technical things we do in the background. Let us know if you'd like to read that! (I'll probably still do it regardless of your responses.)

Project Nova: the future of Missing Stars

All of this is to say that the original release of Missing Stars: Act One was — to put it mildly — a real mess, code-wise. Some of you probably picked up on that because there are plenty of bugs and annoying technical issues in that release that I'm not proud of.

We knew we couldn't continue to add content to the game in the form that had been released. The codebase was already completely unmaintainable and adding more content in the same format would be a nightmare. So, since that release, we've been working hard on rebuilding Act One, and we called this "Project Nova". Credit to Hamadyne, the studio director, for that absolute banger of a name.

As I'm writing this, it's the 9th of August 2021. Yesterday, I committed the very last of Act One's scenes, reformatted per our new specifications. The script folder has gone from 6.54MB to 5.58MB — that's almost a whole megabyte of pure text that has been removed, purely by following DRY principles. We haven't removed any content from the game, only unnecessary, repetitive code. You need a lot of unnecessary code to make a megabyte.

There's still plenty more work to do, though. We still have to rebuild the entire UI, and there's a whole lot of additional content we want to add to the re-release, too. New poses and expressions for existing characters, whole new sprite sets for characters that had none before, loads of new CGs, and entire new scenes. I know it's taking a while, but trust me, if you enjoyed the original release of Missing Stars: Act One, you are going to love the rebuild. I know I do.