VNLEWIKIBuild stories. Connect worlds.Deutsch
VNLE Handbook

Unity

← Bring content into your game

Unity 6 · C# · Newtonsoft Json

Start with one visible sentence. This small example reads the real Harbour export directly; it is not a complete dialogue player. Then connect the data to your game UI.

1 · Prepare files and scene

Import story.json into Assets/Story. In Package Manager install Unity package com.unity.nuget.newtonsoft-json. Create Canvas → Text - TextMeshPro and import TMP Essentials if prompted. Attach FirstText.cs to a GameObject. Assign the JSON TextAsset and text object to storyFile and dialogueText in the Inspector. Press Play.

story.json · Export files explained

2 · Show the first text

using System;
using Newtonsoft.Json.Linq;
using TMPro;
using UnityEngine;

public class FirstText : MonoBehaviour
{
    [SerializeField] private TextAsset storyFile;
    [SerializeField] private TMP_Text dialogueText;
    private JObject story;

    private void Start()
    {
        try { story = JObject.Parse(storyFile.text); ShowLanguage("en"); }
        catch (Exception error) { dialogueText.text = "Cannot read story.json"; Debug.LogException(error); }
    }

    public void ShowLanguage(string locale)
    {
        const string id = "fd7b61a3-8233-4ba7-8f77-b88b119a91bf";
        var content = story["content"];
        var translation = content["translations"]?[locale]?[id];
        var message = translation?["message"] ?? content["texts"][id]["sourceMessage"];
        string line = "";
        foreach (var segment in message["segments"])
        {
            if ((string)segment["kind"] != "text") throw new Exception("Text-only example");
            line += (string)segment["text"];
        }
        dialogueText.text = line;
    }
}

Expected: “Welcome to the harbour. I am Mira.” With locale = "de" or the language parameter de: “Willkommen im Hafen. Ich bin Mira.” A missing language uses source text. This sample supports text segments; it deliberately does not silently discard placeholders.

3 · From one sentence to a conversation

The fixed text ID above is for the first test. In your game choose flowRef/entryRef from story-index.json, read execution.entries[entryRef].nodeRef, then execution.nodes[nodeRef]. The current dialogue supplies text.textRef and speakerRef. Continuations depend on node kind.

Name/dialogue: TMP_Text.text. Answers: Button.onClick; retain each option ID in a separate local variable. End: dialogPanel.SetActive(false). A button can call ShowLanguage with the string de without resetting progress.

  1. Continue and End: follow connections, close the text box
  2. Choices: answer ID, order and selected continuation
  3. Conditions and variables: ten or two gold
  4. Language: the same IDs, newly resolved content
  5. Command: give an item or open a shop
  6. Call/Return: visit a substory and return
Inspect answers and the gold check in this language

Read the data for a choice

Insert this fragment where story is available after loading (for RPG Maker: const story = $gameTemp.vnleExample.story;). It prints IDs for inspection. Replace the output with your answer buttons. It covers the two Harbour answers and their following gold check, not a general interpreter.

// Inside a method after story (JObject) has been loaded.
var choice = story["execution"]["nodes"]["42edfff8-b8fe-46bf-ae0d-9e2347b6e726"];
foreach (var optionIdToken in choice["optionOrder"])
{
    string optionId = (string)optionIdToken;
    var option = choice["options"][optionId];
    Debug.Log(optionId + " " + (string)option["text"]["textRef"]);
    // Resolve the text and retain optionId in your Button.onClick listener.
}
var branch = story["execution"]["nodes"]["c1c3c372-0a73-4cd0-851d-ede59d5709bb"];
int gold = 10; // Repeat with 2.
var next = gold >= (int)branch["condition"]["right"]["value"]["value"]
    ? branch["whenTrue"] : branch["whenFalse"];
Debug.Log((string)next["nodeRef"]);

With 10 gold, next refers to “Pay five gold”; with 2 it refers to “Not enough gold”. In the actual game, run the check only when the selected path reaches this node. Each selected answer supplies its own continuation.

4 · Map your own images

This supplementary excerpt expects a current dialogue object named node and the UI elements named in its comments. For C++ and MonoGame, speakerRef has already been read as SpeakerId or speakerId. The key is Mira’s actual character ID; the image name and location belong to your game. Insert the lines at the indicated locations.

// Add inside your own MonoBehaviour. Assign a Sprite and a UI Image in Inspector.
[SerializeField] private Sprite miraPortrait;
[SerializeField] private UnityEngine.UI.Image portrait;
// node is the current JObject dialogue:
portrait.sprite = (string)node["speakerRef"] == "807b2957-7bab-4a28-83a4-1f3d1589bbc2" ? miraPortrait : null;
portrait.enabled = portrait.sprite != null;

Resolve speaker names through content.characters[speakerRef].nameTextRef just like dialogue text. Missing mappings should hide the previous portrait or show a placeholder. Images are not selected by language.

Use image references from the complete export

5 · Ship files with the game

The referenced TextAsset is included with the scene. ID tables are JSON objects with dynamic keys; JsonUtility is not a direct replacement for JObject here. This example does not require StreamingAssets file access.

Check your result

  1. English and German greetings display correctly; an unavailable language falls back to English.
  2. When adding flow: try both answers, gold 10/2, the return from the substory and End.
  3. Run the built game outside your project folder. JSON, fonts and portraits must be available there too.

Official documentation

API and setup references for this approach: