Aug 11, 2017

Suzy Cube Update: August 11, 2017

#SuzyCube #gamedev #indiedev #madewithunity 
Suzy is now trilingual! That's right, I've finally cracked the localization nut! What you see up top is a screenshot of my very first functional test loading and displaying localized text from an external file!

File it Under 'T' for Translation

In order to even get started with adding localization support to Suzy Cube, I had to first learn to read and parse external files. I did some research last week and, finally, decided to go with comma separated value files (CSV) since they are natively supported by Unity and can be loaded as plain text files. 

Once I was able to load, parse and display text from various dummy CSV files, I moved on to creating an actual working spreadsheet in Google Docs from which I can easily edit and export properly formatted CSV files.

It's a start!

The setup for the spreadsheet is quite simple, the left column hold a series of "keys" (more on this later) and the right column holds the text associated with each key which is what is actually displayed in game.

Each language gets its own page which is convenient for two reasons: 1. All pages inherit their left column from the English page, so as new text keys are added, they automatically appear in the other pages and 2. Google Sheets makes it really easy to export individual pages as CSV files and will even append the name of the page after the document name. This produces very clearly labeled localization files for each language.


¿Hablas Français?

Being able to load any arbitrary language file is grand and all, but it's important to know which file needs loading. For this, I created a new scene called the LocalizationLoader. It's now the first scene to be loaded when the game starts up. Through some simple logic, I check first if there is already language preference data saved and if so, I load whatever language file corresponds to it. If no prior language preference has been set, likely because this is the user's first time playing the game, I use Unity's "Application.systemLanguage" to find out the language settings of the device. If the device language matches one of the game's supported languages, I load the appropriate file, if not, I send the user to this placeholder language picker:

Not pretty, but it does the trick!


Show me the Goods!

That, roughly, covers how I handle the external files, but it would be slow to read and parse the files every time I have to display a line of text on screen. Dictionary to the rescue! Dictionaries are a data structure which allow you to store what are called key-value pairs (told you I would tell you more). What this means in this case is that, as I parse the appropriate CSV file based on the current language setting, I store each line of translated text in my dictionary and associate it with the appropriate key. Once it's all loaded, I can then query the dictionary using a key, and get the appropriate line of text as a result.

Loading and storing the text is, of course, only half the battle. I also had to make some changes to actually display the translated text in-game. All lookups to the localized text dictionary are handled by a static function. The function simply takes a key as input and returns the associated value from the dictionary.

Shall I give you a frenching lesson?

Much of the text in the game is handled simply via UI text fields, placed on a Canvas. Because of this, I needed an easy way to add localized text support to arbitrary text fields. For this, I created a super simple script which, upon loading, simply takes whatever text it finds in the text field, passes it as a key to the translation function mentioned above and replaces the contents of the text field with whatever the function returns. For example, in the screenshot above, the actual Title Screen scene shows a text field containing the word "START". When the scene is loaded, the script on the text field object uses the translation function to look up "START" in the dictionary which, if the French file was loaded, returns "DÉPART". The "START" of the text field is then replaced with "DÉPART" and that's what the user actually sees.

¿Cuales son tus preferencias?

This script is able to handle a large portion of the text found in the game. For any text fields which are dynamically populated, I simply handle those on a case by case basis. Having a global static translation function makes it clean and easy to do this and avoid redundant code.

Toe Tapping

Before I wrap things up, I wanted to mention a long standing issue I've finally fixed!

To the beat y'all!

So, some while ago, I introduced you all to these, so called, beat-blocks. The idea being that they appear and disappear to the beat of the music.

Unfortunately, I ran into a bit of a synchronization issue. Now it isn't that the music and blocks would lose synchronization over time or anything like that. Oh, no, it was a subtle problem. You see, depending on the time it took for the level to be loaded, the blocks and music could have their timing ever so slightly offset. So, the blocks might have been perfectly synchronized when loading the level for the first time, but then slightly out of sync after reloading the level following Suzy dying. In case of a reload, the level would be loaded a little faster causing the music to start playing a little earlier causing a slightly different offset between the game timer and the music.

Fortunately, I did some digging last week and discovered some functions I wish I had looked up when I first developed the beat-blocks! In the end, the solution, essentially, came in the form of replacing the use of Time.time to AudioSource.time! That's it!! I mean, I had to make a tiny adjustment to account for the fact AudioSource.time loops over the length of the audio clip rather than increasing forever the way Time.time does, but, otherwise, that's it! It pays to do your research folks! I would have saved a lot of time trying to fix this timing issue if I had simply run a search for "Timing elements to music" on the Unity website before starting!

I think if I ever work on another similar style of game, I'm going to use the level music's timer for all sorts of elements like moving platforms etc. I mean, if you're going to have things moving on a cycle, why not time them to the music? Right!?

Well, that does it for this week's update. As I had hoped, working from home again helped me stay focused and get some good work done. I'm likely going to be putting out a new test build this afternoon so stay tuned for more on the results of these upcoming tests. See you all next week!





No comments:

Post a Comment