Dev Blog 27 - Crying In The Club Right Now

Hello to all! BitesDev here. It’s my birthday soon! You had better get me a really nice gift. I’m going to be turning 23! Or 24…? I’m not even that old yet, but I’m already having trouble remembering the exact age. When I’m in my 60s, I’ll probably be going around telling people I’m hundreds of years old.

Speaking of birthdays… did you know that it’s the 5th anniversary of UNDERTALE’s release? Go play it if you haven’t, yet! It’s a huge source of inspiration for me as a writer, as well as my sense of humor. I basically spent the last 12 hours reminiscing over all of my old UNDERTALE fan art with friends… listening to the concert… We all sat in a voice chat and cried for 3 hours straight, essentially.

Indie games are so cool.

Also, unrelated, but I keep accidentally uploading the blog posts with the wrong number and then fixing it immediately, because for some reason, my local folder structure has lost a number somewhere.

oops

What a bother…

Anyways, let’s jump into the update!

The last few weeks have been focused a lot on a combination of UI and overall polish. The refactor entered a playable state about a month and change ago, and we have since been reintroducing a lot of cool aesthetic stuff that had been missing.

We’ve also got a lot of brand new menus! If you have played any of my custom level packs before, you might recognize a few of these level names.

Last, but not least… we’ve been implementing a bunch of new mechanics you might have been anticipating. Here’s demos of switches and wind - though they’re very visually unfinished, they work!

Everything is finally starting to come together… I get emotional sometimes, thinking about it. We’ve come so far! I’m anticipating we’ll have polished up these mechanics by the next dev blog, and you’ll get to see them properly in action. We won’t spoil too much, though… there are a lot of levels we want to keep on the D.L. (which means ‘DOWN LOW’, by the way!!), and a lot of those levels are the ones featuring new mechanics.

Now, what kind of update would it be if we didn’t have a segment from our good friend, CraftedCart? Take it away.


On state machines

State: A necessary part of programming, and while it would be nice to not have deal with managing too much state, it is very prevalent within game development.

So: for starters, let’s give some examples of what we might want to keep track of for each player in Rolled Out!

  • Whether we’re in the first intro cutscene (where the camera spins around the level slowly)
  • Whether we’re in a subsequent intro cutscene (where you’re retrying a stage and the camera spins around much quicker)
  • Whether we’re in-gameplay
  • Whether we’ve gone out-of-bounds
  • Whether we’ve goaled
  • Whether we’ve run out of time
  • How long we’ve been playing the intro/goal/out-of-bounds/etc. cutscene (so we know when to stop the cutscene)
  • How many collectables you’ve picked up
  • How long is left on the timer
  • Etc…

Now, I’m just gonna focus on the first six here (the ones beginning with “Whether”). One thing to note about these six is that one one of those things can be active at any given point in time (so, for example, you could have goaled, or you could be in the first intro cutscene, but you can’t be both at once) - we can create a state machine out of this!

The player state machine

Simply put, a state machine is just a box that has a certain state (eg: PlayerGameplay), and can be transitioned to different states. For a basic state machine, that’s it! If you wanted to, you can do fancier things like doing stuff when transitioning between states, having states store some data for themselves - those might be things we would want to look in to in the future if stuff starts getting out of hand, but for now we just have a super-barebones version where you can just switch state and that’s that.

Perhaps unsurprisingly, a barebones state machine has quite a simple implementation. For starters, we need some place to define all of our states…

1
2
3
4
5
6
7
8
9
10
11
12
13
namespace EPlayerMode
{
enum Type
{
WaitingToStart, //< Waiting for BeginPlay
PlayerGameplay,
PostGoalCrossed,
PostFallout,
PostTimeOver,
SpinInFirst,
SpinInRetry,
};
}

…yup, that’ll do. Then, we just need a variable to store the state, and some way to change the state stored.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ROCORE_API FRollingCtrl : public FBaseCtrl
{
protected:
EPlayerMode::Type Mode = EPlayerMode::WaitingToStart;

// -- snip --

protected:
void SwitchMode(EPlayerMode::Type InMode)
{
Mode = InMode;

// Enable/disable input based on whether we're in PlayerGameplay mode
GetLocalPlayerEventBus().SendImmediate(FLocalPlayerSetInputEnabledEvent(Mode == EPlayerMode::PlayerGameplay));
}
};

That’ll do it!

Now… why have a state machine in the first place? For… a long while in the earlier days of the game, managing state was.. a bit of a mess - mostly involving various boolean variables, or sometimes not bothering at all. For example, the way fallouts and goaling used to work is when you goaled, some code would run, and after a fixed delay some more code would run to restart the stage, or whisk you off to the next level. It was entirely possible for you to goal, and then fallout since… it never occurred to us to check that we had goaled before falling out.

Anyways, my point is having all these mutually exclusive states just in a single variable is way easier to manage over just tacking more and more stuff on over time. When we pass through the goal, we check that the state machine is in the PlayerGameplay state before doing anything. That’s a lot easier to manage over checking stuff like not GoalCrossed and not FallOut and not TimeOver - and if for some reason in the future we want to add another failure state, we don’t need to bother updating the goal checking code to say ... and not YouPickedUpSoManyCoinsThatYouGotTooHeavyAndPunchedAHoleInTheStage.


You’ll be pleased to know that I found the image that I was looking for.

i'm about to start

Thanks for reading, and see you in October.