Being a part of the Gamedec dev team for 1.5 years as a programmer, Przemysław is responsible for creating gameplay and making tools for the design team. His input creates solutions to make the work for designers easier. In his free time, Przemek likes to stay creative by being in the loop with new technologies and building new sets of LEGO bricks.
Gamedecis a single-player, non-combative cyberpunk isometric RPG. You are a Gamedec – a private detective who solves crimes inside virtual worlds. The decisions you make will alter your character’s traits and impact the course of the game, but cases are riddled with dilemmas and are rarely clear-cut. You are the sum of your choices.
Gamedec is a game based on many vast and complex dialog and decision branches. One decision can open a way to solve the problem but also can block another. To create a game with that much complexity requires special tools. In conjunction with Unreal Engine, we use Articy, which is software that encompasses game writing, planning, and content management in one coherent, visual tool.
Image courtesy of Anshar Studios
Here's one example of branching pathways in Gamedec.
Objective
To make the Gamedec world more vibrant, designers need to be able to influence it not only through rich dialog, but also through NPC movements, changes in the world, atmospheric music, and more. The player needs to see that they are truly interacting with the game, and not just clicking through walls of text.
In the beginning, the Gamedec team had a tough nut to crack. To influence the world based on players' choices, they had to rely on a unique sequence representing an Articy object called a technical name.
There are several drawbacks to this approach. Looking into a level Blueprint at first glance, we don't know which dialog fragment is represented by a particular technical name, where it is invoked, and what it does. We must open Articy, find the node with that technical name, and analyze a branch to understand what should happen in the game. Also, team members who only work with Articy don't have clear information about which node triggers the action in Unreal Engine.
Image courtesy of Anshar Studios
Our first approach to execute events in response to a dialog choice was based on a switch like this.
Image courtesy of Anshar Studios
Here's one example of branching pathways in Gamedec.
The second obvious problem is the need to change the technical name in level Blueprints when the designer chooses another node that should trigger the event in Unreal Engine. Creating a game, especially one with so many lines of dialog and interactions, is a very iterative process. The need to make changes in several places at the same time heightens the risk of making a mistake, which can take up a lot of time.
The solution
To overcome these issues, Articy offers a very convenient mechanism that allows us to perform our functions. Inserting a function call in the instruction block eliminates the problems the team had to deal with mentioned above.
Image courtesy of Anshar Studios
An instruction block solves this problem.
The solution to the problem is an instruction block. The first advantage of such a feature is that the designer knows exactly when a given activity is performed. It's worth noting that changing the instruction block's location does not require changes in Unreal Engine; we call a function that has already been defined at another point in the dialog.
Using your own functions also gives you a significant advantage. The gameplay programmer doesn't have to define even the smallest action at the Blueprint level anymore. We have created many functions, making it easier to control the game from the Articy level while creating the dialog.
For example, if we want an NPC we're conversing with to take us to a specific place we're talking about, we can do it using two GoTo and FollowBy instructions, passing them the necessary parameters like location, walk velocity, etc.
Image courtesy of Anshar Studios
This cutscene is invoked by dialog choice.
Implementation
Of course, Articy has no idea where something like Locations_TheaterDoor is located and which NPC is Characters_Admin. We have to define this in Unreal Engine. For this purpose, we create a component that allows us to register the actor to the system.
Image courtesy of Anshar Studios
We registered our theater door, seen above, as Locations_TheaterDoor.
The system is straightforward. When the level is started, all actors with this component register to an object that stores this data in dictionaries. The key is the given identifier, and the value is a reference to the actor.
Okay, but how does Articy and UE4 binding look? And how do these functions work? First of all, if we put our function into the instruction block, the plugin importing data from Articy to Unreal Engine generates an interface: UserMethodProvider, which contains definitions of all functions used in Articy.
Image courtesy of Anshar Studios
This listing presents a fragment of the generated interface.
To make sure that the generated interface will always contain a set of available user-created functions, we created an Articy flow section containing all the defined instructions with their parameters. It is never executed but goes to the export, so we can be sure that all our functions will be in this interface, even if they are not used anywhere. This allows us to avoid problems later. It's sort of like an API declaration for Unreal Engine.
Image courtesy of Anshar Studios
Here's a flow fragment containing a set of all available functions.
The Articy plugin for Unreal Engine contains a UAArticyFlowPlayer class. It navigates through the dialog tree and allows the exploration of dialog branches. This class is where you find a function to check if the actor or any of the components implement the above-described interface. Generally speaking, that's it. Now we can create a component that implements the UserMethodProvider interface to define the behavior for our functions.
In Gamedec's case, we were tempted to break down the interface into smaller ones, as it has begun to grow a lot under the pressure of successive feature additions. We created smaller interfaces containing only a group of functions. Then we created a façade that implements the base interface and delegates the execution of the functions to their respective objects. This solution allows us to break the system into smaller fragments and maintains the logical integrity of the whole.
Additionally, we can register multiple classes to respond to a specific event. The problem we came across was the repeated execution of the same function. This is due to the specificity of the traverse action of the tree. FlowPlayer analyzes all possible paths before actually running a particular dialog block – it’s related to the conditions that need to be met to determine whether or not we can enter a specific branch, and thus display a list of available options.
However, there is an Articy feature called Shadow Mode that helps here. At the moment, when Articy analyzes the tree to check which branches are available, it does so in this special (Shadow) mode, which does not affect any variables and gameplay. When we go through a given node, then we are not in Shadow Mode, and only then should our functions be executed. That's something to which we should really pay attention.
Image courtesy of Anshar Studios
Problems
The use of your own functions in Articy is also associated with a particular problem – Articy, out of the box, does not give us any mechanism to syntax check its own functions. The designer doesn't have information about whether the used function exists, and the parameters passed to it are consistent with implementation in Unreal Engine. For a while, it was a kind of problem.
There were situations when, for a few hours, we were analyzing which change in Articy broke the daily build.
Sometimes it was simple typos in function names, the wrong type or an incorrect number of arguments, and errors that could be quickly caught by analyzing the log. One time, a semicolon was missing in the last function in the block. Usually, this is not a problem because the importer adds the missing semicolon at the end of the function. In this case, we forgot to add the semicolon to the function and added a comment block behind the function.
An example of problematic instruction:
CallEvent("OpenHiddenRoom")// Open the secret room
We couldn't quickly find this problem by analyzing the logs, because the error pointed to a completely different place. As a result, we had to laboriously analyze all the changes made and comment on them to find the error. When we forgot to add a semicolon to the end of the last function and added a comment block, and then added a plugin to import data from Articy, a bug appeared in the engine. It changed the return type from void to object, and as a result, we got a compilation error.
That day, a decision was also made to try to eliminate or at least minimize the risk of repeating those situations. Unfortunately, Articy doesn't allow us to define headers for user-created functions, and in effect, designers don't have a syntax check and can make mistakes.
Articy, however, does allow you to create your own plugins. Thus, we created a plugin that analyzed all the instruction blocks to find the functions, and then checked if they were compatible with their definition. We installed this plugin for every member of the team that was actively working with Articy, and from that moment on, that issue hasn't happened in our daily builds.
Image courtesy of Anshar Studios
Conclusion
In game development, every decision matters, like what you pick for your software, toolchain, and software architecture. Choosing Articy as a tool to create interactions and extending it with user-created functions and our own plugin was the right choice for us. The ability to create editor utility widgets for Unreal Engine allowed us to quickly develop tools for debugging interactions between Articy and UE4, which was crucial for our game's development. Making timely decisions about simplifying work has brought tangible benefits to the team in the form of faster iteration of interactions, as well as rapid prototyping that's allowed us to lead the player without having to involve programmers.
The possibility to quickly prototype the game using Blueprints combined with controlling the flow of gameplay directly from the dialogues created in Articy made it an excellent duo for creating a choice-driven game like Gamedec. It allows the designer to quickly test if any changes done to the project present the story in the right way.
In terms of design, a quest-designer doesn't have to involve the rest of the team like programmers or graphic designers to create the interaction skeleton in Unreal Engine. They can do it on their own, play around with it, and iterate. This is fantastic and saves us a lot of time-consuming communication. Articy, combined with Unreal Engine, provides many mechanisms and tools that enable us to adjust them to our needs in the best possible way for the development of Gamedec.
Get Unreal Engine today!
Get the world’s most open and advanced creation tool.
With every feature and full source code access included, Unreal Engine comes fully loaded out of the box.