Published: April 15, 2022
I've been on vacation, but still been squeezing in some extra progress on Rhythm Quest here and there while I can.
First off, I've added yet another playable character! (bringing the total count to 6) This one was a bit easier to do -- it's just the basic single-hit enemy, repurposed as a player character:
Like the normal basic enemies, its spear has a one-pixel bounce (the normal enemies bounce their spears up and down slightly to the beat), but this time it's due to the whole body shifting up and down due to the running movement. This subtle up-and-down movement is pretty universal to most of the other characters as well.
Some of the other characters also have this slight up-and-down bounce during their flying animations, but I tried it out and it felt more awkward here, so I took it out, so the flying animation is really simple here. One interesting thing to note is that the flying and jumping animations are actually offset vertically, so once you get in the air the sprite position actually instantly jumps upwards by a few pixels. This is because the "body" of this character is really short compared to most of the other ones, so it needs a bit of an offset in order to be positioned well relative to the air obstacles.
The attack animation was the main nontrivial part for this character, since it obviously doesn't exist for the normal basic enemies (haha). It's a simple spear thrust, but I'm trying to use a great deal of smearing and some "bounceback" to try and make it feel a little more impactful. There's also some red and yellow "effect lines" near the spear tip that are honestly a little janky, but they're working okay for now to make the motion seem a little bigger (since the spear itself is so thin). I added a "spin" frame after the attack since it looked weird to have the spear snap up to being vertical immediately, I think it works alright.
There's now an in-game settings menu! This is just a single screen worth of options for now, and might expand in the future if there are a lot more game-relevant options, but I thought it would be nice to be able to adjust things like audio latency on the fly without having to exit into the menu. This was relatively simple to put together -- there's no fancy sliding menu transition or anything like that.
Despite not having access to my Nintendo Switch devkit at the moment, I went ahead and put in some experimental new code for handling some stuff on the Switch platform.
The save/load system for Switch needs to be implemented differently than for other platforms because you don't have direct filesystem access -- you need to use specific APIs via the Nintendo Switch Unity plugin. Fortunately some sample code was provided, so I'm basically doing almost exactly the same thing. It's synchronous/blocking right now, and I could potentially improve it by spawning off a separate thread to do the actual saving (same for other platforms), but since Rhythm Quest has very little data to actually write, I'm going to test it first and see whether that's actually needed or not.
I've also put in some new code for Joycon button mappings. Before I was simply piggybacking off of Unity's input system, but that only allows you to handle a single joycon configuration. Now I have code that detects whether you have a single joycon, two joycons, etc. and checks button states differently depending on that, so you can for example press the SL and SR buttons with a horizontal single joycon to jump and attack. This is something that I'm pretty sure will need some additional tweaking, but we'll see how it functions as is first. One thing I'm a little uncertain about is whether I'll allow for controller remapping on the Switch. I already have a screen available for button remapping which is working for desktop builds, but that's a lot more awkward for Switch since the default button mappings are different depending on the joycon style. Also, the default mappings are pretty much that anything on the left joycon is assigned to jump, and anything on the right joycon is assigned to attack, so conveying that UI-wise is a little weird. I'd also have to deal with all of the different controller icon mappings...it just might be more of a headache than it's worth. Maybe as a compromise I'll just have a few different control schemes that you can cycle between, as opposed to a completely-configurable mapping, though honestly I can't imagine why you'd want to have both jump and attack be on the same joycon (if you're playing with two).
There's also a bunch of other small things I worked on that aren't really demoable or worth showing off in detail:
Save operations now happen less frequently, specifically on the settings menus. Previously, changing a game setting value would result in a save immediately being triggered, which wasn't too bad since again, the save files are so small. But it's better practice to just wait until backing out of the menu instead and then write all of the relevant changes. This is especially relevant for console builds (Switch), since there are usually restrictions on how often a game can/should write to the filesystem. This could still be improved a bit -- right now if you change a setting and then revert it, the system still thinks that the state is dirty and does a redundant write -- but I think that's not a big deal.
I don't think I mentioned it before, but there's also an animated loading indicator in the lower-right corner of the screen. This shows while data is saving, or during scene transitions:
Right now this just shows Sayuri, though I could easily change the animation based on the currently-selected character if I wanted to. Unity unfortunately does run a lot of blocking logic on its main thread, so during scene loads this will often freeze up for bits at a time (probably worse when I'm running in the editor on my laptop like in the above gif). This is a pretty common issue with Unity games...in order to make it better I'd have to find ways to split up scene loads, for example loading segments of the level at a time, or spreading out a lot of the initialization logic that runs during the first frame that a level is loaded in, but that isn't on my priority list at the moment.
Respawn/pause timings now correct account for audio latency. This was barely noticable in most cases because audio latency is usually fairly small, but some of the calculations for respawning and rescheduling music weren't correctly factoring in the amount of audio latency. This is a little tricky to think about...if you have 1 second of audio delay, for example, then during a respawn you actually need to start playing the main level music a whole second =before= giving control back to the player, and make sure that the player's positioning lines up accordingly. My mind spins a little bit every time I dig deep into this code, but I've confirmed that it's all working seamlessly now, so hopefully this is the last of it.
I don't have a gamepad with me a the moment, but I've put in some experimental code for triggering controller rumble/vibration when you attack enemies. I have no idea how this will feel or whether it will function on the Switch (could need some platform-specific code instead), but it's there for me to test once I'm back.
To be honest, that was a lot more work than I expected to get done considering that I'm still on vacation (ha ha)...
The main item looming on my to-do list at the moment is setting up the public Steam store page for the game, which is a bit overdue at this point. I'll need to sit down and actually gather enough assets and animated gifs to make it, as well as write out the actual descriptions and everything. I can't imagine that taking any more than a week; it's just something I've been putting off. On the plus side, I already have the gameplay trailer done...
Something else that I'm hoping to do is rebalance the difficulty of some of the existing levels. The levels all play great so far, but right now the difficulty curve is a little steeper than I'd like it to be for the full game, especially given that I'm planning on having 5-6 worlds in total. This is a bit annoying...it can be difficult to adjust the charting of a song after the fact because it's generally so tightly integrated with the music. But there are little things I can get away with here and there, so we'll see how much I can do by examining sections that might be trickier and looking at reducing the amount of syncopation and spacing out note density a little more. Another option I have is to add more levels to each world...making more songs isn't really a problem, but having 5 per world is sort of a satisfying number, so I'm going to try to keep it at that for now. If a song really isn't working out, I can always switch it to being a bonus stage and then write in an easier replacement track.