The Toddler Connection is a detective game for the HTC Vive. It features investigative dialogue, with a unique dialogue mechanic, as well as two distinct perspectives on the world. You take on the role of a toddler in kindergarten who likes playing detective and solving mysteries. Once again playing his game of detective, he searches for the stolen bracelet of his crush, the kindergarten teacher. By talking to the other toddlers in kindergarten, or rather their film-noir alter egos, you find clues and hints about the missing bracelet until you can finally face the thief!
The Toddler Connection is the second Semester Project I did in the Cologne Game Lab, during May to July of 2016. I worked with Felix Schade, Pierre Schlömp and Dominique Bodden on this.
During the entire process of working on The Toddler Connection, we had thought about a lot of different features we could implement. A lot of these came up during Felix and my programming sessions, and we just put them aside as “stretch goals” for the time being, hoping that we might get to them eventually. Sadly, we had enough to do to get the game to a “playable” state, so we had no time to start implementing them.
We wanted to make the kindergarten world a lot more fun, we wanted to implement physics based toys like marble runs, wooden labyrinths, or stuff like the “cheese mouse game”. We also wanted to make the Musician’s piano playable, we thought about making a working pen or chalkboard, and to make a proper cigarette/cigar the detective could smoke.
We also wanted to make it so that the glass bottles and cups in the noir world can break. I noticed that a lot of people tried breaking bottles during playtesting, and I was sad that we couldn’t implement it.
We had thought about a feature where the detective/player had to go to the “Crazy Wall” in his office and combine his clues to get new ones, or to be able to accuse someone. The asset made it into the game, however we couldn’t implement it as a game mechanic.
Initially we also wanted to make the other toddlers in the kindergarten much more active as well, we wanted them to move around and play, instead of standing around idly.
During all of the project phase I met up with Felix Schade at his place to work on programing. It was an absolute necessity, as it was impossible to test anything of our game without the vive, the sole exception of this being the xml.
We met up about twice a week on average, and worked on implementing new features, fixing bugs, all the while making design choices on how things should look or behave. Whenever we had to make an important choice we implemented first, and asked for feedback and opinion of the rest of the group afterwards, which generally worked out well.
I realized that working with two people on the same machine is actually very efficient, as Felix and I could pick up on each other’s mistakes and fill up the gaps that the other had. Especially while working on the Vive, having two people is incredibly helpful, as one person can test whatever changes the other is doing, and because we both know the code, the testing person could notice problems that someone who doesn’t know the code couldn’t.
I did however notice that I find this Co-Programming quite boring after a while of not writing the code (and looking over the shoulder) and I quickly lost motivation while doing that, and got tired much faster. When I eventually told Felix about this he happily offered me to do more of the typing, and from then on I worked a lot more motivated.
While working with Felix I was quite surprised that he enrolled as a Game Designer instead of a Programer. I have to admit that Felix is a lot more experienced with Unity than I am (as he’s been working on his own game for a while now) and he’s good at making things look good, something that I haven’t spent much time on learning yet.
We were VERY ambitious with our dialog system. I absolutely love the idea, I love how much freedom you have, talking about everything to everyone. But we vastly underestimated the amount work involved with it. For the Final version, we have 9 interactable npcs, and 185 possible item combinations, adding up to a staggering 1665 dialog lines, not including detective replies, and a few alternative lines. I don’t know his amount of work hours, but Pierre spent a good part of the last 3 weeks or so working on little else than writing the dialogs, and eventually cutting the voice recordings into lines for the npcs to say.
I had decided to work together with Pierre on the dialogs, check his spelling and grammar, as well as tweak some lines that seemed hard to understand. I had to touch the dialogs anyways, as I was the one to put them into the xml file later, so during this process I worked quite close with Pierre to make sure we have the correct dialogs and items.
Yesterday, after working on the dialogs on and off for the last month, I finally finished writing them into the xml script. It’s still missing a few lines, but they will likely be added later today.
There is not much I can say about the process of copying the dialogs into the xml file, except that I watched 21 hours of a DnD game in the last three days, and that I might need new ‘Ctrl’ ‘C’ and ‘V’ keys.
Looking back, I don’t think I want to make a game with this hugely vast amount of dialog options again any time soon, but I would love to do so one day (hello AI)!
Around the end of the first week of July, I decided to rework the xml I made, because it was quite ugly code. The script that reads the xml file was built in a way that it depended on the xml-file, meaning that if the xml file had some errors in the order of elements, the dialog would not be displayed correctly.
Script-central instead of xml-central code
I took the current xml file and the reader script and started flipping things around. The first thing I did was to make the script read all of the elements of the dialog and save them to local variables, instead of executing the actions as soon as the element is read.
The strings that I get out at the end will be sent to another method, which deals with executing the dialog.
For each of the strings (which are an element in xml) a few things are done. First I check whether there is more than one node of the same name (eg <GainDialogItem />). In the original reader-method I already deal with this by adding a ‘+’ between multiple nodes of the same name, so in this method I can check for that. In most cases there should never be more than one node of the same name, but I like to put some kind of error handling in, to expect human (especially my) errors.
After that I do whatever that element is supposed to do. In the case of GainDialogItem and LoseDialogItem it’s simple, I save the item as “true” or “false” in the PlayerPrefs, therefore saving player progress, and for the GainDialogItem I also display a little icon, so the player knows they gained a new item.
SendAMessage is very straightforward as well. I normally just call the method specified in the innertext, which is something like “OpenMansionDoors” which allows the player to enter the mansion to talk to the boss, as well as saves that information to a PlayerPref, so that progress is saved.
Dealing with what the npc and possibly the detective say, and especially their audio, is a bit more complicated.
Unlike for the other strings I don’t have error handling here if there is more than one element for the npc’s text, I just remind myself not to do that.
The first step is to put the text in the speech bubble for the npc, commented out here, because the script is in a testing project. Then I do a quick check to see if the line has been filled properly, if it is “TEXTLINE” that means the dialog is missing somehow. After that I create strings for the audio files that shall be played, utilizing the naming conventions I decided on. I save the detectiveSays and detectiveAudio strings globally to use them later on.
Now the audio file for the npc shall be played. Most of the text is commented out here again, for reasons explained above.
First I try to Load the npc audio file from the Resources, this is the reason why the naming convention is so important.
Then the playing audio for this npc and the detective is stopped to avoid overlapping. Now I play the audio file for the npc, and -if the detective has a reply for this dialog option- I start a coroutine to let the detective talk, delayed by the length of the npc’s audio. Here I also have a Debug Warning for missing audio files, again, commented out.
After the specified delay, the method “DoDetectiveTalking” is called. This method does pretty much the same as the above, it writes the text on a speech bubble, stops what the detective is currently saying, and then plays the new audio file.
When I realised just how big the xml file would be (it’s >6500 lines, 185 combinations per character), I imagined that traversing through all of it could take up a lot of computing power, and might severely lower the framerate, which I absolutely want to avoid in a VR-game especially! I did a quick stress test, copy+pasting about 3000 options for one character, and -as expected- the game stuttered badly for 2 seconds. Of course we wouldn’t have nearly as many options in the game, but then again, there is a lot of other stuff happening too. I did some optimisation in the code, like stopping the search after the item is found, but It was obvious that that would not do too much.
I thought that I could at least make it better by doing the search parallely to the Update function, instead of attempting to do it all within one frame, so I had to look deeper into Coroutines, which had so far eluded my comprehension.
I tested around a bit and finally learned how exactly they work. I believed that they would actually work in parallel to the Update function, like multithreading (I believe that that is what multithreading does?). However I now understood that they work in the same thread, but a Coroutine can halt its execution through the “yield return null” statement, and let the Update finish, and then continue where it left off in the next Update.
At the end of my testing I decided to “yield return null” after every 50th Item, and that has been working just fine.
Checking for Human Error
As I mentioned before, I like to make my code check itself for errors, and I definitely wanted to check the huge xml file for any missing combinations, so I wrote a script for it. The script should go through all of the characters and all of the possible combinations, DO those combinations, and I’ll be left with a whole bunch of awesome DebugLog!
First I add all npcs and items to two lists, so I can go through those later.
Then I do the combinations. I go through all the single Items, add them to each other single Item -alphabetically sorted- and add them to the final list, if it’s not already in there. I also exclude a few combinations here that are not possible due to the story in the game.
Finally I do all of the combinations for all of the characters.
Note that I wrote this as a Coroutine. Even when returning after every 20 items, the Game has a framerate of around 10 while doing this, and the method takes around a minute to finish checking the xml file.