When we felt good about our third prototype last year and decided to move forward with the concept, it was time to take what we had created and place it within a more rigorously defined architecture. Finding the fine line between keeping our momentum and making sure the foundational pieces were sturdy enough to build upon was tough.
In retrospect I think some decisions I made lost us some of that momentum. With a small team going several months with what feels like little forward progress can be pretty stressful. Having said that, at this point I feel pretty good about where we ended up.
There were a few high level goals we set out to achieve:
Make the ability system more modular
The prototype had abilities that were built into either characters directly (i.e. Chuck) or their archetype (i.e. Melee Guardian). This was fine for throwing things together, but a system like that would not scale very well.
The goal is to build with the current design in mind, leave openings where possible to expand in ways we’ve speculated upon. And if we do it right, adding content moving forward should be accelerated substantially.
We decided pretty early on to use the Unreal Engine’s Gameplay Ability System (GAS) to form the foundation for all of our gameplay abilities. GAS has built-in facilities for complex enabling and blocking abilities using Gameplay Tags, cooldowns, networking support, and client prediction. It also allows for an entire ability to be defined within an activation event, with asynchronous tasks used to wait on user input or targeting data.
The big risk here is the system is quite complex and documentation from Epic is somewhat sparse, although they do provide several example projects that implement it. Multiple AAA titles use GAS so it seemed like a pretty safe bet from a stability perspective. There are several great sources of external documentation on the system which I’ll link to below.
Even then this was a quite large portion of our re-work time, just wrapping my head around how this stuff is setup and how to use it for our system, and debugging it effectively when we have issues (still learning on that one). There were a few times where I was second-guessing whether this system is appropriate for such a small team, but we stayed the course.
We’ve also increased our use of Actor Components to encapsulate functionality. We tried to identify where these can be attached to arbitrary actors wherever we could, so building up new actor classes could take advantage of work already done. Some examples include:
Ability & Equipment configuration.
Team configuration.
Health tracking, to react to health changes.
Turn Control functionality, issuing and following commands.
Strike a good balance between C++ code and Blueprints
For our current and planned team makeup, having as much flexibility present in the Blueprints of our project is essential. We want the majority of the team to be able to make meaningful changes as quickly as possible, while also trying to keep the parts of the code that need to be performant in C++. I’ll link a fantastic video below by Alex Forsythe that sums up a very good line of distinction on where things should go for most teams.
We did have an issue with Blueprint corruption in our prototype build. Components of the actors would somehow get set to NULL when they should have instantiated valid data. It seemed to be triggered when we’d add or change a component in the base C++ class. The only thing we could reliably fix this with was a rebuild of the blueprint, which was very painful for some of our larger blueprint classes.
Now we’re trying to keep our blueprints as compact and manageable as possible. And if we do encounter the problem again, rebuilding them will be less painful if they’re in smaller chunks. We haven’t seen the corruption since moving to a fresh project that started from Unreal Engine 5, so perhaps the prototype project being upgraded through several versions, and jumping from UE4 to UE5 was part of the issue.
Salvage as much from prototype as possible
There were many features that we did quickly for the prototype. We wanted to bring the parts that worked well over to the new project, but often they needed a little love during the port. Some examples would be moving to the UE Enhanced Input system, and adjusting systems like our projectiles & interaction system to use G.A.S. equivalents. The underlying turn control code came over fairly cleanly, although we’re adjusting how you move between strategy and adventure modes after a lot of feedback in playtesting.
Dividends
The first big glimpse of the type of payoff we were hoping for was when Brad and I sat down to put Chuck’s new sword into the game. We first needed to create an Equippable that could grant the sword swinging ability, tag some montages for Chuck that would swing the sword and emit the right gameplay events at the right time to trigger damage. Then we simply opened Chuck’s Ability & Equipment helper component and added the sword to his Primary Ability slot.
I was so happy to see that it worked on our first try, which is always a joy to behold!
Of course there are plenty of features to be added, and rough edges to sand down, but the dream is to have a library of equipment and abilities that we can adorn our new characters with as they start pouring into the game.
Sources
Sam Deiter and Paulo Souza, Action RPG: Gameplay Abilities System | Inside Unreal
Commentaires