Creating a Dynamic Dialogue Engine with AI‑Driven Responses in Unity

Building a Custom Story Graph Editor in Unity (Visual Narrative System
Building a Custom Story Graph Editor in Unity (Visual Narrative System)
August 31, 2026
Dynamic Quest Generator Using AI
Dynamic Quest Generator Using AI + Rule‑Based Systems (AAA Design + Unity Implementation)
September 1, 2026
Building a Custom Story Graph Editor in Unity (Visual Narrative System
Building a Custom Story Graph Editor in Unity (Visual Narrative System)
August 31, 2026
Dynamic Quest Generator Using AI
Dynamic Quest Generator Using AI + Rule‑Based Systems (AAA Design + Unity Implementation)
September 1, 2026

Creating a Dynamic Dialogue Engine with AI‑Driven Responses in Unity

Modern narrative games increasingly blend authored dialogue with procedural, AI‑generated responses.
This allows for conversations that adapt to player choices, character personality, emotional tone, quest progress, and even long‑term narrative memory.

In this guide, we build a hybrid dialogue system where:

  • Writers create core dialogue nodes
  • The engine decides when to use AI generation
  • AI expands, rewrites, or synthesizes responses dynamically
  • Context and character personality are injected into prompts
  • The system runs fully inside Unity

1. System Architecture

The dialogue engine contains three layers:

  1. Authored Layer (ScriptableObjects, story graph)
  2. Runtime Dialogue Engine (flow controller)
  3. AI Response Layer (prompt builder + LLM API)

A conversation can switch between fixed and AI‑generated lines depending on the node type.

Player → DialogueNode → (AI Response?) → NextNode

2. Dialogue Node Structure

We extend the StoryNode to support AI generation modes:

public enum DialogueMode
{
    Authored,       // Use writer‑authored text
    AIGenerated,    // Fully AI‑generated response
    AIVariation,    // Rewrite/expand existing text
    AIChoice        // AI generates multiple choices
}

[System.Serializable]
public class DialogueNodeData
{
    public string speaker;
    public string text;

    public DialogueMode mode;

    public string personalityTag;  // "angry", "shy", "sarcastic"
    public string contextHint;     // "player betrayed him", etc.
}

3. AI Context Model

AI needs story context to generate consistent dialogue.
We define a context container:

public class DialogueContext
{
    public string playerName;
    public Dictionary<string, bool> flags = new Dictionary<string, bool>();
    public List<string> conversationHistory = new List<string>();
    public string questState;
}

4. Building the AI Prompt

We generate structured prompts at runtime:

public class AIPromptBuilder
{
    public static string BuildPrompt(DialogueNodeData node, DialogueContext ctx)
    {
        return 
$@"You are '{node.speaker}' speaking in a video game.
Personality: {node.personalityTag}
Context: {node.contextHint}
QuestState: {ctx.questState}

Conversation so far:
{string.Join("\n", ctx.conversationHistory)}

Task:
Mode = {node.mode}

If Authored: Return the line exactly as-is:
'{node.text}'

If AIVariation: Rewrite this line with more emotion but same meaning:
'{node.text}'

If AIGenerated: Generate a new line that fits the situation.

If AIChoice: Generate 3 different responses, numbered 1‑3.
Keep responses short, conversational and in‑character.";
    }
}

5. AI Integration Layer

This layer calls your LLM provider (OpenAI, Gemini, local model, etc.).
Example using UnityWebRequest:

public class AIClient
{
    public static async Task<string> QueryAsync(string prompt)
    {
        var body = new 
        {
            model = "gpt-4o-mini",
            messages = new[]{ new { role="user", content=prompt } }
        };

        var json = JsonUtility.ToJson(body);

        using var req = new UnityEngine.Networking.UnityWebRequest("https://api.openai.com/v1/chat/completions", "POST");
        byte[] bytes = System.Text.Encoding.UTF8.GetBytes(json);

        req.uploadHandler = new UnityEngine.Networking.UploadHandlerRaw(bytes);
        req.downloadHandler = new UnityEngine.Networking.DownloadHandlerBuffer();
        req.SetRequestHeader("Content-Type", "application/json");
        req.SetRequestHeader("Authorization", "Bearer " + APIKeys.OpenAI);

        await req.SendWebRequest();

        if (req.result == UnityEngine.Networking.UnityWebRequest.Result.Success)
        {
            return ExtractText(req.downloadHandler.text);
        }

        return "[AI Error]";
    }

    static string ExtractText(string json)
    {
        // Very simplified JSON parser
        return "\"choices\":[{\"message\":{\"content\":\""
            .GetBetween(json, "content\":\"", "\"");
    }
}

6. Dialogue Engine

The DialogueEngine handles:

  • Node traversal
  • Calling AI if needed
  • Updating memory/context
public class DialogueEngine
{
    private DialogueNodeData currentNode;
    private DialogueContext context;
    private StoryGraph graph;

    public DialogueEngine(StoryGraph g, DialogueContext ctx)
    {
        graph = g;
        context = ctx;
        currentNode = graph.startNode.GetDialogueData();
    }

    public async Task<string> GetLine()
    {
        string line = currentNode.text;

        // AI modes override authored text
        if (currentNode.mode != DialogueMode.Authored)
        {
            string prompt = AIPromptBuilder.BuildPrompt(currentNode, context);
            line = await AIClient.QueryAsync(prompt);
        }

        // store in history
        context.conversationHistory.Add($"{currentNode.speaker}: {line}");

        return line;
    }

    public void Choose(int index)
    {
        currentNode = currentNode.outputs[index].next.GetDialogueData();
    }
}

7. AI Choices (Dynamic Branching)

If a node is set to AIChoice, the AI will return numbered options:

1. "What happened to you?"
2. "Calm down. Let's talk."
3. "You're lying."

We parse them into branching UI:

public List<string> ParseChoices(string aiText)
{
    var lines = aiText.Split('\n');
    return lines.Where(x => x.StartsWith("1.") || x.StartsWith("2.") || x.StartsWith("3."))
                .Select(x => x.Substring(3))
                .ToList();
}

8. Maintaining Character Consistency

AI is unpredictable unless guided. Best practices:

  • Create a Character Profile ScriptableObject for every NPC
  • Include personality, speech patterns, motivations
  • Pass it to the AI prompt each time
  • Feed conversation history, but capped (last 6‑10 lines)

9. Preventing AI From Breaking Story Logic

The biggest risk: AI says things that break quests.
Solutions:

  • Hard rules inside prompt: “Do not reveal future events.”
  • Inject flags: “Character does NOT know X.”
  • Use AIVariation instead of full generation
  • Limit AIChoice to in‑context safe outputs
  • Keep critical story points authored

10. Example Help: Personality Templates

sarcastic → always slightly mocking, uses snarky tone
noble → formal, heroic, self‑sacrificing tone
timid → avoids confrontation, short sentences, soft voice
angry → short, explosive lines, confrontational
mentor → wise, guiding language, longer sentences

These templates can be referenced automatically by the prompt builder.


11. Advanced Features

  • AI summaries to compress memory
  • Personality drift over time
  • Dynamic emotions (anger/fear/trust)
  • Quest‑aware AI logic
  • Two‑NPC AI conversations
  • Procedural idle chatter systems

12. Summary

By combining authored nodes with AI‑generated responses, you create a dialogue system that is:

  • Flexible
  • Reactive
  • Emotionally expressive
  • Narratively robust
  • Deeply replayable

This hybrid model preserves the structure of traditional branching narratives while enabling the dynamism of AI‑driven storytelling.

 

 

Leave a Reply

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


Skip to toolbar