With the basic animation done I took my first run at moving the feet to match the slope. It did not go great...
So I scrapped my first go and rewrote it. The basis of the approach was to attach two rays to the bottom of the characters feet that would check how far away from the ground they were and then move down to match it. With this check happening every frame, the foot would naturally adjust to whatever the floor below it was doing.
The first problem to fix was getting the leading foot to move upwards from being initially underground. This involved the ray actually starting just above the characters foot and casting downwards through it, so then it would detect raised platforms. This however lead to a further issue, that with each step the foot would raise, it also raised the height of the detection ray, and if it raised up enough to be close to another shadow or platform, soon the characters leg was higher than their head. So I had to lock the ray to only cast at the height the foot was intended to be on level ground.
The other problem to resolve was when lowing the back foot, I needed to lower it farther than the characters leg was actually able to extend. So I had to ensure that as the trailing foot lowered, the characters entire body would also lower to maintain a feasible leg extension. Which caused a whole host of mathematical issues that needed to be accounted for as the feet are positioned as an offset of the bodies position. Many lines of code later, I had a convincing walk:
At this point I was just over half way through the game jam with less than a week to go. It was time to implement the magic system. I spent an entire weekend coding in all 8 powers, which are a mix of light effecting powers and physics effecting powers.
Once I had their basic effects programed in, I then created a UI system for the player that would allow them to both select a power and cast it on a button press. This turned out to be a little technical, and has a large amount of moving parts to indicate on screen which power is selected and what powers were available to the player. This is probably the part of the game which has the most state data to deal with and the most complex code interactions.
The final part of the spell system was to create pickups that when the player touched them, unlocked each power. This system came together fairly quickly, but did lead to some fairly tightly coupled code between the player, the UI, and the pickups which I'm considering a refactor to at a later point in time, but right now I'm on a schedule.
This area of my development is also where I made a terrible mistake for my games performance. But I will get to that in a moment.
With the deadline approaching I decided I needed to make the place look nice. I spent the last few days of the game jam creating some basic puzzles, and put together a handful of assets to use in my level to get a sense of place and not simply black platforms in the void.
I made some flaming torches to act as my lights, a couple of crates, a house, and some rocks. Which sounds very short as a list of assets, but served remarkably well to create what felt like a small town with a mine shaft.
I added in a couple of floating text boxes to explain the mechanics to the player, and created enough areas to get a short demonstration of the fire ability and the earth ability. As the deadline was approaching I decided to leave the remaining two powers at the end of the level as it stood and created a small area where the player could at least try them out, even without any puzzles to solve.
As a last touch I added in a parallax background effect with some mist and trees. The trees I lifted out of another project I had been messing around with, as they were an existing asset I could reused and included a little swaying and wind animation. I attempted to use the Godot parallax system, but this didn't interact well with the shadow effects. I believe it's to do with how the engine adds together different layers, but I ended up coding my own simple system for moving the tree and mist layers at different speeds as the camera panned past them.
In this last stretch I implemented a quick main menu screen, which was a duplicate of the levels background effect and a couple of buttons to navigate. As well as a pause menu to exit out of the game. I ran into a surprising amount of complications transitioning in and out of the main menu as the gameplay and menu had to hand off responsibility to each other in a controlled way, including a fade effect, not interrupting the music, and not causing the engine to freeze up as new assets loaded.
As I just mentioned music, I also added in a music player. Which I massively over engineered, as I added in the ability for it to switch to any specified track at any moment as the level might call for it. The idea being that if the player entered an area of note, there could be a relevant piece of music that would play for that location. This handling included fading down the current track and then bringing in the new one, then when it was done, returning to what it was playing. I never used it. Otherwise the music player had about 8 tracks it played in a loop. The tracks themselves I picked up license free from Pixabay as well as some of the sound effects I used.
The final task, with less than 24 hours of the jam remaining was to get the game onto a webpage.
This went almost smoothly. The export was no problem at all, and it loaded up right away onto the itch.io page. However when I loaded into the game I was hit with some truly terrible performance issues. The game ran at only about 5 frames per second (fps).
This was a concern as all of the builds on my local machine had been running at a smooth 60fps. It must be something that the web version of Godot was not as capable of handling as well as the build on my machines hardware. After some initial debugging in the built in Godot performance inspector I had nothing. I wasn't running any code that seemed to be slow, or had any notable spikes in performance. My only observation from playing the web build was that the low performance appeared to be worse in some areas of the level than others.
Having dismissed the official debugging tools I went with the tried and true; turn features off one at a time until I get an export with good fps.
The first feature I found made a large impact on performance was the background fog effect. Which I quickly resolved by dropping the amount of fog particles and increasing the opacity, it looked a little less effective, but still held up, and was a good win for performance. Following this I quickly realized that most of my performance drops were when I cast spells and when the flaming torches we on screen. I figured this out as all of those systems, including the fog, were using the Godot GPU particle system.
I made a fairly quick improvement to the torches when I switched them from running the particle simulation on the GPU to the CPU. I suspect somewhere in the depths of the Godot web engine there is a good reason that CPU particles perform better than GPU based ones.
So the final performance issue was all of my spells. I couldn't make the same quick win here that I had with the fog and the torches as the spells particle effects were tied into animations and were controlled via code. Both of these things would care if the type of particle system was changed. But change them I did, slowly and carefully I went back through all of my spells and recreated the effects with CPU particles, while also slightly lowering the particle counts.
This change didn't totally solve the performance issues, but it got the game into a state where it was playable and I submitted my work with about 10 hours to spare.
Go give it a play!
I learnt a massive amount about game development from putting this together. I feel like I got at least a little exposure to every aspect of building a game in this project. I touched on art, sound, programming, gameplay, level design, and UI. I mostly have a list of things not to do next time but I found the whole project very rewarding, and I have a short tech demo of a game to show for it.