Fugue Devlog 14: Authoring Tools

06.05.2022

Wow, it's been almost a year since I last updated this blog.

I haven't had time to work on Fugue until a month or so ago. Since then I've been chipping away at more tooling. Once the core game mechanics/systems are in place, I'm expecting that most of the time will be spent creating content for the game: writing, modeling, environment design, etc. So I'm building out tooling and figuring out strategies to streamline all of these processes.

Godot makes it easy to develop editor plugins that integrate relatively seamlessly. It's not without its challenges and frustrations but those are more to do with Godot in general than specifically about their plugin development process (see below).

Writing

The game will play out mostly through characters saying and doing things, and these actions need to be specified in a way where I don't need to meticulously program each one. Previously the game's narrative elements used "Dialogue" as the main organizing element, focusing on spoken dialogue, and let me write "scripts" of spoken dialogue lines with a playback system to have the appropriate characters say their lines in order. That ended up being too limiting because I want to write not only dialogue but to specify various actions/stage directions to write scripts that basically describe entire scenes, including character movement and animation, sound and environmental cues, and so on. So I restructured that whole system around "Sequence" as the main organizing element, with "Dialogue" as a sub-component.

A Sequence is composed of "Actions", which include dialogue lines, choice prompts, animation triggers, game variable setting, character movement and rotation, etc. At the time of writing the following actions are available:

  • Line (L): A line of dialogue, spoken by a single Actor.
  • Decision (%): A set of choices that the player must choose from.
  • VoiceOver (V): A line of voice-over dialogue. The difference between this and Line is that it does not require the speaking actor to be present and shows in a fixed position on screen.
  • Prompt (?): Basically a combination of Line and Decision. A line of dialogue is displayed with the decision's choices.
  • Pause (#): A blocking pause in the sequence
  • SetVar (=): Set a state variable to the specified value (strings only). There are a number of targets
    • Global: Set it on the global state
    • Sequence: Set it on the local state (local to the current sequence). These values persist through multiple executions of the same sequence (i.e. they aren't reset whenever the sequence is run again).
    • An Actor: Set it on the local state (local to a specific actor).
  • PlayAnimation (>): Play an animation with the specified for the specified Actor
  • MoveTo (->): Move the Actor to the specified target
    • You can use this to "bounce" the player character if they enter somewhere they aren't supposed to.
  • LookAt (@): Have the Actor look at the specified target
  • ToggleNode (N): Toggle the visibility of the specified node. Can fade it in/out (but looks quite janky)
  • RepositionNode (>N): Move a node to the position and rotation of the specified target. This happens instantaneously...so you could use it for teleportation; but more likely you'd use it to rearrange a scene while it's faded out.
  • TogglePortal (P): Enable/disable the specified portal.
  • AddItem (+): Add an item to the player's inventory
  • PlaySound ())): Play a sound
  • Parable (~): Start or end a Parable (Quest)
  • Fade (F): Complete fade the scene in or out
  • ChangeScene (>S): Change the scene. Because sequences are associated with one scene, this will end the sequence!

Sequences may be triggered in one of three ways: the player interacting with an object (such as talking to an NPC), the player entering a zone/area, or they automatically start when a scene loads ("ambient" sequences).

A "Sequence Script" is a graph of two types of nodes: "Verses", which are lists of actions, and "Forks", which include one or more "Branches" that each have a set of conditions. If a branch's conditions are true then its child verses are executed.

Sequence Editor

Sequences are associated with only one scene. Generally multiple sequences will be related in some way: the might be part of the same narrative arc, for example. So Sequences can be further organized into "Stories" which is basically just a grouping of Sequences, without any significant additional functionality.

The Sequence and Story Editors both make it very easy to quickly sketch out scripts and later refine them. They both have built-in validators to ensure that scripts are correctly specified, i.e. they don't refer to any objects that aren't in the scene, aren't missing any required data, etc.

Sequences and Stories are just stored as relatively simple JSON so they can be further processed/analyzed outside of Godot easily.

I expect that as the game's writing and development continues more actions will be needed. But for now this set has been comprehensive enough.

Example script showing different sequence actions

Textures

Finding source images that fit my licensing requirements and then editing them into textures is a very tedious process. I built a web tool that makes it much easier to find public domain and CC source images (vastly simplified by Openverse), cut out clippings from them and pack those clippings into textures or generate seamless textures by wrapping and blending their edges. It tracks where the clips were sourced from so that attribution is much easier to manage.

Texture Editor: Search

Texture Editor: Clipping

Texture Editor workflow

Music

I'm not at the point where I've given a ton of thought to the game's music, but I do have one tool, dust, that I developed to help sketch out musical ideas. I didn't develop it specifically for this game but it'll be useful here too. It's a chord progression generator/jammer that outputs MIDI so it can be used as an input to most DAWs (tested with Bitwig Studio and Live 11). It helps to get around blank-canvas-syndrome by giving you a chord base to start working with.

dust

Miscellaneous

Items

I've started working on the item system, which is very simple at the moment (and hopefully will stay that way). To manage the items I created an Item Editor, which, though a lot simpler than the Sequence Editor, is just as useful.

Item Editor

Blender scripts and templates

Blender's also been nice to work with because of its support for Python scripts. It's a little clunky to get things integrated, but can be powerful once you're going. In my case I'm mostly using a "quick export" script that helps me avoid the tedious work of keeping exported files organized (navigating to the correct folder, setting the filename, etc) and double-checking my export settings are correct. In the case of items, which require a static icon to show in the UI, the export script automatically exports a properly-cropped render of the item to the item icons folder so I don't have to bother with that at all.

Another small but immensely helpful thing is having a specific template for Fugue modeling work, with materials, cameras, and what not preconfigured. My material settings change very infrequently; I'm usually just swapping out textures, so this saves me a lot of time configuring materials over and over again.

Dialogue Layout System

Not really a tool, but something I've been refining for awhile now. This is the system that determines where dialogue boxes are placed on screen. Many games have a fixed dialogue box, e.g. at the center bottom of the screen, but I want it to feel more spatial, especially as there won't be any voiced lines in the game (too expensive/too much work/difficult to change and iterate on) so there won't be any 3d audio to offer that auditory depth.

Dialogue from Breath of the Wild

As far as I know there is no easy or reliable way to layout rectangles in a 2d space to guarantee that there are no overlaps. Not only should there be no overlaps, but each dialogue box should be reasonably close to its "host" (the actor that's speaking) so that it's clear who's speaking. I have a reasonable expectation/constraint for myself that something like five at most actors should be speaking at once and the game has a minimum viewport size to ensure there's a reasonable amount of space. That is, I'm not expecting that overlaps will be impossible, only that they are unlikely given these constraints.

The approach I'm using now is using a fixed set of anchors for each object and a quadtree to detect collisions. We just try placing a box at one of an object's anchors, and if it collides with an existing dialogue box or object, try the next anchor.

Dialogue layout prototype

As you can see from the prototype above (on-screen objects are beige, off-screen objects are grey, and dialogue boxes are black), it's not perfect. The box 8 at the top overlaps a bit with object 2 — this is due to how dialogue boxes for off-screen objects are handled, which could be refined, but I'm treating as an acceptable edge case for now.

Another shortcoming is how 2d bounding boxes are calculated from 3d objects. Basically I compute the bounding rectangular prism around the object and project that to 2d space. Depending on the shape of the object that may work well or it may end up placing an anchor far-ish from the object's mesh. You can kind of see it in the screenshot below, the "I'm on the move" dialogue box is meant to accompany the smaller NPC, but it's kind of far away. Tighter bounding boxes might be possible but I'm worried about the overhead they'd require. Something to look more into.

Dialogue layout system in action

Unit Testing

Godot doesn't have its own unit testing framework but there are two popular third-party options: gdUnit3 and Gut. They both seem fairly powerful but Gut felt a bit clunky and I couldn't get gdUnit3 to work properly (compile errors, which I chalk up to Godot's weird stochastic-feeling nature, more on that below). I ended up writing my own very simple testing framework instead. It lacks basically all of the advanced features present in other testing frameworks (spies, mocks, etc), but for my needs it's working great.

Tester

Things not covered here

There are still a few key content areas that I don't have a good approach for:

  • Character animation. This is something I'm not very good at and a huge factor in the visual quality of the game. Crunchy textures and low-poly models are a lot more forgiving than terrible animations. There are now deep learning motion capture tools that might work with a commodity web camera, but I haven't tried them yet so I don't know if their output is good and what the workflow is like.
  • Mapping textures. Taking a texture and then adjusting a model's UV map so that it doesn't look warped is also really, really tedious. No idea how to streamline that.
  • Object modeling. This is harder to streamline/automate because there's so much variation. Some categories like buildings and plants could be streamlined through procedural generation via Blender's geometry node system. Fortunately I enjoy modeling so I don't really mind doing this, it'll just be very time consuming. One more general possibility is to figure out a decent processing pipeline for taking free models and converting them into an polygon count that matches everything else. But finding an approach that is robust enough seems unlikely.
  • Character modeling. To make the world feel lively I'd like to have many, many background characters and a fair amount of more important NPCs. This might be doable with some kind of procedural/parameter character variation system (i.e. creating a few key archetype models, then having a script to vary some vertices, scales, etc) alongside with a procedural texture generation system (for clothing, etc). Again, this might be doable with Blender's geometry node system.

Thoughts on working with Godot

I've spent a far amount of time with Godot while working on all of this. My only reference point is Unity, which was very unpleasant to work with. Everything felt so fragile. Small changes can break tons of other things, with no easy way to undo the damage. Kind of like how in Microsoft Word adding a space can mess up your whole document's layout.

Godot has overall felt better than this, but it still has a similar, if reduced, fragility. I've felt discouraged to experiment with new ideas out of the fear that I will just break a bunch of existing code/scenes and have to manually fix everything. I've found that even just opening a scene file can alter its contents—not yet in a way that has caused me trouble, but it's still very different than other programming work I've done, where things really only change if you change them. It's like trying to build a house on moving ground. Version control is a bit of a safety blanket but its effectiveness depends on what changes I can revert to.

GDScript has been a surprisingly pleasant language. It still lacks many features I'd like like sets, first class functions, and iterators (all of which I believe are coming in Godot 4) but usually those haven't been an issue. What has been very frustrating is how Godot parses/compiles scripts. If you have a syntax error in one file it ends up breaking a bunch of other files that depend on it (which is to be expected) but it reports these issues as obscure errors that don't point to the originating error. I'll be inundated with messages like The class "YourClass" couldn't be fully loaded (script error or cyclic dependency). (it will not point you to what this error might be) or mysterious errors like Cannot get class '_'. repeating several times. Then it requires a painful process of trying to figure out where the syntax error actually is by opening scripts one-by-one until I stumble upon it.

This is less likely to happen if you're using Godot's built-in script editor because you're more likely to catch the syntax error before it causes too much trouble. However Godot's built-in editor is really lacking, mainly because you can only have one script open at a time and if you need to edit multiple files at once it requires a very tedious process of manually jumping between files one at a time. So I use an external editor, which does have a language server integration with Godot—so it does catch syntax errors, but sometimes I don't catch them in time, and then these dizzying cascading errors happen.

I've also noticed that sometimes there will be compile errors that are fixed by reloading the project. It feels like these happen because of an unusual (sometimes seemingly random) parse order for scripts, like classes are found as undeclared when in fact they are. I haven't looked into it too much.

That all being said, Godot has been wonderful to work with overall. These frustrating experiences are infrequent, and it sounds like many of them are being addressed in Godot 4. I've enjoyed it way more than Unity and it's an amazing privilege to have access to such a powerful open-source project!


Fugue Devlog 13: More World Modeling

05.14.2021

Very busy with some other things so not much progress as of late. I've mostly been modeling more assets for the world map, which has forced me to think more thoroughly on what each city might look like. I don't feel totally ready to commit to anything I've made yet though.

Modeling and texturing are very time consuming processes, even with the low-res look I'm going for. I'm convinced that 99% of the time spent working on the game will be modeling and texturing.

I find modeling very meditative and enjoyable, though for the map the things I've been modeling are much bigger (e.g. buildings and cities), and for some reason that's a lot more daunting. It might be because larger objects need more details to look convincing. Modeling smaller objects is a lot nicer.

One thing that hasn't helped is that Blender (2.92) constantly crashes. I'm not sure what the cause is because it doesn't save a crash log.

Texturing is the slowest part of the process. Building the textures can be time-consuming: collecting the images, processing them and then assembling them into a single texture, editing parts to be seamless, etc. Most if not all of this can't really be automated or streamlined much further than they already are. One thing I'm trying to keep in mind is that because of the low-res style I can usually get away with low-resolution textures, which makes searching for appropriate ones a lot quicker (in particular: stacking islands together—kind of baffling that this isn't a part of Blender).

I'm also still learning workflow tips for faster UV editing. Blender's built-in UV editing tools are also kind of lacking, but I learned of TexTools which has helped make some aspects of it a lot quicker.

I'm also experimenting with how much I want to rely on free 3d models from elsewhere. For the megaflora on the map (see below) I'm using this model of a borage flower (organic shapes are harder for me to do quickly) but processing and editing it to better fit into the game also takes a decent amount of time.

Megaflora on the map

This city, inspired by the Ganden Sumtsenling Monastery that I visited many years ago, was so tedious to texture, mostly because I was selecting and positioning faces in a really clumsy way:

Tiantai on the map

This pagoda was pretty quick to model, mostly because the UV editing is relatively simple, but also because I'd started using TexTools:

Dagu Pagoda

I'll spend some time watching videos on more UV editing tips to see if I can make the process less tedious.

I also wrote a bit about some of the thinking behind the game for the Are.na blog.


Fugue Devlog 12: The World, the Story, and the Game Mechanics

05.10.2021

The last week has mostly been a lot of waffling about game mechanics. Should characters have skills/attributes? Should there be any combat, and if so, what should it be like? Are there any "skill games" (like the hacking mini-games that are so prevalent in games)? I originally threw around the idea of there being these kinds of mini-games for different character skills, like repairing machinery or cooking food.

I'm leaning towards just sticking to the dialogue system as the main "mechanic" and seeing how far I can stretch that. If there's any combat, it could be interesting to use the dialogue system for that—in games like Final Fantasy 7, combat is basically through a set of menus, which isn't all that different than the dialogue system I've set up. Or combat happening through dialogue choices as skill checks, like in Disco Elysium (h/t Matt). This is just an example—in practice, there will be very little combat if any in the game. Other skill games/mini-games could take place through the dialogue system too. I like this approach because it gives me a constraint (and so makes the task of coming up with mechanics a bit less daunting) and also lets me hone the dialogue system further.

I'm not totally confident in that decision; I have a weird premature regret about not including other mechanics because I'm worried the play experience will feel lacking. At the same time, I know that plenty of games that are basically just dialogue are really, really good. There are a couple of systems, like law and organizational resource management, I want to include in the game, but these don't necessarily translate into new mechanics (i.e. they can probably be expressed through the dialogue system). I think I just have to stick with this decision for now and be open-minded about something changing later.

One reason I'm hesitant about introducing more mechanics is that the branching narrative will already introduce a lot of complexity, requiring a lot more dialogue and scenes and what not for each branching path. It might be too much as-is: I've also spent some time trying to think through the world and narrative to have a better feel for how much branching and different scenes there'll be, and it's shaping up to be a lot.

I also started laying out the geography of the world. The various regions are developed according to a few priorities: the aesthetic priority/what feeling that landscape evokes; its implications on the geopolitics and history of the world; how geologically feasible they are. For the latter point everything is inspired by real formations/environments, but the spatial arrangement needs to be feasible, like where should the mountain ranges be? What biomes should be near them?

To help answer these questions I read a bit about how mountains, rivers, and so on work. There were several helpful guides on mountain formation, rivers and watersheds, general advice on the map design and vegetation. This channel has several videos on not only these topics but also on mineral deposits, wind, and more.

This procedural map generator also helped give some base material to shape.

I figure once I have a map it will make sorting out additional details a bit easier. I can ask it questions or think through how the existing factions and cities would maneuver through the world instead of coming up with ideas out of thin air and then trying to make them all fit together. For example: placing one city (Baita City) on a major river that empties out into a bay (Bao Bay) which is the location of another major city. If trade occurs mostly along the coast of the land, then Bao Bay can unilaterally blockade Baita City, so Baita City might want to develop a land route to the other major city. But perhaps the only viable path is expensive or dangerous to develop so Baita City can't do so until some new technology makes it feasible. But once that happens it dramatically shifts the relationship between Baita City and Bao Bay. Similarly, the character and culture of a settlement is going to be influenced by its geography so this also helps me have a stronger idea of what the cities look and feel like.

Here's what I have so far:

Working out the world geography

Closer view of the mainland

I need to work out the two smaller islands, fill in more details of the mainland, and add in settlements. I had a pretty good workflow going using Blender's vertex coloring to paint on different terrain textures, but for some reason the vertex color limit is set to 8 (really annoying), so I have to figure out a different approach now.

This part of the process is such an emotional roller coaster, at times overwhelming from the possibilities/uncertainty, daunting from all the work different decisions imply, frightening because of all the ways things could go wrong, anxiety-inducing from the possibility of foreclosing certain mechanics or world/narrative features by committing to choices, or satisfying when pieces start to click together. In any case, it's not something that I can rush. The world's regions, factions, and narrative arc are coming together...but very slowly.


Fugue Devlog 11: NPCs and Cut Scenes

04.30.2021

I realized I forgot to build out an important part of the dialogue system: "cut scene" support. These aren't true cut scenes in the sense that they aren't cinematic sequences; here I'm using the term to describe something more like a play: characters moving around, saying things, with audio and animation triggers. This meant expanding the dialogue system to support "stage directions", and that required re-writing a few parts.

The implementation also meant that I had to reckon with NPCs. So far the entities engaging in dialogue were just the player and static objects. Of course it's far more typical that NPCs will be the ones engaging in dialogue. I'd written an Interactable class for things that the player can interact with, but it didn't capture the necessary functionality for NPCs (such as moving around), so I had to re-write that a bit.

Dialogue advancement got more complicated—I wanted to support chains of stage directions (e.g. walking to a place, turning around, walking to another place, etc), so dialogue timeouts and/or manual dialogue advancement don't happen until all of the stage direction sequences are finished.

This all required enough changes that everything felt a bit brittle, so I also spent a bit of time refactoring the code a bit.

Here's a small example of the cutscene system (the moving character is an NPC and the behavior is entirely directed by a dialogue script). The stage directions here are: walk to the designated point, turn to look at the player, then run the "pick up" animation.

Demo of the "cutscene" dialogue options

Stage directions can be attached to any line in a verse:

Adding stage directions in the dialogue editor

Sometimes NPCs should move around etc without saying anything, so I've added a special speaker name of <none> for these cases. The general syntax for a stage direction is npc node name:target node name (excluding directions that don't have an actor, e.g. playing an audio clip). If there are multiple directions for a single actor attached to one verse-line, they're executed in sequence. If there are directions for multiple actors, they're executed in parallel. I'm hoping this doesn't relinquish too much timing control, but who knows at this point.

One problem here is that these are just open-ended text inputs. There's nothing checking that these actor or target nodes actually exist.

I updated the dialogue editor script validation to now require an associated scene, against which all references of speakers/actors and target nodes are checked against:

Updated dialogue editor validation against associated scene nodes

That should now cover all the systems that'll be necessary no matter how the world, narrative, and other game mechanics shape up. Before moving forward with the rest of the development I need to sort those out to figure out what exactly I'll need.


Fugue Devlog 10: Scene Setups and Outdoor Environments

04.28.2021

Karsts test outdoor environment

Scene portals

One of the last two scene components were "portals", i.e. the connections to other scenes. I didn't give this a whole lot of thought; I just used Areas that have an associated scene that the player's sent to. It works fine, though there were a couple small snags at first.

The first was infinite scene looping: when the player leaves out of one portal and enters the new scene, they spawn in the portal of the other scene. That immediately sends them back to the previous scene, where they spawn in the portal, thus sending back to the other scene, and so on. I just needed a bit of code that starts with portals deactivated until the player leaves the entrance portal area.

The second was load times. There's some hanging after entering a portal while the new scene loads. I added a very simple preloading system, where a scene preloads any linked scenes. A cache of recent scenes is kept (right now the last ten, but could probably be higher than that) to reduce loading further. I imagine a more sophisticated preloading system might be ideal later, such as one that preloads not only the immediately linked scenes but those that are two jumps out.

Scene boundaries

The other last scene component was scene boundaries, i.e. preventing players from walking out of the camera's view or falling off the map. I don't know what the best practice around this is, but I just used static bodies and collision polygons; basically invisible walls. Godot's collision polygons were pretty frustrating to use at first, but eventually I got the hang of it.

Top-down view of level boundaries

Grass and wind shader

I took a shot at creating my own grass wind shader to emulate the Ghibli effect I talked about last time and didn't produce anything worthwhile. Shaders are still really confusing for me! I ended up following this grass shader tutorial for Godot which gave me a bit more insight into how to implement a shader for a particular effect, but still way above my knowledge level. In any case, it looks amazing and I gave it a try in the outdoor environment. The performance lags a bit (20-24fps) but it's not nearly as bad as I thought it'd be.

What I need to think on more is whether or not the effect needs to be reproduced in the background, and if so, how best to do it. I might be able to layout levels so that I don't even need to do that. For example here I shaped the terrain so that it looks like you're at the top of a hill, which is why you don't see any more grass in the closest part of the background. This is just a trick so I don't have to render more grass than absolutely necessary (I'm realizing a huge benefit of the fixed cameras is that you can do many of these kinds of tricks).

Throughout this whole process I'm learning a lot about how challenging emulating light is, especially in video games where you are far more resource-constrained (i.e. you need to hit a certain number of frames per second). I've kind of known that light/shadow is where a lot of the focus is in better video game graphics (e.g. raytracing) but didn't really appreciate it until now. I thought that by sacrificing mesh complexity and detail I'd have more room for light/shadow effects, but I had it backwards: the light/shadow effects were always the primary bottleneck. This is all to say that one problem with the grass/wind shader above is that shadows aren't cast onto the grass! But I think I have to let that slide. I did have a sketch of a very simple cloud shadow shader for grass; maybe I'll try to combine it with this one in the future.

Background environments and skyboxes

I'm still working on figuring out my general approach to these outdoor environments. I thought I should design the foreground to be more like an "indoor" environment with elements like the grass shader and then just have a pre-rendered skybox as the background. I'd create all the terrains in Blender, then just export the environment render and load it in Godot as a skybox.

I was not very successful at this. I tried creating a terrain of karsts, which itself was fun to do in Blender. The shader system there is powerful and you can generate a variety of environments with noise textures (probably much trickier to do if they have to have concave elements like overhangs though). The snag came when importing into Godot—it just looked terrible. I think skyboxes are literally just for skies.

The karsts of Guilin

This was really discouraging...I wasn't sure what to do. Eventually I decided to try just exporting the terrain as a mesh rather than as a background image or skybox and that worked surprisingly well.

There are still some issues with textures here (banding, doesn't seem to be the right resolution), and I simplified the geometry too much (way too pointy). But this captures the gist of what I was going for, and more importantly, the workflow is promising!

What's next

This terrain/environment process needs more tuning, but I'm happy with it for now. That was one of the last big unknowns for me in terms of the art asset workflow (the other one is character design, but I'm holding off on that for now). I think I'll take a step back from the art/game development and dig back into the world-building/narrative side.

<< >>