logo

Working on Localization

Whatever I'm working on a project, the issue of translation is always high up on my priority list. Living in a bilingual environment most likely has something to do with it. The exception to this rule of mine is this blog.

Godot has build-in localization capabilities. They are probably fine for most games. I will be using some of them, but because some of the text for my game will be stored in custom node types, I had to implement some extra functionalities.

Here's an example of a dialog node:

Dialog In Progress

In the dialog system I've developed, dialog nodes have two properties that can be translated:

  • Dialog ID
  • Nametag ID

Godot can import CSV files into what's called the TranslationServer. Each string is identified by a specific ID. All a developer needs to do is call the tr() method and it will return the translation in the current language.

My biggest problem was the creation of the CSV files themselves. I needed a way to:

  1. Go through the node tree of each scene (or level)
  2. Identify nodes that have translatable content. Currently, this is limited to three different types of nodes: Dialogs, questions, and choices.
  3. Retrieve the various IDs and the text strings associated with those IDs
  4. Add or update a CSV file with those IDs and text strings

I decided to write a plugin to accomplish this. With a small amount of googling, I was able to add an item to Godot's Project > Tools menu.

Dialog In Progress

When triggered, the plugin will look at all the scene files (extension .tscn) in a specific folder of my project and generate a CSV file for each of those scenes. The IDs themselves are a concatenation of the scene name and the ID specified in the node (in case the ID is used in more than one scene, which is likely considering the amount of text this game will have). The plugin generates a small report to let me know what it did:

Dialog In Progress

Writing this plugin was pretty fun, even though I was fighting a cold and trying to stay awake after only sleeping for two hours the night before. I did break some code by adding the scene name as a prefix to translation IDs, I will have to spend some time in the coming days to fix that. Actually, I should stop here and do it now so I don't forget.

Categories: gamedev

Godot 4 porting progress update!

This took more time than I thought (mostly because I spent my summer biking, hiking, walking, and playing videogames), but I finished rewriting my dialog system for Godot 4 today! I still need to do some work to get localizations working, but for the most part, this is functional!

Dialog In Progress

On the left side, highlighted in yellow, is how the structure of the dialog is defined. At the top is a what I called a Dialog Block. By itself, it's nothing but a container. Inside the dialog block, I defined different kind of objects:

  • Dialog commands (highlighted in red on the right hand side), which I can use to display dialog, along with a name tag and a character portrait
  • Player choices & options for branching paths
  • Single conditions (compare a value with different comparison operators)
  • Multiple conditions (groupings of single conditions, and I have options for AND or OR)

A dialog block can have sub-blocks. I can put conditions anywhere in the block. Each item inside a block is processed in order. If the dialog system encounters a condition (single or multi), it evaluates it. If the condition fails, then it exits the block entirely and moves to the next block.

I added a feature this morning to replace placeholders in a text with variable values. I also spent some time this morning to rework the UI so that it doesn't get messed up when resized.

The game I'm working on (visual novel/dating sim with turn-based beach soccer/football) has four different types of scenes:

  • Dialog section with 3D animated background (typically used when the team's minivan is on the road)
  • Dialog section with 2D backgrounds (used for in car conversations, group meals, and dates)
  • Explorable 3D environment with NPCs and NPC dialogs (for pre-match talks with teammates and possibly others)
  • Turn-based beach soccer from an isometric perspective

I had the first three working last year. I started working on the fourth one a while ago (there is evidence of that further up in this blog), but I don't think I got very far into it.

The next steps I want to focus on:

  • Localizations
  • Adding simple animations when showing/hiding the dialog box, name tag, and character portrait
  • Import the dialog section with 2D backgrounds in Godot 4

This should keep me busy for the next two or three weeks.

Categories: gamedev

To Kill a Seagull

Though I read the book almost 20 years ago, one of the moments I remember most from Yann Martel's Life of Pi is the scene where Pi catches his first fish, and just can't find the resolve to kill it. He ends up wrapping the fish in rags before breaking its neck.

I was confronted with a similar situation a week ago. I was out jogging and, as I was about to turn to go back to the building where I live, I noticed a seagull on the ground. I ran next to it, but it did not fly away. Instead, it tried to move, but just could not. I found that weird, but did not give it too much thought.

About an hour later, as I was going out to get to the bus stop, I saw the bird again. I looked at it a bit more closely, and I noticed that it was in a very bad shape: it's right wing was almost torn off, and its right leg looked broken and could barely move. My guess is that it was hit by a car and somehow survived.

Somebody had clearly seen the bird too, because they left a small bowl of water with some bread in it. The bird was pretty far from it. I moved closer, and I saw the bird try to move away from me. It moved into the entrance. My roommate, who was with me at the time, suggested I move the seagull on the grass, which I did. (And yes, I used hand sanitizer after.) I brought the bowl next to it, so that it could drink.

All through the afternoon, I could not stop thinking about that bird. The more I thought about it, the more I realized that there was no way this bird would ever fly again. The damage to the wing was too great, I don't think it could even move it. It was probably in pain. All this time, my thinking was that I would have to kill it so that it would not suffer pointlessly.

By the time my roommate and I got back, the bird was gone. No trace of it remained. I don't know what happened. My guess is that security saw it and took it away. Or maybe a dog walked by and attacked it. I don't know, and I will probably never know.

One other thing I will never know: Would I have killed it? I'm not sure. Maybe, like Pi, I would have had to wrap it with rags before breaking its neck.

One interesting thing I learned on that day: Seagulls are very, very light, despite being fairly big birds.

Categories: random-thoughts

A Year with Persona 3 - Progress report #1

I recently picked up Persona 3 again (before the recent port announcements), and so far, it’s been an… interesting experience.

I think this is my fourth or fifth time trying to play through it. This time, I decided to take a slower approach: I started playing it on April 9th (the day the game starts), with the goal of playing one in-game day each day. This did not go exactly as planned due to various life issues, and the fact that I decided to switch from FES to Portable in early June means I had to start over. However, after a month and a half, I am finally caught up! I don’t expect this to be the case very long, I want to go to Tartarus today but I won’t have time for that, so July 18th will spill into July 19th, and possibly July 20th as well. I should be able to catch up on Saturday. As long as I play a bit every day, I expect I can keep up the pace (and catch up on weekends if I need to.)

From my perspective, Portable fixes the two biggest issues I had with FES:

  1. When I exit Tartarus, I can go back in at the exact same floor I was at, without having to reach a teleporter
  2. Characters don’t really get tired anymore. They DO get tired, but it’s not as big of a pain as it was in FES (although I discovered recently that it's possible to reset the 'tired' timer by resetting the game -- something to keep in mind if I ever revisit FES).

I’m not a big fan of the other changes though: I prefer the free exploration rather than the menu-based navigation, and the downgrade in the 3D models is pretty jarring when played on a big screen TV. And I do wish Atlus brings back the anime cutscenes for the future ports (but I am not optimistic they will).

My previous attempts at playing this game (all with FES, not Portable) ended at some point in June. I was always hitting a wall in Tartarus, and could never survive long enough to reach the next teleporter, and I would eventually stop trying and give up. This time things seem to be going OK, but I still find the deadlines very stressful for some reason. Knowing that my characters are going on a vacation for the next week (after a week of exams where I was unable to do anything), I started panicking this morning, looking at the date, thinking ‘Oh sh*t, when they’re back from vacation, I will only have 10 days to reach the next stopping point!’ However, I am still enjoying playing the game, and playing it only a small bit every day makes it less of a grind.

Not sure I’m enjoying the social links though. I really feel that the point of them is just to be a sociopath and say whatever the person actually wants to hear, not what should be said, even in cases where the characters are being stupid. (I’m looking at you, Kenji, Kazushi and Mr President of the Student Council.)

Will I keep going, or will this attempt at beating Persona 3 finish like the others before it? I guess I will find out in the coming months! So far, it looks like this might be the one time I make it across the finish line.

Categories: random-thoughts

Getting Into Game Dev Again

After months of not working on my turn-based beach soccer game (except to give it some thought once in a while), I decided to go back to it today. (Work has been nothing but all-day meetings for weeks, and I got bored.)

The first thing I did was to download the latest Godot 4.0 alpha release. The decision to go with Godot 4 was tough. But, even though it is still in alpha, my main reason to go with it is that I'd rather bite the bullet and start updating my GDScript code right away while it is still pretty small, rather than having to do it later when I have tens of thousands of lines of code to review, fix, and test.

Instead of importing the whole project at once, I'm going to bring in things one component at a time. My file organization was… poor, let's say, so this gives me an opportunity to relocate all the elements in a more logical place, and to fix whatever import issue I run into, rather than having to sort through hundreds of errors.

Since I'm not doing anything too fancy with the graphics, I don't think I'm going to run into major issues. There are, of course, drawbacks with this approach:

  • The engine is in alpha, and will change between now, the various beta releases, the RC releases, and the final release. It is very likely I will run into engine bugs. The problem then will be to determine whether the problems come from my code or the engine.
  • Some useful plugins (like unit testing frameworks and to-do manager) will not work at all.
  • Documentation is not up to date.

I started by importing the classes for my dialog system earlier today. I almost immediately ran into a few issues:

  • Export variables for nodes are written differently in the new version of GDScript.
  • The documentation says that the @export_enum annotation should support string arrays but, in fact, it does not. The sample code does not pass syntax check. I had to define my exported enums separately (which, to be fair, I think is a cleaner approach).

So far, that's about all I've done. My goal for the rest of the week and this weekend is to import the various UI elements for my dialog system so I can retest the whole thing and make sure everything works as it used to. I don't expect to run into major issues, but who knows!

Categories: gamedev