In this post we’re going to implement a basic dialog system in C++. Here is a breakdown of our system:

  • The player can initiate a conversation with a pawn
  • The pawn will answer to our player based on the player’s message
  • The player can terminate the conversation

Before we dive into creating our system, here is the end-result:

Disclaimer: I’ve used some Blueprint code on my Character’s blueprint just to avoid classes which fall off the scope of this tutorial. In case you want to create this system using C++ only, you can read this post in order to convert my logic to C++.

You can find all the code for this tutorial on my github repo.

Gathering the necessary assets

For this system we need some audio recordings. I used audacity to record myself and then I imported my recordings inside the engine (don’t record yourself just yet! You will know why in a bit!)

In total I’ve used 5 recordings:

  • Players’ hi message (and the Pawns’ answer)
  • Players’ small talk message (and the Pawns’ answer)
  • Players’ goodbye message

Setting up our project

Create a Third Person C++ Template Project and add the following keybind:

Keybind

In order to determine when our UI should show the subtitles of our dialogs we’re going to use two structs. The first, will include the excerpt of the players’ choice (this will appear inside the buttons of the UI), the corresponding sfx, an array of subtitles and a bool variable which will be used in order to determine if we want the other pawn to respond.

The second struct is the subtitle itself. Each subtitle includes the associated text that will be shown inside our UI as well as the associated time, in seconds, which is the time that the subtitle will appear on our screen. This is identical to how subtitles work for movies! Note that the time in this case is relative – an associated time of 0 seconds means that the subtitle should appear when our sfx starts. An associated time of 1 second means that the subtitle should appear 1 second after our SFX has started playing.

Since I recorded myself using audacity, I’ve used it’s interface to determine the actual time for each subtitle.

Let’s take a look at the sound wave of the second recording which contains the following subtitles:

  1. Hey this is a pretty nice place…
  2. right?

audacity

Notice that I have selected the time right before the second subtitle should go off and audacity informs me (see the red arrow of the screenshot) that this sound will be played after 2.7 seconds. This value is the associated time. Now you know the workflow to find the correct associated time without hardcoding or guessing!

I suggest to create your recordings at this point since we’ll be focusing on our code from now on.

Note: As a convention from this point I will refer to the second pawn as an AI. Don’t get confused – this post contains no AI at all!

Having said that, let’s add the following C++ structs (in case you don’t remember how to add a c++ struct, check out this post):

  • A struct named Dialog
  • and a struct named Subtitle

Here is my code for the Subtitle struct:

Don’t forget to include the “Engine/DataTable.h” library

Here is my code for the Dialog struct:

Moreover, in the dialog struct add the following includes:

Compile your code and switch to your editor. Then, add a new Data Table, named PlayerLines, based on the Dialog struct and add the following rows:

player_lines

When you’re done with that, create a second data table, named AILines, based on the dialog struct and populate it using the same workflow:

ai_lines

Make sure that both the Player Lines and the AI Lines have the same row names. This has to do with the code that we’re going to implement later on.

In the AI table you can omit some fields, like QuestionExcerpt and the ShouldAnswer variable because we won’t use them.

Creating our dummy AI

Create a new C++ class, named AICharacter, which inherits the character class and add the following code inside the header file:

Don’t forget to include the following libraries:

For now, implement all the functions we’ve added by adding no code inside them. We will implement them later on!

Switch to the source file of your AI and add the following initializations inside the constructor:

Then, modify your begin play functions to match the following code:

Compile and save your code. Switch to your editor and create a blueprint based on the above c++ class. Then:

  • Assign the default static mesh and the default animation blueprint
  • Increase the box comp extent to a relative large scale (I’ve used 250 x 250 x 100)

Let’s leave the AI for now. We’ll come back later on to implement more of it’s functionality.

Creating our UI

Navigate to your [Your_Project_Name].Build.cs file and add the following line of code:

Then, navigate to your [Your_Project_Name].h file and add the following includes:

Then, switch to your editor and add a new C++ class, which inherits from the UserWidget class. I named my class DialogUI. Open up the header file and add the following code:

Moreover, don’t forget to add the Dialog.h file.

Implement an empty logic for the UpdateSubtitles function for now.

Compile and save your code!

Create a blueprint based on the above C++ class and implement the following UI:

dialog_ui

Dialog UI – Tap to open in a new tab

Make sure to bind the variable SubtitleToDisplay in the middle text box.

Moreover, mark the Q1,Q2 and Q3 text boxes as variables.

Switch to the Graph tab of the UMG editor and implement the following custom event:

dialog_ui_show2

Note: You could bind C++ properties to these text boxes as well. I just used this way instead in this case!

Let’s leave the UMG for now and implement some logic on our character!

Creating the logic for our character

Navigate to your character’s header file and add the following code:

Don’t forget to add the following includes before the .generated.h file:

Switch to your character’s source file and inside the constructor add the following code:

Then, let’s add the bind we’ve added through our editor. Go inside the SetupPlayerInputComponent function and add the following line of code:

Moreover, I modified my MoveRight and MoveForward functions in order to disable the player movement if he’s talking like so:

When you’re done with that, implement the Toggle Talking function:

Right here, create an empty body for the rest functions and let’s implement the logic for the ToggleUI function:

bp_mainchar

Then, implement the following logic for the GeneratePlayerLines:

In order for the above function to work, we also need to implement the Retrieve Dialog function:

As a last step, implement the following logic for the Talk function:

Now we are ready to implement the logic inside our dummy AI.

Revisiting our dummy AI

Navigate to the source file of your ai and add the following implementations of the overlap functions:

Then, add the following implementation of the AnswerToCharacter and Talk functions:

Revisiting our UI

The only thing left is to associate the button click with each question excerpt and show our subtitles! Open up the graph of your UMG and create the following function:

playgivenlines_umg

Note that The Associated Text Block is a Text Reference.

Then, implement the following OnClick functionality for each button:

buttonclicks_umg

The last piece of code is kinda catchy, so before we type in our code, I will explain why we need this particular approach.

Multithreading in Unreal

As users, we need every software application to be responsible at any given time, even if the application is going into crash we need to be informed so we can take action. This is done by using Multithreading.

In every application (including games) the thread that is responsible for the UI is considered as the main thread. By using multiple cores of our CPU in our application we are able to perform relatively heavy calculations in another thread while our main thread remains responsive.

In this post we’ve said that we’re going to wait for a certain amount of time before we display our subtitles, which is totally fine. But if we “freeze” our UI the whole game will freeze. You definitely don’t want that! That’s why, I’ve used multithreading in order to update the subtitles in time.

I’ve created a low priority thread in order to update my UI subtitles. This thread literally pauses it’s execution until it’s time for the next subtitle to be shown. Since this thread is a different than the main thread our game won’t get paused and the user won’t notice a thing! Luckily, Unreal Engine provides an easy way to achieve multithreading!

Right under the end of the DialogUI class, add the following class (you don’t need to use the editor in order to add this particular class!):

Just to clarify, here is a screenshot of my code:

cpp_async

When you’ve typed the said code, implement the following logic inside the UpdateSubtitles function:

Compile your code and test your system!

Share postShare on Facebook10Tweet about this on TwitterShare on LinkedIn0Share on Reddit0