Fugue Devlog 25: More generic player handling, inventory UI, and more.

06.02.2023

Hard to believe it's been less than three weeks since the last update. I've managed to finish a lot even though I feel like I haven't had much time to work on things (especially with Tears of the Kingdom out now).

Better character movement

I mainly was procrastinating because I needed to fix some issues with the AI/NavMeshAgent character movement and I had no idea where to start. NavMeshAgent is Unity's built-in character pathfinding system, and it's not bad. I'm using it with root motion (i.e. the animation drives movement rather than direct modification of the character's world position), which makes working with a bit more complicated.

The problem was that when the character was running they would never be able to turn fast enough to match their computed path. So they would overshoot things and run into walls while making large turns. There is an angularSpeed setting but it didn't seem to have any effect. I toyed with having root motion drive the rotation too, but it ended up being too complicated; and even so, the direct rotation looks fine.

What I ended up doing was manually controlling the character's rotation, rather than letting NavMeshAgent do it:

_agent.updateRotation = false;

// ...

// Called in Update
private void SyncAnimatorAndAgent() {
    // ...

    // Handle turning ourselves, rather than
    // delegating it to the NavMeshAgent;
    // this allows us to have more control.
    // Turn faster if running.
    var step = maxTurnSpeed * Time.deltaTime * (shouldRun ? 2 : 1);
    Vector3 direction = _agent.steeringTarget - transform.position;
    direction.y = 0; // Ignore up/down
    var targetRotation = Quaternion.LookRotation(direction, Vector3.up);
    transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, step);

    // ...
}

This lets me change the rotation speed based on how fast the character is moving (i.e. running or walking). It also lets me have conditional behaviors based on the magnitude of required rotation. For example, if the character needs to turn 180deg, then I can have them turn before starting to move, which also looks a bit more natural.

There's definitely a lot more tweaking to be done, but I'm happy with the results for now.

Changing the player character

One feature I wanted to have was for the player to swap out what character they're controlling. Like all JRPGs I want party composition to be an important element of gameplay. This fortunately wasn't too complicated, in part because of the character generation workflow I set up earlier (always nice when that work pays off!). Because all characters share essentially the same skeleton, I just need to swap to the new character's skeleton (and model) and update a couple other parameters (how tall they are, etc).

The one tricky bit was rebinding the new skeleton to a plugin I'm using that controls where the character is looking. There's no built-in way to do this rebinding. Fortunately after digging through the source code I managed to find a solution:

public void RefreshLooker() {
    // NOTE this assumes the path is always the same,
    // which it might be due to `clay`
    _looker.LeadBone = transform.Find("Model/Armature/Root/pelvis/spine_01/spine_02/spine_03/neck_01/head");
    _looker.RefreshLookBones();
    _looker.InitializeBaseVariables();
}

I did have to change RefreshLookBones from an internal to a public method though, so it's not ideal.

Inventory UI

The biggest feature is the inventory UI, which is an RE-style spatial inventory system. I like it better than other inventory constraints, like a weight limit, though it can still be tedious to manage (need to find some good C# bin packing libraries to implement an auto-sort).

This was my first time making a substantial UI in Unity, using their UIElements system. It felt very weird to use what are essentially HTML and CSS (UXML and USS, respectively), and it took me some time to figure out how exactly I should structure things. The system needs some React or Solid-like framework to round it out.

My current approach is to avoid the UXML as much as possible and build the UI in C# instead (essentially creating "components" by inheriting VisualElement). Hopefully the UI demands remain simple enough that this remains viable.

The C# approach made it easier to design the inventory UI as a modal interface, i.e. different inputs change the UI's mode, which makes it easier to ensure that there are less invalid states. The normal mode is just moving the inventory cursor around and entering other modes. For example, pressing the "move" button over an item in normal mode changes the mode to Moving, which then remaps inputs for that particular context (e.g. what was once the "use item" button is now the "place item" button). This feels like the cleanest, most extensible approach, but goodness does UI always take way more code than I anticipate.

I'm not going to include a screenshot of the inventory UI because it looks terrible, but it is passing all its tests!

Dice System

I played Citizen Sleeper and enjoyed it. The dice system there is great, and makes much more sense for the type of game I'm working on than the ones I mentioned in the last update. I'm definitely going to riff off of it for Fugue's skill check system.

Other bits

  • I also implemented a proper scene manager, which handles changing scenes and loading in the player character. Took longer than I expected, but seems to work well enough.
  • I changed the verses script deserialization from JSON to YAML. The JSON deserialization code was a mess, and possibly kind of slow? I found a fast YAML library that vastly simplifies the script importer. YAML's more pleasant to look at too.

Next steps

Two main things are on my mind:

I want to start implementing the dice/skill check system so I can finally test it. It's the game's core mechanic, so I need to make sure it makes sense before running too far ahead.

I want to use the Unity editor as little as possible. It's kind of janky and just slow for me to work in. I don't want to learn all of its shortcuts and wait for it to respond. The way I see it there are three primary activities I need the editor for:

  1. Compiling C# and running tests
  2. Building scenes
  3. Building/testing the game

There may be a way around 1) but I have a feeling it would be very painful to set up, and perhaps not much of an improvement in terms of speeding up the development loop. There's no way around 3), but it's a relatively infrequent activity at this stage so I'm not worried about it.

I expect 2) will be where most of my Unity editor time will be after I've implemented the game's core features. It's unpleasant, especially in contrast to 1) where I'm mostly writing in my comfortable text editor and hopping to the editor only to compile and run tests. I would love to have something equivalent to my text editor but for building Unity scenes.

Fortunately this might actually be possible. Unity's scenes are really just specialized YAML files, and I've already built part of a Rust parser for them in verses. In theory you could set up an external program to edit those YAML files. I'd need to be careful in scoping the tool, however. I don't want to end up re-implementing a huge part of Unity's editor.

For version 1 of umarell, which is what I'd call this tool, I'd probably just want to modify transform information (position, rotation, scale). I'd still have to import objects into the scene in Unity first (which is a relatively infrequent activity), to ensure that they're properly initialized with all the right properties and what not. Then I'd pop over into umarell and position things.

I imagine the scene building workflow would be something like:

  1. Build the static set elements in Blender.
  2. Create a new scene in Unity and drop in all the objects that should be in the scene, and perhaps also attach any scripts/components they need.
  3. Open the scene in umarell and start setting all the objects up.

I looked a bit into what the Rust ecosystem for something like is like, and this might be possible with rend3 and egui. It's not a high priority at the moment—I need to finish getting all the systems down—but could be a fun project later.


Fugue Devlog 24: Puttering Along

05.12.2023

I've been slowly chipping away at porting things to Unity, sketching out new game systems, and tweaking the verses script syntax.

verses Updates

For verses I'm constantly changing things to try and get the most concise syntax I can, without it becoming too hard to skim.

Two of the biggest changes are:

  • Multiline remarks, which make it easier to write consecutive lines that are said by the same actor.
  • Allowing conditionals to be inlined into a verse. Previously any conditional behavior had to be defined as a branching action, which would have to connect to a different verse entirely.

For the second change, the way it worked before was clunky. Say I want to have a character say something if foo==bar and then return back to the normal dialogue.

@root
Branch:
  - @next_verse
    If: foo==bar
  - @default_verse

@next_verse
[Character] Foo equals bar!?
Branch:
  - @default_verse

@default_verse
[Character] Continuing the convo...

This is a lot for what is essentially just a minor aside. The changed syntax introduces a new action, Aside, which groups actions under a condition, and then resumes the normal flow. So that script above would now be written:

@root
If: foo==bar
  [Character] Foo equals bar!?
[Character] Continuing the convo...

There are some other more minor improvements, like immediately specifying dialogue choice consequences/outcomes. Previously this would also be clunky, involving some variable being set by the script runner and then checking for this variable later on. Really roundabout and relies on things happening that aren't defined in the script itself. Now it's just a matter of:

[Character] Make a choice
  - A choice
    Outcomes:
      AddItem: SomeItem

The other change is better variable namespacing. Previously there was just "local" (local to the script) and "global" (set across the entire game). The scopes are now "script" (local to the script), "story" (local to the story the script is a part of, e.g. within the context of a mission), and "global". This will hopefully make managing script variables easier and cleaner.

The last major verses update is that I actually got Unity scene parsing working well in Rust, and ended up stripping out Godot scene parsing entirely (not worth maintaining both).

Semi-related, I also switched entirely from vim to nvim/neovim and set up custom highlighting and in-editor parsing/validation for the game scripts:

verses in nvim

Unity Updates

The Unity version of the game is more or less at parity with the Godot version now. I've also implemented a very rough first draft for several other game systems (time, missions, inventory, etc). For inventory I'm tentatively using an RE-style spatial inventory, since it feels like a better way to limit inventory than weight/encumbrance, and is visually more interesting to manage. Still need to build out the UI for it though.

Because I'm expecting development to take a long time I switched over away from the LTS version of Unity to the 2022 version. So far it's been a better editor experience—less laggy, though the compilation loop still sucks.

In general my experience with Unity has been very positive. I've been able to structure my code in a better and more reliable way than with Godot. The renowned Scriptable Object talk was extremely helpful in designing an architecture that feels easy to build on. Unity's component-based system is much nicer for decoupling, and having access to a more mature programming language is worth a lot (C# features like interfaces are very handy).

I'm still getting used to how Unity does things, like addressables and what not. There is a lot that's clearly meant for very advanced, high-end games, and it's hard for me to discern what's overkill for me to adopt now and what I'll regret not using further in the development process. But that's just how these things go.

In general my focus now is on lowish-level framework stuff (currently trying to get character movement to work correctly). There are still many undetermined mechanics that will require playing around with, so trying to design things to flexibly accommodate different possibilities, or at least get a good foundation to experiment on. At some point soon I'll cross over into actual gameplay and eventually writing (I hope).

For now the current near-term roadmap is:

  • Get character movement working well
  • Start building out more UI

Dice mechanics

I haven't thought much on mechanics as I've been in implementation-mode for the past several weeks, but I have some more thoughts on missions, the energy mechanic (a stand-in for some kind of resource management mechanic). I find myself coming back to the Mario Party character-dice system and the system from Dicey Dungeons where you assign dice to slots. I like these better than regular D&D-style dice rolls because there's some randomness but enough room to strategize so that you have more agency over outcomes.

Dicey Dungeons

Super Mario Party character dice blocks (via)

While searching for the images above I came across a game called Slice & Dice where you construct the dice yourself!

Slice & Dice


Fugue Devlog 23: Migrating to Unity & Clay

04.14.2023

Unity screenshot

Migrating to Unity

So the last post was about migrating to Godot 4 and now I've gone and started migrating to Unity. At the start of this project I was deciding between Unity and Godot and ended up going with Godot for a few reasons, a big one being that I had used Unity maybe 8 or so years ago (I was originally developing The Founder in Unity) and hated it. At the time I believe Unity's UI support was basically non-existent and the game was rather UI-heavy, so it was a frustrating experience. I think the Unity Editor for Linux was also in beta, though I was probably using OSX at that point.

But recently I figured Unity was worth another look, since this is a 3D game and less UI-heavy, and surely things have improved in the interim years. And they have. The Unity Editor for Linux works (more on this below) and the Unity ecosystem is of course more mature and battle-tested.

Some other considerations:

  • Console/closed-platform support. A big downside with open-source game engines like Godot and Bevy is that they can't yet build for consoles, in part because these platforms require their integrations be closed-source (or something along those lines).
  • Built-in animation retargeting. It's been a struggle to figure out how to retarget animations for my generated characters. I was using the Auto-Rig Pro addon for Blender, which works great but wasn't giving me the right root motion results in Godot. It seemed very complicated to debug. Unity, on the other hand, has a built-in animation retargeting for humanoid rigs that works great.
  • C# is a more mature and strongly-typed language. Godot 4 improved a lot with GDScript but ultimately it still feels too in-development. It has typing support but many important types are lacking and it just doesn't feel as solid as a proper strongly-typed language. C# isn't the prettiest language to work with, but it feels like a good, stable foundation for a game.

And a couple other bonuses:

  • I haven't done much UI work just yet but Unity's new UI Toolkit system is interesting...the styling and layout is basically CSS.
  • Package management is also nice with openupm.

So far I don't have much bad to say about my new Unity experience. The biggest issue is that the Editor still feels kind of janky in Linux; it's rather slow and a bit buggy. It might still technically be in beta. I can't, for example, drag and dock tabs into panels, which is annoying (see below for a workaround). Not sure if this is a limitation with my window manager or what. Godot's Linux support on the other hand is amazing; their editor feels responsive and stable.

Setting up my text editor was tricky but it's working alright now, except that I have to restart nvim to properly process new files (see this issue) and that the language server takes a long time to start.

In general the development loop feels slower than Godot, largely due to the increased compilation times. There are some ways to improve these times (mainly by essentially bundling your code into sub-packages with assembly definition files), but so far I haven't noticed a major improvement (though it's probably not apparent until your project gets quite big). It's not the worst thing but it does make development drag a bit.

And a very minor gripe is the number of artifacts that Unity produces. Tons of .csproj files and other folders. Godot was really lean in this regard; I believe all these generated artifacts were confined to a hidden .import folder.

(I'm also stubbornly not doing the C# new-line curly brace thing)

I'm still getting familiar with most of Unity's core concepts—how input handling works, how unit testing works, etc—and so far haven't encountered any major road blocks. The documentation is ok, but I've still had to do a bunch of forum digging to figure out exact approaches to some problems.

I suppose one bit of weirdness is that there are two different UI systems available; one seems more appropriate for more static/simpler UIs (this is the CSS-like UI Toolkit system) and the other (soon-to-be legacy? idk) is a canvas-based UI system (closer to how UI works in Godot). The latter seems more appropriate for more dynamic interface elements—I'm using them for dialogue boxes primarily because they can use TextMeshPro which gives fine-grained control over text meshes. I think TextMeshPro is supposed to be integrated into the UI Toolkit? Maybe it is already? I don't know.

I am a little sad to stop using Godot...it's great and I'm excited to see where it goes in the next several years. It's already amazing how full-featured it is—perhaps it could become a Blender equivalent for game development. If Unity and Godot were at closer feature parity (especially with the three benefits listed above) I'd prefer Godot. But for now Unity makes more sense.

clay

I ported the character generation system from hundun into its own Rust package, clay, so I can generate characters independently of hundun and via, for example, a script in bulk.

Not much interesting to say here, except maybe on how I bundled static assets into the Rust binary. The character generation part relies on several Blender Python scripts that I need to know the paths for (so I can call blender /path/to/the/python/script.py). The trouble is these files could be anywhere, and I don't want to hardcode or constantly pass in the paths to these scripts.

What I do is package them (and other static assets needed, like brush images) with the compiled application using the include_dir crate. Then they can be extracted to a known location when needed:

/// An interface to run scripts with Blender.
/// Most of the character generation actually happens
/// in Blender with Python scripts. These Python scripts
/// are included in a kind of hacky way (see below).
/// This method means that if the Python scripts are edited
/// the Rust program needs to be re-compiled to include the
/// latest script versions.

use include_dir::{include_dir, Dir};
use std::{
    io::Error,
    path::{Path, PathBuf},
    fs::{create_dir, remove_dir_all},
    process::{Command, Stdio, ExitStatus}
};

const BLENDER_PATH: &str = "/usr/local/bin/blender";

// Where to extract the included Blender scripts when calling
// the `blender` command.
const EXTRACT_SCRIPTS_PATH: &str = "/tmp/clay-blender";

// Bundle the Blender scripts with the Rust binary.
// When needed we'll extract the scripts somewhere we can point to.
// This is a kind of hacky way to avoid juggling filepaths if this
// library is used elsewhere.
static BLENDER_SCRIPTS_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/assets/blender");

/// Path to a file included in `BLENDER_SCRIPTS_DIR`.
pub fn bundled_file(path: &str) -> PathBuf {
    format!("{}/{}", EXTRACT_SCRIPTS_PATH, path).into()
}

/// Note: this assumes that `script` is bundled as parst of `BLENDER_SCRIPTS_DIR`.
pub fn blender(blendfile_path: &PathBuf, script: &str, env_vars: Vec<(&str, &str)>)
    -> Result<ExitStatus, Error> {
    // Extract the Blender scripts
    if Path::new(EXTRACT_SCRIPTS_PATH).is_dir() {
        let _ = remove_dir_all(EXTRACT_SCRIPTS_PATH);
    }
    create_dir(EXTRACT_SCRIPTS_PATH).unwrap();
    BLENDER_SCRIPTS_DIR.extract(EXTRACT_SCRIPTS_PATH).unwrap();

    let mut cmd = Command::new(BLENDER_PATH);
    cmd.args([
             "-b", &blendfile_path.to_string_lossy(),
             "--python", &bundled_file(script).to_string_lossy()
    ]);

    for (key, val) in env_vars {
        cmd.env(key, val);
    }
    let mut proc = cmd.stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .spawn()
        .expect("blender command failed to start");

    let res = proc.wait();

    // Clean up extracted files
    let _ = remove_dir_all(EXTRACT_SCRIPTS_PATH);

    res
}

A very hacky way of setting the editor layout in Linux

I did manage to figure out a very hacky way of docking tabs in the end. You can export the current editor layout to a .wlt file, which is essentially just a YAML file (as far as I can tell, it's the exact same format that Unity game scenes use). So say for example I want to dock the Test Runner to be in the same dock as the Inspector. I'd open the Test Runner—which opens in a new window by default—and then save the layout. Then I'd edit the .wlt file. The YAML file consists of individual subdocuments, separated like so:

--- !u!114 &1
(some yaml)
--- !u!114 &2
(some more yaml)
--- !u!114 &3
(etc)

The key here is these dividers preceded by ---. The number after & is the id (specifically, the fileID) of that component. Some of these represent entire windows (so in my case I'd have a main editor window and the smaller window spawned for the Test Runner), others represent docks (which I identify by their m_Panes property), and others represent the tabs themselves.

The gist is to look for the Test Runner tab (by searching for m_Text: Test Runner) and then getting the id of that component (say it's 15). Then I look for the Inspector tab (searching for m_Text: Inspector) and get that id (say it's 20). Then I look for a subdocument where {fileID: 20} is an item under m_Panes. That will be where the Inspector tab is currently docked. I just add another entry below it: {fileID: 15}.

I search for the document to other references of {fileID: 15} and then clear anything that references those parent ids, recursively, just to ensure that there aren't multiple elements referring to the same component (i.e. deleting the dock that used to contain the Test Runner, and deleting the window that used to contain that dock).

Then save and load the layout in the editor.


Fugue Devlog 22: Migrating to Godot 4, Tooling Changes, and the Skill Check System

03.31.2023

Gliss, and Migrating to Godot 4

Godot 4 was recently released and brings with it many improvements to GDScript (thankfully addressing most of my pain points with the language), better performance, and several other changes that I can't yet appreciate. Because the game code is still pretty basic I figured it'd be worthwhile to just migrate to Godot 4 now. It ended up being a good opportunity to refactor some things now that I have a clearer idea of what systems need to be included.

Semi-related to this refactor: I've decided to first work on a smaller demo/prototype called Gliss (for glissando) to test out mechanic ideas and the overall development process. The hope is to flesh out all the game designs and systems and then Fugue will just a bigger version with few, if any, new systems & mechanics. My ideal outcome is that Gliss establishes the framework, and the expanding it into Fugue is mostly a matter of authoring more content—characters, locations, etc.

Overhauling the Sequence Editor (verses)

As I was starting to write out more sequence scripts I found the existing editor (below) to be clunky. And when I need to define new script actions (such as skill checks, more on that below), it requires a lot of lift to define the new frontend components and inputs. Just super unwieldy.

The now old sequence editor

I revisited an idea which was to define a special plain-text format for sequence scripts. I never pursued it because I was daunted by the prospect of writing my own custom parser...but I had to do that anyway to parse Godot's .tscn files, so what's one more parser?

The process of writing the parser with nom actually wasn't too bad. The trickiest/most frustrating bits were dealing with error handling (just haven't fully grokked error handling in Rust in general) and handling recursive parsing (couldn't figure out a good approach for that and ended up just putting a fixed limit on recursion depth). But otherwise once you get a handle on the combinators (especially with their indispensable guide) the whole effort becomes intuitive.

I also implemented script validation which checks for common issues like requesting nodes or entities that don't exist in a given scene, or referencing files that don't exist, or bad sequence script structure (orphaned nodes, invalid branching, etc), and even typos. The goal is to have some assurance that there will be minimal runtime sequence errors.

The end result is verses, a custom Rust crate/program for parsing and validating gliss/fugue sequence script files. This program can be used to parse scripts to JSON (to import them into Godot as custom resources), and the previous hundun sequence editor (pictured above) is now a relatively thin UI on top of it:

The new sequence editor

Now the script is just written in the editor on the left and the parsed sequence graph is displayed on the right. Validation happens live. Now the process of writing sequence scripts is less stop-and-go, more fluid than before. It also means that if I need to quickly edit a script file I can do it easily with a text editor.

The text editor itself is made with CodeMirror, which is an intimidatingly powerful editor library. Here I've set it up to have custom syntax highlighting and custom autocomplete, which lets me autocomplete, for example, actor names.

The Skill Check System

I began working out the skill check system—the raw skill check mechanic itself is very straightforward, just compare the skill level and difficulty and roll to see if you succeed. I designed the actual rolling algorithm to be visualizable, so you're not just seeing your odds and then the result. Instead, a rough summary is that the skill difficulty sets a number of successful flips you have to achieve, and your skill level determines how many tries you have. So for a skill level of 3 you get 3 tries. Each try lasts until it fails, so it is possible to succeed at a challenge of difficulty 4, even with just a skill level of 3. The probability of a successful flip is tuned to produce the following overall skill check success probabilities (i.e. each flip is not 50/50):

Skill check probabilities

This chart is kind of confusing, but each line represents a different skill level. For example, say the skill is "Lockpicking". Say your skill level at that is 3 (s=3). You have about a 100% chance of succeeding at any skill check with a difficulty less than 3. You have a very good chance for difficulty of 3 and about a 60% chance for a difficulty of 4.

I'm hoping that the modifiers will be where this system gets more interesting. The modifiers themselves are just straightforward increases/decreases to skill levels, but I want them to be organized in a way that 1) requires interesting character build decisions (through skill progression) and 2) reflects a character's beliefs about and experiences in the world (that is, characters don't just mindlessly/mechanically get better at lockpicking; rather, as they get better it changes how they see the world; and how they see the world affects their proficiencies in certain skills).

I need to think more on 1), but the general idea is that each skill has one or two underlying "proficiencies" that are shared with other skills. For example the two proficiencies for "Lockpicking" might be "Hand-Eye Coordination" (which also underlies the "Medical" skill) and "Puzzle-Breaking" (which also underlies the "Digital Evasion" skill). At the 3rd and 5th skill levels you can pick which of the two proficiencies to take a bonus in (a more expansive build), or you can pick a perk (e.g. you get one free lockpicking attempt without using a lockpick, for a more specialized build). This isn't all that different from typical skill systems.

Whereas 1) are intentional decisions the player makes, 2) reflects playstyle patterns, and so is more indirect. If a character frequently uses intimidation actions, or frequently witnesses them, they may pick up an "insight" of "Violence is the Answer", which gives bonuses to violence-related skill checks and penalizes non-violent ones. If they are constantly lockpicking and hacking, they may pick up the "Security is a Fiction" insight, which buffs these skills, but the anxiety of this realization means they stress more easily (which connects to the Energy mechanic, which I'm still working out).

Refactoring chargen into clay

What I'm working on now is refactoring the character generation system (formerly chargen) into a separate crate called clay. This is to also streamline some things as with verses, e.g. make it easier to quickly edit characters and bulk generate large amounts of them. hundun will again be mostly just the UI on top and not handle the actual character generation logic.

Next steps

  • Finish porting clay
  • Figure out the character export/import into Godot workflow (running into some root motion issues here)
  • Re-implement sequence script importing using verses
  • Implement skill check mechanic for testing
  • Continue developing the core mechanics (e.g. the Energy mechanic)
  • Probably some other stuff I'm not remembering

Fugue Devlog 21: More Character Generation Work

12.16.2022

Chargen fixes and improvements

I've had to fix a few more issues with the character generation system, but also added some improvements.

One of the main problems was with the poly reduction (the Decimate modifier), which can make some interactions between clothing and the character body unpredictable. Basically when clothes is close to the body and you have the character in different animations/poses, sometimes the underlying body can clip through the clothes. If the clothes conform to the body's geometry this is less of an issue because you won't, for example, have pointy geometry that can poke through more rounded geometry.

The way MakeClothes handles this is with its delete groups, where you basically flag specific vertices to be deleted when wearing a particular clothing item. The underlying vertices are redundant because they're covered by the clothes, and no vertices there means no clipping.

The Decimate modifier complicates this in two ways:

  1. It may modify the body and clothing geometry in different ways, such that two surfaces that conformed are now shaped differently, so now the surface underneath may poke out through the surface above (consider that a smooth rounded surface has a higher poly count than a pointy pyramid-like surface; decimate converts these rounded surfaces into pointier ones.
  2. It may modify the geometry such that delete groups end up deleting too much of the mesh.

So far these haven't caused major problems; I've been able to solve them by adjusting delete groups or by slightly scaling up clothing/hair so there's margin for pointy surfaces to stay concealed.

Aside from this I made a few minor quality-of-life improvements. I added a "Quick Preview" option which only converts the first frame of each character animation, cutting down character generation time by 30 seconds or so (really useful when tweaking things). I also added a way to generate clothing previews from within Blender and see them from within the character generator:

Clothes preview generation and picker

I also added a way to colorize clothing. Solid-colored clothes are made using a base forest green texture, (#1e3b2f), generated using the texture painting algorithm I described a few posts back:

Base solid color texture

In the UI you can pick a color and then that forest green is replaced in that texture:

Picking the color of clothes

This lets me squeeze a bit more mileage out of clothes meshes without needing to make different textures for every solid color I might want to use. The system is flexible enough that I can create more complicated textures that also use this system to re-colorize them, so long as they're based around that same forest green.

And I finally figured out why my webpack builds were taking so long. They took anywhere from 5-10min for the initial build, now it's just a few seconds1.

Screen shader tweaking

I'm trying to settle on screen shader settings for the game. There is already some downsampling happening here (well, the max resolution is set to 720p, so pixel cruchiness is more visible when rendering to higher resolutions than that), and I also had the color depth reduced to 4 for awhile. It's a nice visual effect but might get fatiguing, so I played around with some other settings.

Common settings across these renders: resolution=1, saturation=1.1, dithering=false, brightness=1.

(I did do some comparisons with dithering=true but this didn't make much of a difference except introducing some artifacts at lower resolutions.)

no effect d=4,c=1.1 d=6,c=1.1 d=6,c=1.2 d=8,c=1.1 d=8,c=1.2

I think I'll probably stick to depth=6 (since it still introduces some interesting banding on the wall, but not too aggressive like it is with a color depth of 4) and contrast=1.2 for now. It may change as more environments and characters are created.

Test characters

I started making more use out of the character generation system to see if anything else needs improving. Part of this trial run required creating more clothing and hairstyles to try out. These characters are all randomly generated (except for the clothing/hair, which are added afterwards):

Next steps

I'll likely leave the character generation system here for now, though I'll need to make a lot more clothes and hairstyles eventually. The priority now is to figure out the story and game mechanics since those influence everything else moving forward.



  1. The main change was that the typescript-loader is very slow unless it's set to transpileOnly: true, I don't fully understand why. 

>>