Code is a User Interface

Our focus in Terranova has been on how players will feel playing our game. For example, we've worked hard on the LimeJournal site, making sure it looks and feels good. It has to evoke the feeling of the 2000's era web, but it doesn't have to be an exact clone. In fact, we intentionally ditched some of the "period accurate" UI elements for more usable ones. (Janky and frustrating websites do not make for fond nostalgia).

Lately, though, I've been hard at work on code that won't matter one way or another to players. Code that's "behind the scenes"--because while it doesn't change the *player* experience, it has a big impact on the *developer* experience.

That's the topic for this weeks update: how CJ and I wrangle the stories we want to tell into runnable code, and what tools we're using to our advantage.

Early stages

Terranova began as a game jam project for Global Game Jam 2019. First versions of the script were written entirely in JSON code, so they looked a little something like this:

  {user: 'CherryGoRound', message: 'hey'},
  {user: 'CherryGoRound', message: 'what's up?'},
  {user: 'Tourmaline', message: 'Hi!'},
  {user: 'Tourmaline', message: 'How's it going? :D'},

It was a start. This gave us the chance to test out some gameplay quickly, without getting too stuck in the weeds. Since then I've replaced JSON code with our own coding language, and last week I completed what is by now the *third* parser rewrite. Each time I grumble and curse at my computer a lot (getting parsing code juuuust right is quite tricky) but each time is ultimately worth it for how much more productive it makes us in the long run.

Code is a user interface!

While the JSON format got us off the ground, it quickly became cumbersome. This kind of code is great for computers, not so much for humans. It's not easy to write a story this way, and not easy to go back and read or edit it either.

By using a custom language, the same conversation can instead look like this:

  > hey
  > what's up?
  > Hi!
  > How's it going? :D

SO much easier to read write and edit! Along the way I've also introduced new features to the language so that we can, for example, code for user choices:

  choice "What?" do
      > "What?" He raised an eyebrow in confusion. What was that boy thinking...
  choice "Yeah, you'll be sorry..." do
      > "Yeah, you'll be sorry," Eytan said with a smirk at the Scavenger.

Or run multiple conversations at the same time

  branch do
    run import("./")
  branch do
    run import("./")

Code Usability Tips

This update won't be full-blown tutorial on how to roll your own language -- that would take a book rather than a blog post (perhaps another time). Instead I want to share some tips I have for making code usable, and some of the tools I've found the most helpful.

1. Make it Modular

An important problem to solve in using your own language is making sure you have a good way to get your custom scripts into your game.

One of the real gems to have come of the javascript community in the last few years is a tool called webpack. It's fidgety, and a real pain to configure, but it's done one thing incredibly well. It's made "polyglot" codebases 1000% easier. Best of all, you can write your own loaders for webpack to handle your own file formats.

I wrote a webpack loader for our language, which not only makes it possible to load our scripts into our game code, but also makes it possible for us to use webpack to load other files from within our script. This means we can break up our script into multiple chapters and write each in a separate file.

2. Use Syntax Highlighting

Syntax highlighting is a good way to confirm that what you're writing is being understood by the computer. I find that having real-time feedback while you're typing helps prevent a lot of debugging later down the road.

Our code editor of choice is Atom, which is pretty easy to extend with your own scripts/plugins. For syntax highlighting, I'm using a package called 'tree-sitter', which lets you highlight parts of your code based on the parser grammar. I found it a lot easier to reason about than using regular expressions, and a lot less duplicate work as well, since I already had to define the grammar.

(CJ Note: For me, who understands the basics of programming but doesn't have extensive practice, syntax highlighting was a godsend. It helped me visually proofread the stories and overall made me a more helpful contributor to our codebase. Thanks, Matt!)

3. Provide Useful Error Messages

Error messages. If there's one thing I've learned, it's that you'll be reading a lot of them, so do your best to make sure they're helpful.

For parsing our scripting language, I picked a parser-combinator library called Parsimmon. It's not the fastest, or the most "popular", but I picked it because it generally does good job of producing helpful error messages. For example if I forget the 'let' keyword on line 28: 

  27 | 
> 28 | character-intro = do
     | ^
  29 |   @CherryGoRound
Expected one of the following: 
'choose', 'choose-all', 'let', 'run', EOF, a character name,

Another way I've found to do make errors more helpful is to use "fuzzy matching". For example, I wrote a syntax checker that warns me about undefined variables, and also suggests alternatives. So if I mistype...

Error: Variable "wrold-intro" not defined. Did you mean "world-intro"?

I can't count how many times I've had to double-take at error messages that tell me a variable isn't defined, only to realize the reason: I had a typo. This one feature took a couple minutes to implement, and will probably save me hours.


Remember, devs are users too. If you're working on a game, do what you can to make your user experience enjoyable and productive.

Your friendly devs,
CJ and Matt

Get Terranova

Buy Now$20.04 USD or more

Leave a comment

Log in with to leave a comment.