Devlog 43 - Unpause Animation, Bugfixes

Published: September 9, 2022

I debated taking the time to write another devlog since I don't have that much to report on, besides the fact that I'm spending a bunch of time on packing and moving and such. I worry about making devlogs that read more like blogs than anything else, but I =did= manage to squeeze in some Rhythm Quest work here and there, so let's cover it quickly. I get antsy when I go for too long without returning to one of my regular activities, and devlogs are a part of that, I think.

Unpause Countdown

There was a pretty sensible feature request for some sort of countdown when unpausing the game. Normally this would be pretty easy, just add a 3-second delay and some text that counts down "3, 2, 1" along with some tick/beep sounds before unpausing the game.

But this is Rhythm Quest we're talking about; the pause music maintains the same beat as the original song, and unpausing also relies on that sync, and waits until the pause music loop gets to an appropriate beat in order to drop you in seamlessly back into the level track, regardless of when you paused (on a beat, off of a beat, etc.). Because of this, the duration of the unpause delay varies depending on when you pause and unpause, which is probably part of the reason that a countdown timer is desirable.

The ideal solution here would probably be to count you in based on the beat of the song, making sure that unpausing takes at least 3 or 4 beats. That...seemed like a lot of work to me, so I was a little more lazy...I kept the already-existing unpause timing logic and simply added a bar that fills up:

Not the prettiest, but it'll do, at least for the time being, though I admit that in the process of writing this devlog I'm wondering if the countdown really would be that much more effort...maybe something to look at later. Anyways, the screen tint also fades away as you jump back into the action, which I think works nicely.

Input Buffering

I probably haven't mentioned this before, but you can actually buffer input during a respawn. If you press jump or attack before you're technically actionable, the Player class will store a flag and process the input on the first possible frame after respawning instead. With the added unpause progress bar, it seemed like it made sense to add that functionality to unpausing too. That wasn't too difficult, just added a different (new) condition to the input buffering logic.

One thing I did realize was that I also needed to add input buffering for jump releases for unpausing. Previously I didn't need to worry about this because, well, there's no reason to buffer a jump release after a respawn, as you'd never be on a flight path. With unpausing you =can= be in the middle of a flight path, however, so I went ahead and implemented that.

This is deceptively a little more complicated than the other cases because the jump release behavior depends on whether the jump input is held down or not. Because of how the input system works, I don't actually detect jump releases based on polling the input state every frame; instead I receive discrete key release events. This means that if you released the jump button while you were in the pause menu, I would ignore that event entirely and you'd still be flying after you unpause. That's fixed now -- I toggle a flag for jump release buffering each time you press or unpress jump during pause/unpause so I can just "do the right thing" when you regain control.

Of course, if you're using different jump inputs across multiple devices (keyboard / mouse / gamepad) that's another story, but at that point there's really only so much I can do to help you, haha...

Bugfixing

I've also had some time to look at a few bugs, such as a lot of interaction sounds being completely missing if you disable sfx prescheduling (which I don't recommend, and therefore don't often test).

One of these was a report that the screenreader text to speech (TTS) functionality was broken on Mac/OSX, which I confirmed. This was odd to me since the UAP accessibility plugin that I'm using claims to support Mac just fine. It was also working just fine in the Unity editor on Mac, so clearly it was implemented fine...something was just going wrong somewhere along the way.

Digging into it a little more, I encountered this cryptic error:

[Accessibility] TTS Error: System.ComponentModel.Win32Exception (0x80004005): ApplicationName='say', CommandLine='-r 175 "Start Game"', CurrentDirectory='', Native error= Success
  at System.Diagnostics.Process.StartWithCreateProcess (System.Diagnostics.ProcessStartInfo startInfo)

Not super promising since the error code 0x80004005 didn't seem to be specific and the "Native error= Success" field wasn't particularly elucidating. Looking at this (and the UAP plugin code), it looks like UAP is trying to use the built-in "say" command to invoke the Mac VoiceOver functionality:

m_VoiceProcess = new System.Diagnostics.Process();
m_VoiceProcess.StartInfo.FileName = "say";
m_VoiceProcess.StartInfo.Arguments = parameters;
m_VoiceProcess.StartInfo.CreateNoWindow = true;
m_VoiceProcess.StartInfo.RedirectStandardOutput = true;
m_VoiceProcess.StartInfo.RedirectStandardError = true;
m_VoiceProcess.StartInfo.UseShellExecute = false;
m_VoiceProcess.StartInfo.StandardOutputEncoding = System.Text.Encoding.UTF8;

System.Threading.Thread worker = new System.Threading.Thread(() => WaitForVoiceToFinish(m_VoiceProcess)) { Name = "UAP_TTS_Proc" };
worker.Start();

I figured this was some sort of permissions/sandboxing issue where the app was failing to spawn an arbitrary process due to OS restrictions or something like that, but this actually worked fine on a blank project, so I had to keep digging to see what the difference was (and hope it wasn't something too hairy).

Well, the good news is that it was just a single setting in the project settings file. The less good news is that the setting was the scripting framework being used. Turns out that System.Diagnostics.Process (and spawning a new process in general) is unimplemented in Unity's IL2CPP scripting backend and fails silently, whereas this works fine under Mono.

This is annoying. In a vacuum I'd rather use the IL2CPP backend -- it uses ahead-of-time compilation and is generally more performant, whereas Mono uses just-in-time compilation, which can lead to some niche issues (serialization/deserialization issues due to how reflection is handled). But poking around didn't seem to reveal any promising solutions for other TTS implementations on Mac that I could plug in here. I decided to switch the scripting backend to Mono to get the TTS support working and hope that everything else didn't suffer too much (so far it's looking okay...). The silver lining here is that Mono builds tend to be smaller on disk, so at least we get that.

Still hanging in there

Yeah, this update probably wasn't super interesting, and I haven't had much in terms of Rhythm Quest content lately at all. This is probably going to be the case for the next few weeks as I finish my move, so you'll just have to sit tight for now (sorry!). I have been seeing the demo pop up as something that people have been playing on twitch sometimes, so shoutout to anyone who's streamed some footage of it! I do have a new idea for another character to add in, so maybe I'll end up working on that next!

<< Back: Devlog 42 - Demo Release