Update 35

As said last week, this month we’re trying to focus on resolving smaller tasks that have slowly piled up over time. It might make the next couple devlog entries a bit less interesting, but it’s gotta be done!

Unity 5 was released earlier this week, so we finally switched over! Despite having one of the longest changelogs I’ve ever seen it went super smooth though. There were only a couple minor issues that were easy enough to resolve, and now we’re using Unity 5 :)
We’ll need some time to learn all the new features that are suddenly available to us, but I’m pretty excited about the switch.

If you’ve seen any of the videos of some of our KS-backers playing the game you’ll have noticed that it was impossible to delete certain things after they’ve been placed. The reason was that in Unity 4 we were experiencing some weird performance drops when making objects selectable…before cooking up my own solution for this I wanted to wait for Unity 5 though and see if it’s fixed there - and indeed everything seems to be fine now, so placed objects can finally be deleted. :)

We’ve also added some additional guest animations this week that would have been a pain to implement in Unity 4.

There’s now a search bar in the deco builder window:

Not like there’s so much stuff already that it’s needed, but it’s probably best to have this early.
The preview icons fill the available space a bit better now, while still giving a rough idea of scale (at least when you compare similar items to each other, like the logs or trees).

A part of the finance overview from Update 32 is now also available as a tooltip when hovering over the overall money in the main menu (and it got an additional “Overall” bar).

It shows the current and previous months, so you can get a quick overview without having to open and close a window.

Added a height number when building raised stuff:

It currently shows height above ground level, we should change that to height above sea level instead.

I finally decided to give coaster tunnels a try today. Having to implement this was one of the things that’s been giving me headaches for months, but after having a reasonable idea of how to approach this it turned out to be really easy.

The white box look is obviously placeholder and will be replaced with proper art. Garret visited GDC this week though, so all you get for now is my programmer art :)

Update 34

We quickly noticed a problem with the text boxes from last weeks activity logs - if you had many activities close to each other or zoomed out the boxes would overlap and you couldn’t read a thing.

It seemed like a good idea to automatically space them out to not overlap anymore, so we tried a couple different methods…

…but that didn’t work too well. And even if it did work all these boxes would really block your view.

In the end, the most simple solution worked the best:

As you already know from the previous blog entry and can see here we also worked on adding support for different languages :)
It’s pointless to actually translate things before they’re final but since there’s slowly more text being added to the game it’s good to have this working. One thing less to worry about!

People received names this week. We’re using these lists from the US Census Bureau that handily also contain data on how frequently the names occur. We’re using the peoples names as seed for their appearance and characteristics, so “John Smith” will always look and behave the same. It’s a bit silly and probably nothing you’d have noticed, but I like the thought that these people are all unique somehow.
Speaking of translations, I guess we’ll have to get similar lists for other countries.

You guys convinced Garret to create a raptor entertainer during this weeks stream, and here he is:

He still needs animations before we can properly implement him though.

Here’s a timelapse of the livestream:

March

Unity 5 is getting close to release. It’s a huge update of the engine we’re using with many exciting new features that should make some things easier for us. We planned to switch to Unity 5 from the very beginning and think the time has finally come to do so next month! It should go fairly smooth but we’ll see.

February was tech-focused, so for March we mostly want to take care of polishing and tying up some loose ends.

Translation support for Unity games

Unity provides you with pretty much all the tools you need for creating a game - one really handy feature that’s unfortunately missing though is inbuilt support for doing translations into different languages.

It wasn’t too complicated to add it myself in the end, but since I couldn’t really find any articles on this topic I thought I’d do a quick write-up of how I approached it in case it’s helpful for anyone :)

So, if you want to add translation support to software you’ll have to ask yourself how and where to store your text strings and their translated versions, and how to load them into your program. Especially the latter was really important to me - I wanted to have a system that requires minimal code changes and requires the least possible amount of work when adding new strings that needed translations. What I especially did not want was having to manually manage a database of all strings in the game with cryptic lookup keys like ID_UI_INGAME_WINDOW_BUILDER_TITLE that some systems use, because having to maintain that database would be super uncomfortable and I’d rather not have to copy these keys around where after a couple weeks I don’t know anymore which text exactly is hidden away behind them.

When researching translation systems you’ll quickly stumble over gettext (up-to-date Windows binaries can be found here), and it satisfies the above requirements perfectly.
All you have to do is to wrap your strings that need translations into some function, so for example this:

string text = “Some text”;

becomes this:

string text = I18N.GetString(“Some text”);

You then run a command line program named “xgettext” that’s part of the gettext system over your source code. It looks for appearances of your wrapper function and extracts their string argument into a separate .pot file. Using “msginit” you then take this .pot file and create individual .po files for each language you want to support. The .po is a simple plaintext file containing both your original language strings and their translations.
Here’s an example of what that .po file looks like for German:

msgid “Some text”
    msgstr “Irgendein Text”

    msgid “Another text”
    msgstr “Ein weiterer Text”

You then parse this file and put its contents into a hashtable, using the msgid as key and msgstr as value. Your GetString()-wrapper method from the example above then simply looks up and returns the proper value for the given key (or returns the key if it doesn’t exist in your hashtable, so if some strings are missing translations they’ll at least show up in English).
There’s a source code example of how to do this here that should get you started.

If you’re like me and try to use the Windows command line only if you really have to you can make it even less of a hassle by creating a couple of batch files that do all the gettext stuff and a Unity editor window that executes them. Now you can run the string extraction with a single click, without having to leave the Unity Editor!

Another benefit of using gettext is that it’s a pretty common translation system, so there are plenty of tools already supporting it. For example there’s poEdit, a GUI application for editing the .po files, which makes life easier for your translators. And if you’re neither into command line programs nor Unity editor scripting it can do the string extraction for you as well.

Making it work with Unitys UI system

So, this system is great for text that you’re directly assigning from within your code - but what about the Text components in Unitys UI system? I certainly didn’t want to set all the Text component values from code and wanted to keep being able to visually create my UI in the Unity editor.

My solution is to do the same thing as for the code basically. I “tag” the Text components that need to be translated by adding another special component to them (named “I18NText”):

It does nothing more than take the text value and run it through the wrapper function:

public class I18NText : MonoBehaviour {
     void Awake() {
        Text textComponent = GetComponent<Text>();
        textComponent.text = I18N.GetString(textComponent.text);
    }
}

Of course it’s also necessary to extract these UI strings into the .po file, so I wrote a small editor script that loops over my UI assets, gets their Text components and writes them into a .pot. The .pot containing the UI strings and the .pot containing the source strings are merged into a single .pot (using the gettext tool “msgcat”), that is then used for creating the .po files.

Phew! :)
This sounds like a lot, but once it’s set up all you really have to do in the end is to wrap your strings into the lookup function, mark your UI texts with the additional component, and run the gettext tools before doing a build. That’s it!

Update 33

Art Stream

There’s another art stream coming up! Watch Garret create some Parkitect art on his Twitch channel, on Wednesday from 1pm to 3pm PST.

Devlog

We added guest activity logging! We’re recording certain events for individual guests and there’s a neat visualization overlay showing you what they’ve been up to:

I was a bit concerned what this would do to the filesize of our savegames, but judging by some quick tests it should be alright. And if not there are ways to optimize it.

Guests became cleverer this week - they learned how to properly use transport rides when heading for a specific location. So if for example they want to leave the park they might either walk or hop onto a transport ride and drop off at a suitable station.

Here’s a couple of debug screenshots, with the yellow line showing which path the guests would take if they wanted to get from one endpoint of the line to the other (for rides it marks where they enter the queue and the station they leave at):

It takes into account the average waiting time at a station, the time the ride needs from the entrance to the exit station (on average) and the ride entrance fee in relation to the travelled distance. The weighting of these factors is something we’ll have to balance.