log #9: Assets and Many Improvements

Time interval: 2023-09-28/2024-02-11

A long time has passed from the day I published the last post here. In this time I managed to fix many bugs, add many improvements and most importantly, finally add proper assets to the game. But we will get to the assets later in this post. First I will quickly run through some notable changes.

Removed Telegraphing

As mentioned in the last post, I removed the telegraphing feature. I decided it’s not actually needed to play the game and that it’s just there to annoy the player with bureaucracy. However now the game is a bit harder, because you need to manualy keep track of incoming trains. However in reality, this isn’t that much of an issue.

Pocket Watch Rework

The pocket watch is the oldest item in the game. It’s used by the player to keep track of time, however it also has a hidden ability - fast forward time. This is crucial to the gameplay of the game. You don’t want to wait hours for the trains to arrive. In the past, to fast forward time, you just held down the spacebar. This worked, but it had some problems:

For these reasons I decided to rewrite the watch from scratch. It now comes with two buttons. One forwards the time by a minute and the other one skips the time until a train arrives.

Command Shell

One day while debugging, I thought is would be nice to have the ability to just give myself the log book in the middle of the day, instead of waiting for the day to end. In the past I might have done this by adding a special key handler just for this testing, but I decided I should invest some time into my debug tooling. And that was the birth of my debug shell.

It’s a game window, which allows the user to run commands and see their output, just like a terminal emulator on your PC. Game modules can easily add their own commands to the shell like this:

cmdwin.addCommand("spawn", |this| {
    if len(args) != 1 {
        cmdwin.setStatus("usage: spawn <entity>")
        return
    }
    
    this.w.spawn(worldload.ents[args[0]], this.w.player.e.pos.add({ 10, 10 }))
    cmdwin.setStatus("OK")
})

I’m adding commands as I need them. At this time the shell is already quite capable, especially with my favorite command var, which allows you to control various game variables.

Command Line Interface

When debugging something in the game, I realised I had to do a lot of manual. Edit the code, launch the game, select a save, run all the shell commands I needed for testing and then I could see if my fix did something. To simplify my life a bit, I added these command line flags:

The implementation of the script flag is a case for why I like using signals so much:

f := std.fopen(script, "r")
if f == null {
    logs.err(TAG, "Script not found\n")
    window.quit()
}

dat := str([]char(io.mkFile(f).read()))
std.fclose(f)

gmgr.smgr.loadWorld.register(|dat| {
    lines := strings.split(dat, "\n")
    for i,cmd in lines {
        logs.dbg(TAG, "+ {}", cmd)
        cmdwin.runCommand(cmd)
    }
})

New Assets

And now we finally get to the most exciting part. The new assets. This whole operation of changing all the assets started somewhat randomly. I was browsing the internet and stumbled upon Kenny’s Asset Forge. I thought that this program would be great for someone like me, who isn’t well versed in digital art of any kind. I bought a copy and made a simple locomotive.

Then became the issue of how to put it into the game. Now I don’t just have a single image to draw, I have to choose an image based on the angle of the entity. I solved this by adding an abstraction over tophat images called textures. Textures are automatically loaded by my res.um module. If the image has a JSON file associated with it, it is loaded and used to provide extra info during the texture creation.

Then there was the issue of exporting the model from Asset Forge. The program has the ability to automatically render the image at various angles. Sadly, it outputs multiple images instead of a single atlas. Additionaly, the model has a quite large amount of empty space around this. I fixed this by hacking together a simple (but very slow) python script. This was enough to allow me to rotate my images in game.

After that, I made textures for a few more entities, but then I got to signals. The might seem simple at first, but they require one crucial feature: animations.

Animations

This is where Asset Forge failed me. Theoretically I could implement animations using Lua scripting, but there isn’t a way to automatically run Lua scripts from the command line anyways, so it would all require too much manual work. I turned to Blender for help. Blender has two main advantages: it has out of the box support for animations and it can be very well automated using python scripts. So I imported my Asset Forge models into Blender and made another python script.

Then I edited my first script to support not only rotation, but also animations. The resulting files are now 2 dimensional atlases. The rotations are on the X axis and animation frames on the Y axis. Maybe I will need to implement a 3D bitmap format in the future :).

What’s more, Blender can be launched in headles mode, preloaded with a python script. This allowed me to write a Makefile, which builds all my assets when needed.

Switches

The switches are currently created using a bezier curve in Blender. They don’t look that bad in the game, but I’m unhappy with multiple things.

Switches are one of the bigger features I will focus more in the future.

Asset Browser

I implemented a simple asset browser, which helps me preview the assets at different orientations and play their animations. It is available by running asset_browser from the debug shell.


If you are interesting in finding out more about the development of my game, or want to try playing a demo, you can visit our discord server.