skip to Main Content

Implementing a basic Dialog System in C++

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!

Avatar photo

This Post Has 16 Comments

  1. In the screenshot right after the section “Revisiting our UI” the blueprint calls the Talk function, but in the C++ class, the subtitle is a parameter, not a return value (i’m implementing everything in C++ like in the Inventory tutorial), so what’s happening?

    1. The subtitles array is passed by reference. Since we pass a value by reference (which doesn’t include the const keyword) in a function, it means that the function will modify the given value by some way. UE4 translates this function with an output pin in the Blueprint graphs.

      -Orfeas

      1. Ok got it 😉 oh btw i’ve been trying to implement drag and drop functionality to your inventory system tutorial in C++, but i’m having kind of a hard time doing so :p do you think you could do a tutorial on that?

        1. I don’t think I’m going to expand old tutorials at this time. However, I’ll see if I can integrate this functionality in a future tutorial.

          -Orfeas

        1. You can’t do that on the Blueprint side. A workaround would be to create a BlueprintCallable setter function.

          -Orfeas

  2. Hey Orfeas,

    I implemented this code as you have it, and I double checked everything. I’m not getting any errors, however the game is crashing when I run to the AI and overlap the box. From what I can gather, the issue is a null pointer in GeneratePlayerLines function. It seems like the array is not accessible or isn’t filling in, so nothing can be generated? I’m a complete greenhorn when it comes to C++ and unreal, so this is really throwing me off. Any ideas?

      1. That’s the thing, I think PlayerLines might be null, but I don’t know how to remedy that. It’s like the code isn’t able to reference the data table I created in the editor.

        1. Have you assigned the required player lines in the corresponding character? Moreover, try to restart your editor and re-run your game. Sometimes this might fix your issue. Additionally, I would suggest to download the complete source code from GitHub and compare it with your local files.

          -Orfeas

          1. Hey Orfeas!

            I got it working! I didn’t know I needed to go into the blueprint for the AI and updated the dialog section with the appropriate lines. Now I’ve got it working, I just need to troubleshoot the timing of it.

            Also, just so you know, there was an update with Unreal for the .AddDynamic function, and there is a new parameter needed for the overlap functions. The function calls for another primitive component. Here is how I had to set it up to get OnBoxOverlap/OnBoxEndOverlap to work:

            UFUNCTION()
            void OnBoxOverlap(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherIndex, bool bFromSweep, const FHitResult& SweepResult);

            UFUNCTION()
            void OnBoxEndOverlap(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherIndex);

            1. Hi, I have the same issue but could not fix it yet.
              Im not sure what you mean by “updating the dialog section in the blueprint”

              Would be nice if someone is still there for help.

  3. Hello. Everything worked nice until one point – Creating the logic for our character. I am using default FPS character template, provided by the engine and I really don’t know where to put the code for it’s logic or how/where to create the empty body for the toggle UI.. a hint would be nice.. Thank you

  4. In your Revisiting our UI part, where did you get ‘Play Given Lines’ ? Do I need to declare it myself?

    1. Leaving this here in case anyone else has this issue but play given lines is a function created in blueprints, it’s not actually made in the event graph you have to make the function by creating a function to the left first.

  5. I have follow this tutorial with UE5 and work fine 🙂 , very useful and interesting way to programming.

Leave a Reply to Οrfeas Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Back To Top