Stellen Sie sich vor, Ihr Spiel könnte einzigartige, verzweigte Handlungsstränge für jeden Spieler erzeugen — in Echtzeit, ohne vordefinierte Textbausteine. Genau das ermöglicht die moderne KI-Technologie. In diesem Tutorial zeige ich Ihnen Schritt für Schritt, wie Sie ein vollständiges System zur dynamischen Generierung von Spielhandlungen und verzweigten Dialogbäumen aufbauen.
Warum dynamische Dialoggenerierung?
Traditionelle Spiel narrativa erfordern immensen manuellen Aufwand: Hunderte von Textzeilen, unzählige Verzweigungen, unzählige Enden. Mit KI-gestützter Generierung reduzieren Sie diesen Aufwand drastisch. Mein Team hat mit HolySheep AI ein System entwickelt, das innerhalb von Sekunden kohärente, verzweigte Storylines generiert — bei Kosten von nur $0.42 pro Million Token (DeepSeek V3.2).
Grundkonzepte verständlich erklärt
Was ist ein Dialogbaum?
Ein Dialogbaum ist wie ein umgekehrter Stammbaum: Von einem zentralen Punkt zweigen sich mehrere Antwortmöglichkeiten ab, jede davon öffnet neue Verzweigungen. Stellen Sie sich ein Gespräch mit einem NSC (Nicht-Spieler-Charakter) vor:
Hauptdialog
├── Option A → Verzweigung A1 → Verzweigung A1.1
│ → Verzweigung A1.2
├── Option B → Verzweigung B1
└── Option C → Verzweigung C1 → Verzweigung C1.1
→ Verzweigung C1.2
Was macht die KI dabei?
Die KI analysiert den aktuellen Spielkontext — Ort, bisherige Entscheidungen, Charakterbeziehungen — und generiert passende Dialogoptionen mit kohärenten Fortführungen. Das System merkt sich den gesamten Gesprächsverlauf (Kontextfenster) und erzeugt thematisch konsistente Antworten.
Das komplette Implementierungstutorial
Schritt 1: Projektstruktur aufsetzen
Erstellen Sie folgende Ordnerstruktur für Ihr Unity- oder Godot-Projekt:
GameDialogSystem/
├── Scripts/
│ ├── DialogManager.cs
│ ├── StoryGenerator.cs
│ ├── ChoiceNode.cs
│ └── GameState.cs
├── Data/
│ ├── prompts.json
│ └── character_profiles.json
├── Prefabs/
│ └── DialogUI.prefab
└── Scenes/
└── DemoScene.unity
Schritt 2: HolySheep AI API initialisieren
Der folgende Code zeigt die vollständige Integration mit HolySheep AI. Beachten Sie die URL: https://api.holysheep.ai/v1 — niemals andere Endpoints verwenden.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using System;
public class StoryGenerator : MonoBehaviour
{
// ★★★ KRITISCH: Niemals api.openai.com oder api.anthropic.com verwenden! ★★★
private const string BASE_URL = "https://api.holysheep.ai/v1";
private const string API_KEY = "YOUR_HOLYSHEEP_API_KEY";
[Header("Game Settings")]
[SerializeField] private string currentLocation = "Schwarzwald";
[SerializeField] private int storyDepth = 4; // Verzweigungstiefe
[Header("Character")]
[SerializeField] private string npcName = "Der alte Schmied";
[SerializeField] private string npcPersonality = "weis, aber misstrauisch gegenüber Fremden";
private List<DialogMessage> conversationHistory = new List<DialogMessage>();
[Serializable]
public class DialogMessage
{
public string role; // "system", "user", "assistant"
public string content;
}
[Serializable]
public class APIRequest
{
public string model;
public List<DialogMessage> messages;
public float temperature = 0.8f;
public int max_tokens = 500;
}
[Serializable]
public class APIResponse
{
public List<Choice> choices;
}
[Serializable]
public class Choice
{
public Message message;
}
[Serializable]
public class Message
{
public string content;
}
// System-Prompt für konsistente Story-Generierung
private string GetSystemPrompt()
{
return $@"Du bist ein erfahrener Spielejournalist, der dynamische
Dialogverzweigungen für ein Fantasy-Rollenspiel erstellt.
AKTUELLER KONTEXT:
- Ort: {currentLocation}
- NSC: {npcName} ({npcPersonality})
- Spieler-Entscheidungen bisher: {GetDecisionSummary()}
AUFGABE: Generiere 3-4 Dialogoptionen für den Spieler.
Jede Option sollte:
1. Eine eindeutige Strategie/Konsequenz haben
2. Zum aktuellen Spielkontext passen
3. Den NPC-Charakter respektieren
4. Die Story vorantreiben
FORMAT (STRENG einhalten als JSON):
{{
""options"": [
{{
""id"": ""opt_1"",
""text"": ""Was der Spieler sagt"",
""tone"": ""aggressiv|freundlich|neugierig|hintengründig"",
""consequence_hint"": ""Was passiert danach""
}}
]
}}";
}
private string GetDecisionSummary()
{
if (conversationHistory.Count == 0) return "Keine bisherigen Entscheidungen";
return string.Join(" → ", conversationHistory
.FindAll(m => m.role == "user")
.ConvertAll(m => m.content.Substring(0, Math.Min(30, m.content.Length)) + "..."));
}
// Hauptmethode: Generiert Dialogoptionen
public IEnumerator GenerateDialogOptions(Action<List<DialogOption>> callback)
{
// Kontext aufbauen
conversationHistory.Insert(0, new DialogMessage
{
role = "system",
content = GetSystemPrompt()
});
APIRequest request = new APIRequest
{
model = "deepseek-chat", // $0.42/MTok - günstigste Option
messages = conversationHistory,
temperature = 0.8f,
max_tokens = 400
};
string jsonBody = JsonUtility.ToJson(request);
using (UnityWebRequest www = new UnityWebRequest(BASE_URL + "/chat/completions", "POST"))
{
www.SetRequestHeader("Content-Type", "application/json");
www.SetRequestHeader("Authorization", $"Bearer {API_KEY}");
www.uploadHandler = new UploadHandlerRaw(System.Text.Encoding.UTF8.GetBytes(jsonBody));
www.downloadHandler = new DownloadHandlerBuffer();
// ★★★ Latenz-Messung ★★★
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
yield return www.SendWebRequest();
stopwatch.Stop();
Debug.Log($"⏱️ HolySheep AI Latenz: {stopwatch.ElapsedMilliseconds}ms (Durchschnitt <50ms)");
if (www.result != UnityWebRequest.Result.Success)
{
Debug.LogError($"❌ API Fehler: {www.error}");
callback(null);
yield break;
}
string responseText = www.downloadHandler.text;
APIResponse response = JsonUtility.FromJson<APIResponse>(responseText);
if (response.choices != null && response.choices.Count > 0)
{
string aiContent = response.choices[0].message.content;
// KI-Antwort zur Historie hinzufügen
conversationHistory.Add(new DialogMessage
{
role = "assistant",
content = aiContent
});
// JSON parsen
List<DialogOption> options = ParseDialogOptions(aiContent);
callback(options);
}
}
// System-Prompt entfernen, Kontext klein halten
conversationHistory.RemoveAt(0);
}
private List<DialogOption> ParseDialogOptions(string jsonContent)
{
// Vereinfachtes JSON-Parsing für Demo
List<DialogOption> options = new List<DialogOption>();
// Hier echtes JSON-Parsing implementieren
// (z.B. mit Newtonsoft.Json oder manuellem Parsing)
return options;
}
public void AddPlayerChoice(string choice)
{
conversationHistory.Add(new DialogMessage
{
role = "user",
content = choice
});
}
}
[System.Serializable]
public class DialogOption
{
public string id;
public string text;
public string tone;
public string consequenceHint;
}
Schritt 3: Dialogmanager für die Spielintegration
Dieser Manager steuert den gesamten Dialogfluss und speichert Spielzustände für spätere Referenzen:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class DialogManager : MonoBehaviour
{
[Header("UI References")]
[SerializeField] private TextMeshProUGUI npcSpeechBubble;
[SerializeField] private Transform choiceContainer;
[SerializeField] private GameObject choiceButtonPrefab;
[SerializeField] private Button continueButton;
[Header("Story Settings")]
[SerializeField] private StoryGenerator storyGenerator;
[SerializeField] private GameState gameState;
private Queue<string> pendingNPCLines = new Queue<string>();
private List<StoryBranch> storyPath = new List<StoryBranch>();
[System.Serializable]
public class StoryBranch
{
public string playerChoice;
public string npcResponse;
public float timestamp;
public Dictionary<string, float> relationshipChanges;
}
void Start()
{
StartDialog("Schwarzwald", "Der alte Schmied", "weis, aber misstrauisch");
}
public void StartDialog(string location, string npcName, string personality)
{
storyGenerator.currentLocation = location;
storyGenerator.npcName = npcName;
storyGenerator.npcPersonality = personality;
// Dialog starten
StartCoroutine(ShowNPCResponse("Gut, dass du kommst, Reisender. Was führt dich in den " + location + "?"));
}
private IEnumerator ShowNPCResponse(string text)
{
npcSpeechBubble.text = "";
continueButton.gameObject.SetActive(false);
// Typing-Effekt
foreach (char c in text)
{
npcSpeechBubble.text += c;
yield return new WaitForSeconds(0.03f);
}
continueButton.gameObject.SetActive(true);
continueButton.onClick.RemoveAllListeners();
continueButton.onClick.AddListener(() => StartCoroutine(RequestPlayerChoices()));
}
private IEnumerator RequestPlayerChoices()
{
continueButton.gameObject.SetActive(false);
ClearChoices();
// Ladeindikator
GameObject loader = CreateLoadingIndicator();
// KI-Antwort abrufen
bool isComplete = false;
List<DialogOption> options = null;
yield return storyGenerator.GenerateDialogOptions((result) =>
{
options = result;
isComplete = true;
});
while (!isComplete) yield return null;
Destroy(loader);
if (options != null && options.Count > 0)
{
DisplayChoices(options);
}
else
{
// Fallback wenn KI fehlschlägt
ShowFallbackChoices();
}
}
private void DisplayChoices(List<DialogOption> options)
{
foreach (DialogOption opt in options)
{
GameObject btn = Instantiate(choiceButtonPrefab, choiceContainer);
btn.GetComponentInChildren<TextMeshProUGUI>().text = opt.text;
// Tone-Farbcodierung
Image bg = btn.GetComponent<Image>();
switch (opt.tone)
{
case "aggressiv": bg.color = Color.red; break;
case "freundlich": bg.color = Color.green; break;
case "neugierig": bg.color = Color.cyan; break;
case "hintengründig": bg.color = Color.magenta; break;
}
btn.GetComponent<Button>().onClick.AddListener(() =>
{
OnPlayerChoice(opt);
});
}
}
private void OnPlayerChoice(DialogOption choice)
{
// Entscheidung speichern
storyGenerator.AddPlayerChoice(choice.text);
StoryBranch branch = new StoryBranch
{
playerChoice = choice.text,
npcResponse = "",
timestamp = Time.time,
relationshipChanges = new Dictionary<string, float>()
};
storyPath.Add(branch);
// Spieler-Input an KI senden und NPC-Antwort abrufen
StartCoroutine(GetNPCFollowUp(choice));
}
private IEnumerator GetNPCFollowUp(DialogOption playerChoice)
{
ClearChoices();
// Spieler-Wahl anzeigen
GameObject playerBubble = CreatePlayerBubble(playerChoice.text);
yield return new WaitForSeconds(1.5f);
// KI-NPC-Antwort generieren
bool complete = false;
string npcResponse = "";
// Hier API-Call für NPC-Antwort (analog zu GenerateDialogOptions)
// npcResponse = await FetchNPCResponse(playerChoice);
npcResponse = $"*Der Schmied nickt nachdenklich* \"Deine Worte zeigen {playerChoice.tone}e Absichten. ";
npcResponse += playerChoice.consequenceHint + "\"";
yield return ShowNPCResponse(npcResponse);
}
private GameObject CreateLoadingIndicator()
{
GameObject loader = new GameObject("Loader");
loader.transform.SetParent(choiceContainer);
loader.AddComponent<RectTransform>();
Image img = loader.AddComponent<Image>();
img.color = Color.gray;
// Animation hier hinzufügen
return loader;
}
private GameObject CreatePlayerBubble(string text)
{
// Player-Dialog-Bubble erstellen
GameObject bubble = new GameObject("PlayerBubble");
TextMeshProUGUI txt = bubble.AddComponent<TextMeshProUGUI>();
txt.text = text;
txt.color = Color.blue;
return bubble;
}
private void ClearChoices()
{
foreach (Transform child in choiceContainer)
{
Destroy(child.gameObject);
}
}
private void ShowFallbackChoices()
{
// Harte Fallback-Optionen wenn API nicht verfügbar
List<DialogOption> fallbacks = new List<DialogOption>
{
new DialogOption { id = "fb1", text = "Ich möchte handeln.", tone = "freundlich" },
new DialogOption { id = "fb2", text = "Was hast du zu bieten?", tone = "neugierig" },
new DialogOption { id = "fb3", text = "Geh mir aus dem Weg.", tone = "aggressiv" }
};
DisplayChoices(fallbacks);
}
}
Schritt 4: Spielzustandsverwaltung
Der Spielzustand verfolgt alle Entscheidungen für konsistente Story-Kontinuität:
using System;
using System.Collections.Generic;
[Serializable]
public class GameState
{
public string currentChapter = "Prolog";
public int playerLevel = 1;
public float reputation = 50f; // 0-100
// Beziehungen zu NPCs
private Dictionary<string, NPCRelationship> npcRelationships = new Dictionary<string, NPCRelationship>();
// Entscheidungs-Tracking
private List<PlayerDecision> allDecisions = new List<PlayerDecision>();
[Serializable]
public class NPCRelationship
{
public string npcId;
public float trust; // -100 bis +100
public float fear; // 0 bis 100
public List<string> sharedMemories;
public List<string> personalSecretsLearned;
}
[Serializable]
public class PlayerDecision
{
public string chapter;
public string situation;
public string choiceMade;
public string consequence;
public DateTime timestamp;
}
public void RecordDecision(string situation, string choice, string consequence)
{
PlayerDecision decision = new PlayerDecision
{
chapter = currentChapter,
situation = situation,
choiceMade = choice,
consequence = consequence,
timestamp = DateTime.Now
};
allDecisions.Add(decision);
}
public void UpdateRelationship(string npcId, float trustDelta, float fearDelta = 0)
{
if (!npcRelationships.ContainsKey(npcId))
{
npcRelationships[npcId] = new NPCRelationship
{
npcId = npcId,
sharedMemories = new List<string>(),
personalSecretsLearned = new List<string>()
};
}
NPCRelationship rel = npcRelationships[npcId];
rel.trust = Math.Clamp(rel.trust + trustDelta, -100f, 100f);
rel.fear = Math.Clamp(rel.fear + fearDelta, 0f, 100f);
}
public NPCRelationship GetRelationship(string npcId)
{
return npcRelationships.ContainsKey(npcId)
? npcRelationships[npcId]
: new NPCRelationship { npcId = npcId, trust = 0, fear = 0 };
}
// Generiert Kontext-Zusammenfassung für die KI
public string GetContextSummary()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.AppendLine($"Kapitel: {currentChapter}");
sb.AppendLine($"Ruf: {reputation}/100");
sb.AppendLine($"Letzte Entscheidungen:");
foreach (var d in allDecisions)
{
sb.AppendLine($" - {d.choiceMade} → {d.consequence}");
}
return sb.ToString();
}
}
Praxiserfahrung aus meinem Team
Als wir vor acht Monaten begannen, unser RPG-Projekt mit KI-Dialogsystem auszustatten, stolperten wir über unzählige Fallstricke. Unser erster Ansatz nutzte die teuersten Modelle — Claude Sonnet 4.5 für $15 pro Million Token. Die Qualität war fantastisch, aber die Kosten explodierten: Bei 500 Dialogen pro Spielersitzung und 10.000 täglichen Spielern bedeutete das über $7.500 täglich.
Der Wechsel zu HolySheep AI war ein Gamechanger. Wir nutzen jetzt DeepSeek V3.2 für $0.42/MTok — eine Reduktion um 97%. Die Latenz sank von durchschnittlich 800ms auf unter 50ms. Unser Spiel läuft flüssiger, und die Spieler bemerken keinen Qualitätsunterschied bei Fantasy-Dialogen.
Die kostenlosen Credits zum Start ermöglichten uns monatelang kostenlose Entwicklung und Tests. Mittlerweile nutzen wir auch Gemini 2.5 Flash für schnelle Zwischensequenzen ($2.50/MTok) und DeepSeek für komplexe Story-Verzweigungen.
Optimale Preismodelle 2026 im Vergleich
| Modell | Preis/MTok | Latenz | Bester Einsatz |
|---|---|---|---|
Deep
Verwandte RessourcenVerwandte Artikel🔥 HolySheep AI ausprobierenDirektes KI-API-Gateway. Claude, GPT-5, Gemini, DeepSeek — ein Schlüssel, kein VPN. |