This past week has been a blast! I came this close (pinches fingers) to making my goal of implementing all the core features within a week. I got a lot of them done, and some still have work to be done. Overall, I would say it was a success. This past week was a week of learning and I got further in my development than before.
The Plan
Starting the week off, I made the following Gantt Chart to plan out the tasks I’ll work on.
There are two weeks of work shown here. The first week I’m going to focus on the Core Features of the game. These are the things I identified as being foundational to the game. The following week is all the non-core features that flesh the game out and will provide a solid base to begin the more creative aspects of game development.
So, for this week I needed to get the following Done:
- Player Movement
- Characters can take damage
- Bullets that can spawn in 3D
- Characters can Shoot
- Characters can Heal
- Player can start a New Game, Save the Game, and Load a previous Game
Long story short, I ended up not following this plan to the letter. Turns out some of these features were far more complex, and others far simpler.
What I Did
What I actually got done looks more like the following:
- Characters can spawn bullets
- Characters can take damage
- A Simple HUD UI
- Main Menu and Pause Menu
Starting from the top, spawning the bullets was relatively easy. All I had to do was write a function that spawns them at a desired location around the character. This function can then be tied to an input action. Boom, now the player can shoot bullets.
What was not as easy to figure out was how to register collisions between a character and a bullet. The problem stems from the way I want to implement the bullets using the Niagara System. You see, Niagara Particles do not interact with other objects in Unreal the same way an Actor or Pawn (The object type that the character is) might. The Niagara Particles do not automatically transmit their data when they collide. This can be good, because if you want to have sparks spewing from something and bouncing across the ground, you do not want all thousand of them to be transmitting all their data on every bounce. You will lose performance this way.
Instead, I have to tell the Niagara Particles when and how to transmit their data on a collision. The way I ultimately implemented this was to have the Niagara Particles use a Callback Parameter. This parameter acts as a bridge between the particle and a blueprint that I tie it into. Obviously I now need a blueprint for this, so I made one to act as the base for spawning the Niagara System Bullets.
The way the collisions now work is that a Bullet Base Class handles the damage dealing while the Niagara Particle Bullet just handles registering collisions. When the Bullet registers a collision, it then sends this data to the BulletBase Class. The BulletBase Class then creates a sphere trace at the collision’s location, checking for anything of type Pawn. If one exists and it has a health component, the character’s TakeDamage function is called. At the end the particle is then killed.
Now that is done, I get to work on making a basic HUD. For now, all I want to display is the health of the Player Character. To setup the health bar, I grab the current health and max health from the player, divide the two to get a percent, and bind this to the health bar. The HUD is then added to the viewport via the Player Controller.
Now I get to work on making a Main Menu and a Pause Menu. The Main Menu has buttons to start a New Game, Load Game, Options, and to Quit. The Pause Menu mirrors this closely, instead having a Resume, Load Game, Options, and Quit to Main Menu buttons.
To make it all work, The Main Menu has its own level and gamemode, separate from the one that gameplay occurs on. This allows me to change the player controller, the HUD, and the player’s pawn. Currently, when the Player goes to start a new game, it loads in the Default Third Person Level I’ve been working in and sets the Player Character. The Player can also try to load a game, but I have yet to get this working. The Options Menu Page does not work yet either. Easiest of them all to get working was the Quit Button. When clicked, the Game closes.
The Pause Menu has the same Load Game and Options Buttons. It also has a Resume Button, a Save Game Button, and a Quit to Menu Button. The Resume Button and Save Game Button I got working quickly. However, the Load Game Button also seems to have issues, just like the Load Game Button on the Main Menu. The Options button also does not have functionality yet. The Quit To Menu button takes the player back to the Main Menu.
And that is what I got done in a week! It was a learning experience too.
What Have I Learned and How I Plan to Iterate
First, I need to rebuild the menus to use a Base Menu Class. While the Main Menu and Pause Menu each have unique functionality, they also share common functionality, such as the Options Button and Load Game Button. It will be far easier to build these from a common class that they inherit from, increasing reusability.
I am also thinking of moving where the Save Game and Load Game functions are done. Currently they both occur within the menus. While this worked to get me started, it’s not very reusable. Instead, it would probably be better to have these placed in the Player Controller. This will make it easier to then implement Auto Saves in the future.
More on the planning side, I learned that what took me the longest wasn’t what I expected it to be. It took me far longer to figure out the bullet collisions than I originally thought. On the other hand, I planned for far longer than the menus took. The learning point here is that features and designs that are common to most or all games will be far easier to start than features and designs that I invent. Figuring out how to get started on the Save Game functionality was easy because Unreal already had the functionality built in. Meanwhile with the Bullet Collisions, Unreal had all the pieces for me to work with, but it took longer to figure out the right way to put them together.
Moving forward, I’ll adjust how I anticipate the time for given tasks based on how common a feature is to games in general. Building out the base of a Save Game function? That should be quick. Something super unique that requires careful engineering? Maybe a bit longer.
P.S. Now that I said programing a Save Game function is easy, it will become the bane of my existence. Till next time.