Jackalope - Dev Log 02

Topics: Match Statements (Godot), Custom Resources (Godot)


Last I left off, this was where I stood on my initial “prototype essentials” checklist:

  • A level that has a floor, walls, and a start point and end point DONE!

  • A character the player can control DONE!

  • Obstacles within the level that harm the player DONE!

  • Health tracked in UI

  • Game over when health is depleted

  • A second level the player is taken to (when they reach the end point in the first level)

So I diligently continued working my way through the checklist, and each of the remaining items ended up being relatively straightforward.

For tracking the player’s health, I gave the player a health value and created a function that will deduct health whenever the player overlaps with any enemies (currently just bats), then used a match statement (GDScript’s version of a switch statement) to select either a “full” or “empty” heart texture to be used in various slots in the HUD based on the value of the player’s health variable.

A match statement determines which texture is shown (“empty” or “full”) based on the health value.

With the health system in place, it wasn’t much more effort to check the next item off the list as well (the “game over” state). All that was needed was to create a “game over” screen and set it to be visible when the player’s health dropped to 0. And, just for good measure, I added a couple buttons that let you quickly restart or quit.

It’s all good - there’s unlimited continues.

Now, I know I mentioned before that I wasn’t going to be focusing on polishing any art assets and, by and large, I’m still holding myself to that. I did, however, indulge myself with a bit of a side-jaunt to polish a couple elements, though in my defense they were to help facilitate feedback to the player and to make it easier for me to test. The first of these minor embellishments was to add a knockback animation to the player when you get hit.

Careful - they bite!

This not only helped make it explicitly clear that you were just damaged, but also served the gameplay function of making the player stop and rethink their actions.

The other small thing I added was a simple walk cycle (which really just involved rotating the player back and forth a bit to give the impression his legs are moving).

Even a simple, blocked-in walk animation helps in determining if the walk speed feels good or not.

The walk cycle didn’t necessarily serve any gameplay function, but it did make it easier for me to determine if the walk speed felt good or not. The sliding, tweened motion I had in there before was functional, but the speed felt off and it was hard to put my finger on exactly how it needed to change because, let’s face it, I’m simply not used to looking at people moving like that. Adding in a walk animation really helped a lot, but I still took the approach of adding in just the bare essentials of the motions necessary to communicate a “walk.” I can go back and polish it up later (at which point I’ll actually need to learn how to animate better, but that’s a whole other adventure).

So we now had the health tracked and a “game over” state when the player’s health drops to 0. Last (on the checklist, that is) was to allow for the loading of more rooms. I created another room - empty for now, since I just needed it to test functionality - then created a new “finish” tile and added it to the MeshLibrary. Finally, I hooked it up so when the player steps on the finish tile it loads the second room. Pretty simple!

Okay, okay… so the fade out/in transition wasn’t completely necessary. Shhh. Don’t tell.

And with that, the checklist was complete!

  • A level that has a floor, walls, and a start point and end point DONE!

  • A character the player can control DONE!

  • Obstacles within the level that harm the player DONE!

  • Health tracked in UI DONE!

  • Game over when health is depleted DONE!

  • A second level the player is taken to (when they reach the end point in the first level) DONE!

And although it wasn’t incredibly thrilling at the moment, I did have something that was at least playable. But, as I expected, things got slightly more complicated as I started thinking about what I should work on next. Even with this bare-bones, barely functional suggestion of a game, I had already identified some new needs that had to be addressed if I was to make any more progress on the project.

For starters, since loading into a new scene meant loading new instances of all the nodes (the player, UI, camera, etc.), the player’s health was reset each time a new scene loaded; you might have been hit a couple times in the first room, but when you transitioned to the second room you were now at full health. I needed a way to preserve the current state of things and carry that data forward through the rest of the game.

Looking a bit further down the road, another thing I knew I wanted was to have the game’s level progression be somewhat procedurally generated, which is something the current setup does not allow for. Instead of having the first room load to the second room, the second load to the third, and so on, I wanted each room transition to take you to a new room that was randomly selected from a curated list. This way, each time you played the game, your playthrough could be slightly different which would keep the player on their toes and generally make things more interesting. I needed a way to do this random room sequencing.

And finally, as more and more elements were added to the game, the connections between nodes became increasingly complicated. The lists of @onready and @export variables for each component were growing and it wouldn’t be long before they got completely out of control. This was less of a player-facing problem and more just something that affected me - the person working on the game - but it was still incredibly important. I needed a way to keep the communication between nodes organized so that working on the game didn’t drive me crazy.

I decided this last issue was the most important - the longer I waited, the more difficult it would be to fix - so I put the other things aside for now and decided to address that first. Since I’m new to programming I’ve never had to create the architecture for a game before, but I had a theory for how I should go about fixing it. Instead of having each node talk to each other node, what if I had nodes communicate their statuses up to some sort of “game state manager,” which would assess the information and then give orders down to any nodes that needed that information? Thus, Mr. Manager was born.

Now that I have my coffee, I’m ready to facilitate communication between nodes.

Mr. Manager is a node that contains references to all the other nodes in the scene that need to communicate with each other.

It also contains functions for processing information it receives from nodes, and can then send the results of those processes to any other nodes that might need that information. So, for example, if the player runs into an enemy, the player node reports this up to Mr. Manager and tells it to use it’s “take_damage()” function. This function (which exists in Mr. Manager) calculates the damage, deducts that damage from the player’s health (also kept in Mr. Manager), and then passes that information down to the health HUD to update it’s visuals accordingly.

Nodes report their statuses up to Mr. Manager, which makes changes to any game data affected by that status. The new values are then reported down to any nodes that need to know about these updates.

Another advantage of using Mr. Manager was that it gave me a place to set up and store the data I would need to carry over to new scenes. To do the actual saving and loading of data, though, I needed to learn about something new (to me) in Godot: Custom Resources.

I created a resource that held all the data I needed to save and carry over into a new scene (currently this is just the health and a list of valid room choices, but will probably include other things in the future as the game gets more complex). Whenever the player reaches the end of a room, Mr. Managers saves the current state of that data in the resource file, and when the next scene is loaded all the game data is populated by whatever values were saved in the resource file so it matches where the player left off in the previous room. At the same time, Mr. Manager selects a random room from a list of valid room options, removes that room from the list (so that you don’t get any duplicate rooms), and then the updated valid room list is saved to the resource file as well.

Problems solved! All thanks to Mr. Manager.

Addressing those extra issues definitely puts me in a better place to start experimenting more with the design of the game. As I mentioned before, at the moment the game just involves walking from one end of a hall to the other while avoiding some bats. The next thing I need to focus on is trying to add more interesting things for the player to do - avoiding more enemies and obstacles, adding more objectives, and providing scenarios that prompt the player to make interesting choices.

More on that soon!

Previous
Previous

Gaining Perspective

Next
Next

Jackalope - Dev Log 01