Important notes about the visual showcases:
Left gif: I connected the players death event to the game going into game over state and the ui showing.
Top right gif: I made the gun that is shooting and the bullets (not the meshes)
Bottom right gif: I made the damage overlay material and made it show and implemented the particle effects that play when you get hit.
This section goes more in depth about how everything works, reasons why we did what we did and also shows some code.
I worked on the statemachine which handled the flow of the game. It was one of the first things I did in the project and something I kept spending a lot of time on polishing. I also asked for help during mentoring session for improving it. The states themselves had simple tasks, such as pausing / resuming the timer in our game.
When setting a new state, we first check that it's a valid state. If it is, we clean up the current state. Then, the new state gets created and runs the EnterState() function. We then notify all listerners what state the statemachine has switched to.
If we don't have a current state running (when setting the initial state) we don't do anything. If we do have a state running, we call the ExitState() function to prepare the game for switching state. We then clear our reference to the state.
The blood gun was originally meant to be a weapon that the player could use where the longer you hold the shoot button, the more bullets would shoot at once. Each bullet shot would remove a certain amount of health from the player. However, this idea was eventually scrapped and the blood gun had its values tweaked to only fire a single bullet and was instead just used by some enemies in the game. In this section I will show you what the gun was originally meant to be since this was still the final code being used, although with tweaked variables.
Functions for registering when the shoot input was pressed and then one for when it was released. Originally I used a timer to get the duration of the shoot input but in a mentoring session I got introduced to the GetTimeSeconds() function. What happens now is when you press the shoot input we save the time since the current world started and compare that to the time when the input is released. That way we don't have to waste performance on a timer since we can just use a really cheap calculation instead.
When a shot is triggered, I start a timer for the length of the shot cooldown duration, and after that has passed the FireBullet function gets called. This was my first time using unreals timer handles and experimenting with how they work.
When a shot is triggered, I start a timer for the length of the shot cooldown duration, and after that has passed the FireBullet function gets called.
This function was used to calculate how many bullets should be shot. It took the duration the shoot input was held and then compared it to the duration we wanted to give max bullets. From that it got a percentage value which was multiplied together with the max amount of bullets the gun could shoot. The resulting value after rounding to integer was how many bullets should be shot.
Here I was also introduced to the check (in this case: checkf). Since it's very important to not have divisions by zero, I used the checkf here to stop the program from continuing if it would just result in an error or some issues anyways.
When the game starts, we get a reference to the player stats component on the player and then bind the death event after a little delay. This delay was to avoid some issues with the order of execution. It isn't a clean solution but it was easy to use and it worked since you could never die before the event is bound either way.
Bind the OnDeath event that gets called from the player stats once the players health reaches 0 to our OnDeath function.
When the player dies, switch the games state to GameOver and remove ourselves from the OnDeath event in PlayerStats.
In this section I'm only gonna show the blueprint implementation since the C++ side didn't include much. On the C++ side I simply made a blueprintable actor object with 2 variables and 1 function. The variables were LevelName for the name of the level the trigger was gonna load and FadeDuration for how long the screen fade effect should take before the level is loaded. As for the function, LoadNextLevel(), it was simply calling UGameplayStatics::OpenLevel().
Upon colliding with the collision box of the level loading trigger object, I spawned a widget with a black image on it. The image started out being invisible and then became more opaque until the amount of time specified by the FadeDuration variable had passed.
I add the delta time from the tick event onto a TotalSeconds variable which is used to track the progress of the image being shown. Once it has reached 100% visibility, the next level loads. Everything didn't fit into 1 image since I wanted readability, but what happens to the right of the level loading node is just variables being set to 0.
This function is what gets called when the player is attacked. We give the player an overlay material to make the player flash red when taking damage and set a timer to remove the overlay material from the player. After that, we determine what effect should be played based on what type of damage we took. They all call the same function but use different parameters to create different effects for each attack type.
We spawn an object with the niagara system on it and call an activate effect function which determines what parts of the effect should be played. Then we set a timer for when the niagara systems should be destroyed.
The function for activating the effects. Based on the parameters it can activate either one of the effects or both.
The function for resetting the effects, if it can even get that title. The only reason this was made into it's own function in this case was because it needed to happen after a delay. This removed the overlay material from the player.
For this project I want to mention something outside of the project as the most important thing I learnt, even though I learnt a lot during the project too. During this game project I was quite stressed due to things going on outside of school, and it definitely impacted my efficiency when working and mainly my motivation. I felt really awful not performing according to my standards and every meeting I felt like I was letting down the team. My point is, it is genuinely super important to take care of yourself. If you're stressed, communicate that with the team, get a mutual understanding of why or at least what can be done to work around that. I understand that now, and in the future I am confident I'll be able to communicate clearly when I need to change my workflow, or anything in general.
Gameplay, Quality Assurance, Sound
Product Owner, Gameplay & Level Design
Narrative & Level Design
Gameplay, User Interface
Game Programmer
Game Programmer
Game Programmer
Game Programmer
Game Producer
Project & Web Manager
Release Manager
Prop Artist
Character Artist
Artistic Director, Environment Artist
Animation
VFX Artist