After the rush to get the first playtest demo out, I spent the first two weeks of February writing some overdue posts on what got me into keyboards, what was interesting about touch typing and how getting away from the keyboard and writing by hand could help move the side projects along:

I also wrote about my experience learning Godot that some people found helpful.

Meanwhile I had a shortlist of features I wanted to implement for the next playtest demo. The chief of them was getting the full text of “Dracula” into the game, including all the formatting and text replacements so that it could be typed on a regular keyboard.

I used HTML as the source format and wrote a converter in Go that produced a file in the Godot textual resource format, TRES (largely equivalent to the scene format TSCN). The converter could apply styles defined in HTML and lay out text to the given width, while producing replacements at the same time.

All was well except it didn’t work. The problem, as far as I could tell, was that though Godot used random resource IDs, they were still cached in what’s called a “engine resource cache” and if the engine encountered a new resource file, it could not be loaded. I got several different errors and tried reading the source for the loader but checked myself and stepped back to look at the problem in another light.

My ultimate goal was to get data into the game, not to produce a resource file that the engine could load directly. I found in the Godot documentation that you could run a standalone script from the terminal and decided to add an intermediate step. Since Godot has native JSON support and produces dictionaries after parsing, I quickly rewrote my converter to make a JSON file instead of the TRES one and wrote a small script in GDScript that loaded JSON and created a new TRES file in-engine. The benefit of the Godot script was that it existed within the project and could see my custom classes that were used to make the resource file—something that I had trouble with making a TRES file directly.

After spending three days debugging my TRES output, I spent half an evening on this new approach and voilà:

HTML source, JSON and TRES

It was a great feeling to see the full text of the book ingame, including margins, justified text, font styles and everything else that I picked up from the HTML source. It also worked right away because the resource format was exactly the same that I used for my small piece of text laid out manually.


One change I made almost straightaway was to replace single dashes in the source text with double dashes. To make the layout easier, I used a monospace font and the dashes were also exactly one character wide which didn’t look good to me. Because I could do arbitrary replacements, instead of typing two dashes “——” you have to type one hyphen “-”. In a monospace font, two dashes look just like one dash is supposed to look and there is no gap between them.

Double dash

I also added a help page as an alternative to just typing screen after screen of text that acted like an interactive tutorial. It showed different text styles and what they meant. (Yes, I know it’s not very accessible, but you’ll be able to pick a color theme that you like.)

Help page

You can see how the interactive tutorial works in the video below. Right after the March playtest demo, I also added “streamer mode” because Typingvania was now good enough for me to practice! It didn’t yet have any statistics besides the recent typing speed (words per minute), but it was already much less boring than typing random words. In streamer mode, the text is limited to just four lines so that I can overlay a video with myself typing, and in the top right corner there is a short log of the actual text typed so that the viewers can see what exactly you’ve mistyped.

The next thing I’m going to work on is statistics, so that the game can learn your skill and give you first words and sentences that match your skill (what letters you know and which you don’t, based on frequency). I’ll see you in the April devlog to show how it works!