In Game Dev Tycoon 1.1.0 we implemented a couple of important features.
Implementing the longer game options
This is one of these features which seem pretty easy from outside but are quite tedious to implement and require extensive re-testing of the game. From the feedback we’ve received we knew that the speed of the game was too fast for a lot of players and after reviewing the game we came to the same conclusion. The biggest influence of the game length is that platform (gaming console) releases need to happen at different times. In the original version the timing for platform releases were hard coded. Initially we thought about simply changing the game to run longer by default but then you would have serious issues when continuing a game from previous versions. Obviously making previous save games invalid is not an option for a game like this so we had to keep the ability to run at 25-years while also adding a longer game option.
Changing the game so that platform releases happen at a calculated time is easy but we have a lot of text in the game which refers to the relative time when a platform release will happen.
Example
Today, Ninvento has confirmed recent rumours and announced their plans to release a new home gaming console called ‘TES’ early next year. […]
Obviously if the time when platforms are released is dynamic you cannot hard code the sentence like that. With the longer game option the actual release might happen in the middle of the year instead of the start of it.
Initially we thought about simply changing the text to say soon or in the coming months or something of that nature but when we tested that it became obvious that it is very repetitive and worse, doesn’t really give you an indication when something will happen.
There are of course other solutions to tell a player when a platform release will happen. You could introduce a timer or simple show them the exact date in the message but all of that didn’t feel right. We liked the feeling that you are reading a news report in the game so we wanted to keep this.
What we ended up doing is to dynamically create text fragments such as next month, in the coming months, late this year, early next year etc.
This was in fact much easier than expected. We simply created a helper method which calculates the text fragment based on the actual dates.
This is the usage of the getETADescription method. The first parameter is the current date and the second parameter is the target date. These dates are all based on a 25-year game length and the methods which transform the strings into actual in-game dates take care of different game lengths.
"Today, Ninvento has confirmed recent rumours and announced their plans to release a new home gaming console called 'TES' {0}.\nThe console features cartridge based games and a uniquely designed gamepad." .format(getETADescription('1/10/2', '2/1/2'));
This is the code that calculates the fragment
//since game length is dynamic dates of the events change as well and that's why we have to dynamically build some of the strings. var getETADescription = function (msgDate, targetDate) { var now = GameManager.company.getDate(General.getWeekFromDateString(msgDate)); var then = GameManager.company.getDate(General.getWeekFromDateString(targetDate)); if (now.year != then.year) { if (Math.abs(then.year - now.year) > 1) { if (DEBUG) { throw 'unexpected option'; } return "in the coming years"; } if (then.month <= 3) { return "early next year"; } else if (then.month >= 8) { return "late next year"; } return "later next year"; } else { var diff = then.month - now.month; if (diff === 0) { return "later this month"; } else if (diff === 1) { return "next month"; } else if (diff === 2) { return "in two months"; } else if (diff <= 4) { return "in the coming months"; } else if (then.month == 12) { return "at the end of this year"; } return "later this year"; } };
We use this technique in 21 different notifications and it works well. We had some initial concern that this might make translations harder to get right but after some investigation we don’t think that will be an issue.
There were numerous other places where the game length was assumed but all of them were quite easy to change. The thing with a change like this though is that you have to re-test the entire game a couple of times with different settings to make absolutely sure everything works as expected. This is not a simple task and takes a couple of days to do right. We had one other feature on our roadmap which would also require extensive re-testing so in order to be efficient we decided to implement it as well. The feature is to make the game translatable.
Preparing for localization
We knew from the start that we wanted to translate the game into a few languages. Usually, if you know that this is a requirement, you would aim to support localization from the start. Here are some reasons why we didn’t do that:
- We wanted to get the initial English version out as soon as possible.
- We started Game Dev Tycoon during the Developer Preview of Windows 8 where API’s where not necessarily final or stable.
- This was our first HTML/JS experience. We didn’t know HTML/JS very well.
- The suggested route for researching HTML/JS apps in Windows 8 goes against our own preferences (more on that later).
All this meant that researching how to properly do translations would have taken quite a bit of time and we decided to rather incur the technical debt to later implement support for this rather than delaying the initial release date.
The last point about personal preference is probably a bit unusual so let me elaborate. Previously, when I worked on the WPF app NovaMind 5 we investigated how to do translations.The official procedure suggested by Microsoft back then (.NET 3.5) was so incredibly complex that pretty much everyone resorted to different approaches, most of which were based on .resx files from the Winforms era and a bunch of markup extensions.
I have used the .resx variation in the past and I’m not a fan of it for several reasons:
- You cannot see the actual string in code!
I prefer showMsg(“Welcome my dear user!”) over showMsg(MsgStr.WelcomeMsg) any day.This has several reasons. First and most importantly you can see the actual string which means that you are much more likely to see mistakes such as text which is out of context, typos, incorrect amount of {0} parameters, or any other possible issue. In the above example someone on the team might catch that it should be “Welcome, my dear user!”. Anyway, my point is that the more often you see the actual text the more likely you are to catch errors. - Managing .resx files is a pain.
You end up with dead entries which are not used. You end up with multiple .resx files strewn about the place in different assemblies and adding/changing text is a pain. Also, refactoring or moving methods from one assembly to another means you have to migrate the appropriate .resx entries across as well.
Personally I think that .resx only really works for old-school software projects where all required strings are defined in a giant document and where no one ever changes anything around. I certainly think that, for agile projects, this approach is outdated.
Back to Game Dev Tycoon and the situation seemed very similar. The suggested way to do translations is very similar to the old WPF + .resx approach and we were not convinced.
The other downside with the suggested approach is that it won’t work so well cross-platform and we rather not rely on WinJS on such a heavy basis.
Anyway, we implemented our own translation method within a week. The usage is very simple:
“Welcome, my dear user!”.localize()
Simply add .localize() after a string which needs to be translated and the method will return the localized string for you. If you need to specify a comment simply pass it in like so
"Welcome, my dear user!".localize("here you can specify a comment")
.localize() is a method available on all string instances defined via String.prototype.localize.
The method itself figures out which language is currently set and then looks up the translation string based on the key, whereby the key is simply the text and the comment concatenated. This way, you can support the same strings with different comments very easily. In our case we use .json files to store the actual translations so the .localize() method simply looks up if there is a translation for the current language and returns it.
Strings in HTML look a bit differently
<div>ll:{Welcome, my dear user!},lc:{comment}</div>
We don’t use attributes to define the string but simply use special characters to say that this string should be translated. This way, you can use the same techniques for attributes.
<input ... value="ll:{Content}" />
In code we pre-process HTML before we show it with a method which will scan for occurrences of ll:{} and replaced it with the translated content.
Generating translations
To get to the actual set of translations a command line application scans all .js and .html files and builds a up-to-date list of strings and comments. You could then use whatever you want to do the translations. We chose to generate a .po (gettext) file which is widely supported by translation software. Once we get the translations we convert them into .json files which are then used by the application.
Conlusion
The implementation of all of this is trivial and the usage is very similar to what other languages like C(GNU), ObjectiveC, PHP etc. do. The whole .resx management nightmare on Windows seems a bit specific to the platform. With this approach it is also way easier to implement localization for an existing project as you don’t have to go and replace strings with ids all throughout your project.
Anyway, it took us about a week to make the game translatable and Game Dev Tycoon has over 15,000 words of text.
Now that Game Dev Tycoon 1.1.0 s underway we will get the first localizations started and get back to investigating performance issues on ARM.




