How should I be modifying my dialogue system to allow for branching?

I’m working on dialogue, and right now it’s really straightforward- each conversation/cutscene is stored as a series of single [line,speaker] pairs, so in order to play the entire dialogue the system simply needs to iterate through the array of lines and play them one-by-one until they run out, indicating the end of the conversation:


USTRUCT(Blueprintable)
struct FDialogueLine {
	GENERATED_BODY()

		UPROPERTY(EditAnywhere)
		FString text;
		
		UPROPERTY(EditAnywhere)
		FName speakerName;
};

USTRUCT(Blueprintable)
struct FDialogueLookupTable : public FTableRowBase {
	GENERATED_BODY()
		UPROPERTY(EditAnywhere)
		TArray<FDialogueLine> lines;
};

This is really all I need for linear dialogue, from here I could simply store each complete conversation as its own unique datatable and load the desired table whenever I want to play a conversation. However, if I wanted to add branching dialogue, where player choices selected during the conversation change the outcome, this no longer works. I have a kind of nebulous notion that I could do something like store each potential branch of the conversation in its own datatable, and make a choice look something like this:



USTRUCT(Blueprintable)
struct FDialogueChoice {
	GENERATED_BODY()

		UPROPERTY(EditAnywhere)
		FString text;

		UPROPERTY(EditAnywhere)
		UDataTable* Dialogue;
};

If I do it that way, I could simply place each branch of the dialogue in its own discrete datatable, add a TArray<FDialogueChoice> to every FDialogueLine, and check after each line was displayed to see if the system needs to break out of the current dialogue, display a list of choices, and load + iterate through a new array of FDialogueLine based on which FDialogueChoice was selected.

This kinda works, but I can quickly see it spiraling out of control as the complexity of conversations increases and it becomes borderline-impossible to remember how everything is connected together. Is there any easy way I could modify my design to make it more friendly and modular, or is this entire approach ill-advised from the getgo?

You can store the whole tree in a single data table. Just add a column, that holds for every node the id of the parent node. The parent of the root node(s) would be -1.
When selecting a node in the dialog, you walk through the table and select all the nodes that have the id of the selected node as parent.
It’s a little bit harder to parse, but your typical dialog has only very few choices. So you won’t run in performance issues.
You will need only one object to handle all dialogs. Just switch out the data table as needed.

That’s an interesting idea, I never considered folding everything into a single table; thank you for the suggestion! :slight_smile: