I have always had a passion for competitive multiplayer games, especially those that are ability based such as MOBAs. I decided to try my hand at making one as a solo project in Unreal. However, I quickly ran into a bit of a roadblock as my blueprints were getting very messy very fast. So, I went about developing subsystems to abstract and genericize core functionality so that my project would be clean and easy to debug and iterate on.
The first thing I created was a CharacterStats object, which I could use to both abstract character data, as well as verify if an object is a character. I attached the CharacterStats object to any actor that I wanted to classify as a character. It houses the functionality to initialize, “destroy” and update a character. I chose to manage characters in this way because of three factors. First, I can make any actor in the world a “character” without having to write new code. Second, as mentioned earlier, I could now easily check if an actor is a character by seeing if they had a CharacterStats object attached to them. This was especially useful for raycasting and collisions where I wanted to ignore characters/non-characters. Third, if I get to the stage of networking, I only need to worry about the CharacterStats object being synchronized as it serves as an abstraction layer for the actual character actor.
After creating the CharacterStats object, I thought about how I wanted to handle effects with durations that could be applied to a character, such as crowd control (CC) or damage over time/healing over time effects. In my pre-implementation planning I identified some major problems that I had to solve. First, I didn’t want CC effects of the same type to stack on top of each other. My solution to this was quite simple, store all CC and over time effects in two separate arrays. When a new effect is added to the CC array, parse the array for effects of the same type only keep the one with the strongest effect. The second, but similar, problem was characters being effected by multiple effects of the same name. My solution to this problem was similar to handling multiple effects of the same type, I simply attached a name tag to each effect and when that effect is added to an array that contains an effect with the same name, I refresh the duration on the existing one. Because all of my CC/over time effects were already stored in arrays updating them were easy. I store the arrays in the actors CharacterStats’ object then go through them and update each effect in the CharacterStats’ update function. With this functionality put into place, I now had a way to very quickly make new CC and over time effects without having to duplicate code.
Writing your core character functionality in this way, despite an initial time investment, not only allows for fast iteration down the line, but also makes your code easier to read and debug. However, I found that the time investment is not always worth it. Since I was working on a short time solo project I dumped so much time into genericizing my functionality that I ended up with very little content. This was all fine and good since this was for academic purposes but I think in a real world scenario I’d consider prioritizing content a bit more.