Rhythm Quest is a 2-button rhythm platformer game. Jump, attack, and fly to the beat through upbeat songs that are hand-crafted to highlight complex rhythms!
My most recent work on custom levels has been this screen where you can import new custom levels, either ones that you've already downloaded, or by typing/pasting public URLs where they're hosted:
[img]https://rhythmquestgame.com/devlog/62-importer.gif[/img]
Internally this importer needs to handle a bunch of different things -- fetching the level package, making sure that it doesn't already exist in your levels folder, and extracting the appropriate files from the archive. It's all more or less working now, though the "Browse Steam Workshop" button just takes you to an empty page (more on that later)... It currently also supports file drag-and-drop, though only on Windows (sorry, this is a really OS-specific thing and I couldn't find any other implementations!).
[h2]Level End Fastforward[/h2]
I also took some time to implement a small quality of life feature -- being able to fast-forward through the level end sequence:
[img]https://rhythmquestgame.com/devlog/62-fastforward.gif[/img]
This is done by pressing the menu button/key (escape on keyboard, start on gamepad), since you can't bring up the menu during the endgame sequence anyways. For mobile and mouse players, the "pause" icon in the upper-right corner fades into a fastforward button that has the same effect.
I'm just doing this by increasing the global timescale of the game to 3x whenever that button/key is pressed. In an ideal world I would fast-forward only the UI animation here, but that isn't really easy to do with my current implementation, so I think I'll just live with this.
[h2]Object Pooling[/h2]
I don't have any visuals to show for this one, but I also went ahead and did a nice performance optimization for the level editor. If you'll remember, the editor works by regenerating the entire level from scratch every time you make a change. It isn't super slow, but you can definitely notice a hitch every time you make a change when your level gets longer.
A lot of the time here was spent reinstantiating all of the level objects from scratch (including not only the obstacles but also every segment of the ground/terrain, timeline helpers, etc), immediately after the old ones were all destroyed and discarded. This is super wasteful as we could have just reused a lot of the same objects that we already had, as long as we reset their state properly.
To address this I now have an object pooling implementation in place so that instead of destroying the objects, I disable them and mark them as available to be reused. Then when the level is re-generated I can just grab those same instances and reuse them once I reset their state. My implemention isn't super robust, but it also doesn't need to be since level regeneration is essentially the only place in my game when objects need to be destroyed and recreated like this. This took a bunch of (mostly not too difficult) refactoring to get working, but there's a noticable difference in responsiveness now when changing larger levels. Yay!
[h2]Custom Level Publishing[/h2]
Looking forward I'm going to be taking a little bit of time to experiment with solutions for custom level publishing and browsing. I had assumed for a while that I was going to look at integrating with Steam Workshop to give players an easy way to share and browse each others' levels, but I'm now having second thoughts about that since it won't work well across other versions of the game (itch.io, mobile, switch...).
It's probably worth doing some exploration here, so I'm hoping to see if any other solutions may fit my game well. For example, I may be able to integrate with [url=https://mod.io/]mod.io[/url] and use their service as a way to host and manage user levels. I could either use their built-in whitelabel in-game content browser, OR go the extra mile and implement one of my own (say hello to another mountain of UI work, future me...). In theory this could work cross-platform, but I'll have to start by dropping in their integration and seeing how it actually function in practice.