Rumble Crumble Logo

Devlog 3

Rumble Crumble Progression Changes


Progression Problem

This is a big feature that I’ve been putting off for a while now (if you couldn’t tell), and it’s based on feedback we’ve got from playtesters. Basically from the feedback we’ve received, the general consensus is that the game is too short and there’s not enough replayability at this point. We’ve had people say that the amount of content is fine, it’s just that you achieve it too quickly. One person also said that the score you get doesn’t mean anything, and that’s completely true right now, and something that we’d been talking about for a while, but never got round to implementing anything that uses the points yet. They also suggested using the score as a way to do progression, sort of using it like experience points and levelling up - absolutely fantastic idea - and the more we have talked about this the more we love it. (Thanks for that Brad!)

So the idea that we had originally for score was to perhaps make some sort of shop system, where 1000 points equals one “star” or something, and you could use those stars to buy cosmetics or something from a shop somewhere. And this is why we haven’t done anything with the score yet, because that’s sort of late game stuff and we didn’t need to worry about implementing that yet, so we’re just storing the total score in the save file and calling it a day for now. However, we really hadn’t thought about the progression of the game at all, at the moment you just complete each level once which nets you at least one item or powerup unlock as well as unlocking the next “level” (either just the next depth, or the next depth AND the next difficulty @ depth 50m). This system really just means you never have any need to replay any levels, and you also skip each difficulty’s endless depth, until extreme endless - purely because when you beat 250m on any difficulty, the unlock animation for the next depth (endless) plays, but then it takes you to the next difficulty @ 50m and shows the unlock animation for that, so the flow is if you just press space to continue, you will always just skip the current endless mode.

Here’s a video showcasing this behaviour - I am on the finish screen for Medium 250m, when I click continue it removes control to play the “cutscene” of unlocking both Medium Endless, and then the Hard difficulty:

So you can see when I just press spacebar to continue, it will automatically take me to the new difficulty at 50m, essentially skipping Endless mode every time you finish a 250m run.

Powerup Problem

There is another slightly unrelated issue as well, regarding powerups - basically because of the way powerups spawn at the moment, when you have only unlocked one powerup (the first one is always Rhino Rumble), you pretty much get that for the entire next level that you play; until you unlock another powerup. This is because at the moment powerups just spawn every X blocks (+/- some random amount), and it just picks a random powerup from the ones that you have unlocked. This worked during development, but I hadn’t seen the effect that would have at the earlier stages of the game - just another reason why playtesting is so important!

The Solution

So to remedy all these issues, we have decided upon making score be your experience, which unlocks a single new thing at certain “levels”. Here’s a mockup of what that might look like as the finish screen:

FinishScreenExp

This would have the energy bar wave animation, and show the score ticking up until it reaches the top, where an “unlock” animation will appear showing the thing unlocked in all its glory.

Each level would unlock you one of:

  • Item
  • Powerup
  • Enemy
  • Level

Where a “Level” is defined as a depth OR a difficulty (a difficulty unlock is technically just depth 50m at that new difficulty).

This will allow us to fine tune the actual progression of the game more carefully, choosing which things unlock where, for example creating bigger gaps after unlocking endless so you can have more fun utilising the currently unlocked powerups on the current difficulty, whilst working towards the goal of filling up that bar to unlock the next thing.

As for the powerup problem, that is solvable by having powerup spawning work similarly to item spawning, where each powerup would have a base chance of spawning on each row of blocks generated. We will just have to figure out roughly what those rates should be when you’ve got everything unlocked and work backwards from there, so it’s not underwhelming when you don’t have too many unlocked, and not a complete mess when you have everything unlocked. I was also thinking of making it have some sort of min/max amount of powerups that can spawn, perhaps of each type - something like the maximum number of each powerup is just the total powerups you have unlocked - but I haven’t fully fleshed out the idea yet, if you have any suggestions then please feel free to get in touch through X

Implementation

So how have I actually implemented this? The first step was to figure out how to store the information about what was unlocked and when, which at this point was not easy.

So I created a base ScriptableObject class called UnlockableSO which just has:

  • unlockableType - an enum containing the four things listed above
  • scoreUnlockedAt - pretty self explanatory
  • virtual UnlockDataContainer - which is pretty much just a container for things like title, description etc. that each “thing” will override - This is used for when we’re displaying what you’ve unlocked in a popup, and in the gallery screen.

Then I made all the current Item and Powerup ScriptableObjects extend from this and filled in the values, and created other SOs for Levels and Enemies, and made those extend from this as well. This was quite a hassle, only insofar as I had to do the same thing a lot of times. But it was nothing compared to removing the logic I had put in that coupled beating a level with unlocking a thing, turns out sometimes if you have a great idea at the time, it’s sometimes not good if you have to completely rip it out. Good lessons for future games though, and just software development in general - expecting the unexpected can be quite difficult though so I’ll cut myself some slack.

Anyway, that entailed pretty much removing everything to do with unlocking from before, and creating a new system for using the points. I started with the “experience” bar (which I put in quotes because it’s not really experience, it’s score, but I’m going to call it that from now on), and actually created a whole test scene, which just had the bare minimum in it to get the FinishUI working, so I could quickly iterate on how it looked and functioned.

I was already storing all the points, and the diff between “current run’s points” and “total points in save file” so converting that into a workable experience bar went quite smoothly, the fun part was allowing the player to press a button to “skip” any part of that process. So it counts up the total “normal” score you’ve got in the top score counter, then it counts down the “TimeBonus” while simultaneously adding that to the top score counter, then it does the same with the “GemBonus”. On top of that if the experience bar fills up at any point during this, the whole thing should pause and show you the unlocked item with the nice animation as it was before, and then continue on from where it left off, until all the scores are depleted and it shows you your total final score.

Wow, that’s a lot of different things to handle and different areas to allow stopping and skipping at! If I had to use coroutines for this I would have cried I think, allowing different callbacks and handling when one has been prematurely cancelled by the user sounds like a nightmare, luckily this all supports native async/await in C# - so I absolutely used that to my advantage.

Here’s the main snippet of code which handles each of the three score counters:

CountInternal

This handles all three cases; counting up the main scoreCounter and counting down each of the two extraCounters and is called just like this:

CallingCountInternal

You can see in the CountInternal function definition, it’s pretty much working out how much score is needed to either:

  • level up
  • deplete extraScore entirely

Then in the Count() functions of the WinScreenCounters that handles the actual animating up to whatever the next value passed in was, in an awaitable Task. This allows me to define a CancellationTokenSource, and trigger that using a button press, which literally just called skipPointsTask.Start() if it exists and hasn’t been called already. This while loop just sets up that cancellation token and task at the start of each iteration, and sets it to null at the end of each iteration, so anywhere in the middle of that while an await is happening, the player can press the button to skip the score getting counted, no matter the outcome.

Here’s a video of the final product (note that I’ve made the score increase a lot more so I can show you all the features like skipping and the unlock juice that I’ve put in):

There’s obviously more polish we can add for sure, I’m thinking particles while the bar is filling up, a sound when it fills, things like that, but I think as a first draft it’s pretty good!

I didn’t go over absolutely everything covered in this update as it’s already getting pretty long and I wanted to get something out there again, we are hopefully going to start picking up on the socials again, and getting a steam page up soon!

Next Steps

We have also had some ideas about what to do with your score post-unlocking everything the game has to offer, but we are going to keep those under wraps for now. That might come in some sort of update down the line, so keep your eyes peeled and make sure to follow us on our socials!