Skip to main content

Game Jam #2 - Van Helsing (PART 4) - More Functionality

Continuing on from the last log:


The Ghost

The purpose of the ghost is simple: to keep the player moving. Although not refined, there are elevated platforms the player can stand on in the level (i.e. the Statues and Lampposts) and one issue we identified quite early is that the player could take advantage of this to avoid the zombies indefinitely by remaining in one 'safe' spot until they had killed all the zombies. To rectify this, we created the idea of the ghost - an enemy that could reach the player on any y level and could not be killed. They have a delayed start to give the player some time to get moving and then, if not progressing quick enough, the ghost will eventually catch up to the player and nudge them along (or kill them if they fail to do so).

 
 
Creating the delay was simple. On level start, the ghost spawns just off-screen with the variable "begin" set to false. Inside the _process function, everything is behind an if statement waiting for begin to be true before proceeding, meaning that the ghost stands frozen until then. On the first line of executed code, the function "await" is called with the process get_timer(). This sets up a one-time timer that only runs once per application execution and will never run again (even if you run the line again, it will just pass it). In this scenario, I found it more efficient than using a node since a one-time timer is all I needed but for most situations, I do recommend using the proper Timer node in Godot.

 
 
The code for the ghost's x-axis is identical to that of the zombie's but now below it there's some for the y aswell. Since the sprite does not have to change based on the y-axis position like it does for the x, it can be simplified into 1 line of code. Damage and collision for the ghost is handled by the Player so there is no code for it here.

And that's it. That's the ghost. A simple addition, but one I feel is integral to the gameplay.


Importing the Animations



My final task for the game was to take all the (amazing) animations created by our artist and make them work inside the game. Luckily for me, Godot has a very well-built Sprite and animation editor that allowed me to very quickly import and fine-tune the animations. With them all set up, all I had to do was correctly call them inside the appropriate scripts.

That was with one exception: the zombie's death animation. This was a little different since after losing all health, the zombie needed to fall to the ground and remain there forever as a corpse.


By default, animations will play on an infinite loop until a new animation is called for. For most actions like walking this is perfectly fine, but for death, it poses a problem since it's only meant to play once and there is nothing that follows it. With the corpse, there were collision issues that needed to be sorted as without deleting the object, "Zombie_Area2D" still existed for the player to collide with.

The solution I engineered involved the use of a boolean variable called 'dead' which would start false and only be changed once: after the death animation. Upon the zombie losing health, if 'dead' had not already been triggered, the collisions would be disabled using a built-in command, the death animation would play and another one-time timer from the await function would activate. The timer is set to the length of the animation so that it has time to play before moving on, as without it, the next animation would get played instantly and cut off the one before it. Once the timer times out, dead is set to true.

In a different if statement, the value of dead is checked each frame. If false, the rest of the zombie code is executed as normal, but if true, then the animation "corpse" (which consists of only one frame - a zombie corpse) is played infinitely. Once triggered, this is the state the zombie remains in for the rest of the game.

Here's a video of how it looked once implemented:



And with that, my contributions were (almost) complete! At this point, the project was sent off to the other programmer who added all their bits and made it look amazing. And while they succeeded in the end, it is important to note that they reported sizeable challenges in trying to find information online about the particle and lighting systems due to Godot being a less well-known engine with a smaller community and its documentation being less helpful than it perhaps could be (along with their own technical challenges like mine).

After this, my only remaining input on the project was on submission day when I quickly whipped up functioning Menu, Game Over and Victory screens as well as designing the final level due to the absence of our designer.

And with that, I only have the reflection left to record. 
 
See you in the final log,
 
- JDM

Comments

Popular posts from this blog

Global Game Jam 2024 - Stand-Up Simulator

From the 22nd - 26th January I took part in the Global Game Jam 2024 with a team of friends. Our university opted to officially host it at one of their buildings and cancelled regular lessons in order to allow us to focus on it. The theme was announced on the 20th and we all watched it live while in a call together. When it was first revealed as "Make Me Laugh", we were all very stunned. Such an abstract theme makes it hard to think of any possible idea to work with, but when we broke it down (and with some prompting from ChatGPT), we eventually came up with the idea of Stand-Up simulator . The premise was simple: You are a stand-up comedian performing at various clubs, you have to select 16 out of the total 64 available jokes to take in with you and perform (but you will only use max 10). Each club has a different audience with different tastes, so you have to gear your set of jokes to fit that. Each joke you tell will be judged by the audience on a scale of Excellent, Good,...

Game Jam #3 - Feathers & Shadows (PART 3) - Refinement

  Logo by Ephraim Mananga Invincibility Frames  A common design choice in games is to subtly give the player an unfair advantage over the enemy. This is because the goal is for the player to succeed, and it's important that a good balance is struck between the challenge of the encounter and the ability to succeed. One common design feature is invincibility frames, wherein the player enters a temporary state after taking damage where they cannot take any more. This gives the player time to react accordingly without losing lots of health. In many platformers, this is represented by the player model flickering, and so that is the approach that I took. Blueprints of the Invincibility Frames When the player takes damage, a boolean called Invincibility is set to true. This triggers the Invincibility Frames Controller which calls the Invincibility Event and sets a Delay. The boolean timer_control is used to stop the code being rerun as it is directly connected to Event Tick and witho...

Game Jam #3 - Feathers & Shadows (PART 1) - Entering the third dimension

  And we're back! New team, new engine, new problems. Game Jam 3 increased the team size to 6/7 people, made it 4 weeks long instead of 3 and, of course, introduced the new theme: Platformer. This was a lot more generic than the past theme and really opened the door for a lot of different interpretations. My team consisted of the following: - 2 Programmers (Including me) - 2 Artists - 2 Designers Like last time, my group did not get a producer, and unlike last time, this will prove to be a prevalent issue later down the road. But at the start, it was fine. On Van Helsing, we played to our strengths; The artist was best at pixel art, and so that was the style we went for; Me & the other Coder came from pythonic backgrounds, so we chose Godot for its similar language. And while that is the best approach to create a good product, it isn't the best for learning. As such, this time round I wanted to challenge myself & the group, so we ran the logic in reverse. The Artists ha...