VNLEWIKIGeschichten entwickeln. Welten verbinden.
Deutsch
VNLE Handbuch

Inhalte ins Spiel einbauen

VNLE exportiert Texte, Übersetzungen und den geplanten Ablauf. Dein Spiel liest diese Daten, zeigt die Dialogbox an und verbindet Spielaktionen mit seinen eigenen Systemen. Du kannst ein eigenes Skript oder eine Community-Integration verwenden. Die folgenden Beispiele erklären die Daten und benötigen keinen VNLE-Handler.

Dateien, JSON und Startpunkte

Exportieren → Dateien und JSON verstehen → Ablaufregeln nachschlagen

Alle Beispiele verwenden den echten Export „Der Hafenschlüssel“. Die langen IDs gehören genau diesem Projekt. Ersetze sie für dein Spiel durch IDs aus deinem Export; Namen und übersetzte Texte sind keine Verknüpfungsschlüssel.

story.json · story-index.json · Bearbeitbares Beispielprojekt

Deine Engine auswählen

Jede Seite erklärt den Ladeweg und die Anzeige in der jeweiligen Engine. Der Lernweg darunter erklärt einmal die gemeinsamen Datenregeln. Weitere Engines können denselben JSON-Vertrag verwenden; die Wiki verspricht damit kein fertiges Plugin.

1 · Einen Text aus der JSON anzeigen

Beginne mit „Welcome to the harbour. I am Mira.“. Ein Dialog enthält eine textRef, nicht unmittelbar einen String. Die Texttabelle enthält Segmente. Dieser JavaScript-Ausschnitt setzt bereits geladenes story.json in story voraus; die Engine-Seiten zeigen das Laden und die Ausgabe.

// story is the parsed story.json. This excerpt reads text-only messages.
const locale = 'en';
const textRef = 'fd7b61a3-8233-4ba7-8f77-b88b119a91bf';
const record = story.content.texts[textRef];
const translation = story.content.translations[locale]?.[textRef];
const message = translation ? translation.message : record.sourceMessage;
const text = message.segments.map(segment => {
  if (segment.kind !== 'text') throw new Error('This example needs text-only segments');
  return segment.text;
}).join('');
console.log(text);

Für den ersten Test ist die Text-ID fest angegeben. Leere Übersetzungen nicht mit „fehlt“ verwechseln: eine vorhandene, gültige leere Übersetzung darf leer bleiben.

2 · Gespräch beginnen, fortsetzen und beenden

Der Spieler spricht Mira an. Dein Spiel wählt den passenden Startpunkt aus story-index.json, öffnet die Dialogbox und löst dessen ersten Knoten auf. Verwende die Referenzen, niemals die Reihenfolge der JSON-Eigenschaften.

const entryRef = '5e6b2e12-1ac1-4c46-8221-1a051e87370a';
const entry = story.execution.entries[entryRef];
const node = story.execution.nodes[entry.nodeRef];
console.log(node.kind, node.text.textRef);
// Read this dialogue's text; wait for the player's Continue action.
// node.continuation describes the next step, not the next array element.

Nach „Weiter“ folgt das Spiel der continuation. kind: node verweist über nodeRef auf den nächsten Knoten; kind: end beendet das Gespräch. Ein eigener Knoten mit kind: end bedeutet dasselbe. Dann schließt das Spiel die Dialogbox und gibt die Steuerung frei. VNLE beendet weder das Programm noch automatisch das Level.

{
  "continuation": {
    "kind": "node",
    "nodeRef": "7c804744-b002-4136-9a7c-703fcf2d0880"
  },
  "flowRef": "1f69a7b6-1bdc-4dff-b8ee-9b2819b35168",
  "id": "d789af84-d51d-4b97-b564-86a6756e2f2e",
  "kind": "dialogue",
  "speakerRef": "807b2957-7bab-4a28-83a4-1f3d1589bbc2",
  "text": {
    "bindings": {},
    "textRef": "cbe1a637-991f-4857-81d1-6cc053d68927"
  }
}

In diesem Ausschnitt führt „Maybe later“ zum End-Knoten. Die Begrüßung des Beispielprojekts führt dagegen zuerst zu Call Substory. Ein einfaches Dialog/End-Skript muss dort verständlich stoppen, bis Call/Return unterstützt wird.

3 · Antworten anbieten

Mira bietet „Schlüssel kaufen (5 Gold)“ und „Vielleicht später“ an. optionOrder bestimmt die Reihenfolge. options enthält die Datensätze. Ein Button merkt sich die Options-ID. Nach dem Klick werden nur die Effekte und die Fortsetzung dieser Antwort verwendet.

// Read this exact choice from the Harbour story. No condition on these two options.
const choice = story.execution.nodes['42edfff8-b8fe-46bf-ae0d-9e2347b6e726'];
for (const optionId of choice.optionOrder) {
  const option = choice.options[optionId];
  console.log(optionId, option.text.textRef, option.continuation);
}
// Each answer button must retain its optionId.
// On click: check availability, apply that option's effects once,
// then follow that option's continuation.

Eine vorhandene option.condition entscheidet, ob die Antwort angeboten wird. Der Harbour-Export hat an dieser Stelle zwei Antworten ohne eigene Bedingung; die Goldprüfung liegt im folgenden branch-Knoten. Wenn keine Antwort verfügbar ist, gilt whenEmpty: entweder eine angegebene Fortsetzung oder ein erklärter Fehler.

4 · Bedingungen prüfen und Werte ändern

Zehn Goldstücke reichen für den Schlüssel; zwei reichen nicht. Die Bedingung steht bereits in der JSON. Das Spiel stellt den aktuellen Goldwert bereit. Dies ist ein Beispiel für genau den Vergleich gte; weitere Operatoren und verschachtelte Bedingungen stehen in der Ablaufreferenz.

// This excerpt evaluates the Harbour purchase check: Gold >= 5.
const branch = story.execution.nodes['c1c3c372-0a73-4cd0-851d-ede59d5709bb'];
const condition = branch.condition;
const gold = 10; // Supply the current value from your game's saved state.
const minimum = condition.right.value.value; // 5, from the exported typed literal.
const continuation = gold >= minimum ? branch.whenTrue : branch.whenFalse;
console.log(story.execution.nodes[continuation.nodeRef].kind);
// Repeat with gold = 2: the next node is a dialogue explaining the price.
// The full condition grammar is documented in Flow rules.
Die echten JSON-Felder der Bedingung und Änderung
{
  "condition": {
    "kind": "compare",
    "left": {
      "kind": "variable",
      "variableRef": "a547610e-661f-4b8d-a25b-21d042f1a2d5"
    },
    "operator": "gte",
    "right": {
      "kind": "literal",
      "value": {
        "type": "integer",
        "value": 5
      }
    }
  },
  "flowRef": "1f69a7b6-1bdc-4dff-b8ee-9b2819b35168",
  "id": "c1c3c372-0a73-4cd0-851d-ede59d5709bb",
  "kind": "branch",
  "whenFalse": {
    "kind": "node",
    "nodeRef": "eaffd4b0-397d-40b7-abc6-404a0a801e0e"
  },
  "whenTrue": {
    "kind": "node",
    "nodeRef": "242b2fd4-8101-4a7f-9306-17b789508bdc"
  }
}
{
  "continuation": {
    "kind": "node",
    "nodeRef": "500c28a1-ed85-4c36-9a38-bedacce1c6b7"
  },
  "effects": [
    {
      "kind": "subtract",
      "value": {
        "kind": "literal",
        "value": {
          "type": "integer",
          "value": 5
        }
      },
      "variableRef": "a547610e-661f-4b8d-a25b-21d042f1a2d5"
    },
    {
      "kind": "set",
      "value": {
        "kind": "literal",
        "value": {
          "type": "boolean",
          "value": true
        }
      },
      "variableRef": "76407a53-5ce1-408e-a8c6-a31c10f69ca5"
    }
  ],
  "flowRef": "1f69a7b6-1bdc-4dff-b8ee-9b2819b35168",
  "id": "242b2fd4-8101-4a7f-9306-17b789508bdc",
  "kind": "action"
}

Change Variable wird als action mit effects exportiert. Hier werden fünf Gold abgezogen und QuestAccepted auf true gesetzt. Initialisiere Variablen aus execution.variables nur beim neuen Spielkontext; beim Laden eines Spielstands verwendest du die gespeicherten Werte. Wenn Gold auch im Inventarsystem existiert, braucht das Spiel eine gemeinsame verlässliche Quelle und eine abgestimmte Aktualisierung.

5 · Die Sprache wechseln

Im vollständigen Export liegen die Übersetzungen in content.translations. Für diese Dateien musst du nicht pro Sprache eine weitere JSON laden. Wähle content.translations[locale][textRef].message, wenn der Eintrag vorhanden ist, sonst content.texts[textRef].sourceMessage. Der Sprachcode kommt aus den Spieleinstellungen.

Beim Sprachwechsel den aktuellen Dialog, Sprechernamen und die angebotenen Antworten erneut auflösen. Position, gewählte Antworten und Variablen bleiben erhalten. Keine Commands oder Variablenänderungen erneut ausführen. Für eine bereits angezeigte Zeile die beim Anzeigen festgehaltenen Platzhalterwerte wiederverwenden.

Platzhalter: „Du hast noch 5 Gold“ · Übersetzungen in VNLE pflegen

6 · Name, Dialog und Bild in einer Dialogleiste

  1. Dialogtext: node.text.textRef auflösen.
  2. Sprecher: content.characters[node.speakerRef] nachschlagen und dessen nameTextRef übersetzen. Ohne speakerRef kann die Zeile ein Erzählertext ohne Figur sein.
  3. Porträt im vollständigen Export: zuerst das Bild der angegebenen Emotion verwenden, ansonsten defaultPortraitRef. Dann content.assets[assetRef].relativePath relativ zum Ordner von story.json laden. Die JSON enthält den Pfad, nicht die PNG-Bilddaten.
  4. Antworten: je verfügbare Options-ID einen Button erstellen. Bei einem normalen Dialog nur Weiter anbieten.
  5. Bei Ende die gesamte Dialogleiste schließen, Antwortbuttons deaktivieren und gegebenenfalls die Spielersteuerung wiederherstellen.

Wenn du eigene Engine-Bilder verwendest oder dein Export keine Bildzuordnung enthält, lege eine Zuordnung im Spiel an. Charakter-ID → Bildressource bleibt auch beim Umbenennen und Übersetzen stabil. Bei Emotionen zusätzlich die Emotions-ID verwenden.

// The game owns this mapping. These are real Harbour character IDs.
const portraits = {
  '807b2957-7bab-4a28-83a4-1f3d1589bbc2': 'assets/portraits/mira.png'
};
const node = story.execution.nodes['ffa7141b-97ed-44c1-a346-8bba7e26d94f'];
const file = portraits[node.speakerRef];
// Load file using your engine's image loader; show a placeholder if absent.
// Optional emotion variants: portraits[characterId][emotionId].
// Do not index by the translated character name.

Die Engine-Seiten zeigen diese Zuordnung mit importierten Texturen, Sprites oder Atlasbildern. Es gibt keine sprachabhängige Bildauswahl im VNLE-Export. Bei fehlendem Bild das vorige Porträt entfernen und optional ein Ersatzbild anzeigen.

Erweiterte Abläufe nach Bedarf

Jump · Call Substory / Return · Command · Spielstand und Fehlerfälle

Der Narrative Artist legt den Ablauf fest. Der Entwickler unterstützt die Knotentypen einmal und verbindet die vereinbarten Commands mit dem Spiel. So folgen auch später geänderte Geschichten ihren exportierten Verbindungen.