VNLEWIKIСоздавайте истории. Соединяйте миры.
Русский
Руководство VNLE

Unity

← Интеграция контента в игру

Unity 6 · C# · Newtonsoft Json

Начните с вывода одной фразы. Этот небольшой пример напрямую читает реальные данные экспорта гавани, но ещё не является полноценной системой диалогов. Затем подключите данные к интерфейсу своей игры.

1 · Подготовка файлов и сцены

Импортируйте story.json в Assets/Story. В Package Manager установите пакет Unity com.unity.nuget.newtonsoft-json. Создайте Canvas → Text - TextMeshPro и при запросе импортируйте TMP Essentials. Добавьте FirstText.cs к GameObject. В Inspector назначьте JSON-ресурс TextAsset полю storyFile, а текстовый объект — dialogueText. Нажмите Play.

story.json · Назначение файлов экспорта

2 · Выведите первый текст

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;
    }
}

Ожидаемый текст: «Welcome to the harbour. I am Mira.» При locale = "de" или параметре языка de: «Willkommen im Hafen. Ich bin Mira.» Если язык отсутствует, используется оригинал. Этот пример поддерживает текстовые сегменты и не отбрасывает подстановочные поля без предупреждения.

3 · От одной фразы к разговору

Фиксированный ID текста выше нужен только для первого теста. В игре выберите flowRef/entryRef из story-index.json, прочитайте execution.entries[entryRef].nodeRef, затем execution.nodes[nodeRef]. Текущий диалог содержит text.textRef и speakerRef. Дальнейший путь зависит от типа узла.

Имя и реплика: TMP_Text.text. Ответы: Button.onClick; сохраняйте ID каждого варианта в отдельной локальной переменной. Завершение: dialogPanel.SetActive(false). Кнопка может вызвать ShowLanguage со строкой de, не сбрасывая ход разговора.

  1. Continue и End: переходы по связям и закрытие окна диалога
  2. Выбор: ID ответа, порядок и выбранное продолжение
  3. Условия и переменные: десять или две золотые монеты
  4. Язык: те же ID, заново полученный текст
  5. Command: выдать предмет или открыть магазин
  6. Call/Return: перейти в подысторию и вернуться
Разберите ответы и проверку золота на этом языке программирования

Прочитайте данные узла выбора

Вставьте фрагмент там, где после загрузки доступен story (для RPG Maker: const story = $gameTemp.vnleExample.story;). Он выводит ID для проверки; замените этот вывод кнопками ответов. Фрагмент охватывает два ответа примера и следующую за ними проверку золота, а не реализует универсальный интерпретатор.

// 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"]);

При 10 золотых next указывает на Pay five gold, при 2 — на Not enough gold. В игре выполняйте проверку только тогда, когда выбранный путь дошёл до этого узла. Каждый ответ имеет собственное продолжение.

4 · Настройте соответствия для своих изображений

Этот дополнительный фрагмент ожидает текущий объект диалога с именем node и элементы интерфейса, указанные в комментариях. В C++ и MonoGame speakerRef уже прочитан как SpeakerId или speakerId. Ключ — реальный ID персонажа Миры, а имя и расположение изображения определяются вашей игрой. Вставьте строки в указанные места.

// 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;

Получайте имена персонажей через content.characters[speakerRef].nameTextRef так же, как текст диалога. Если соответствие изображения отсутствует, скройте предыдущий портрет или покажите заглушку. Изображения не выбираются по языку.

Используйте ссылки на изображения из полного экспорта

5 · Включите файлы в сборку игры

TextAsset, на который ссылается сцена, включается вместе с ней. Таблицы ID — это JSON-объекты с динамическими ключами; здесь JsonUtility не заменяет JObject напрямую. Доступ к файлам StreamingAssets в этом примере не нужен.

Проверьте свой результат

  1. Приветствия на английском и немецком отображаются правильно; при выборе недоступного языка используется английский.
  2. Добавив выполнение сюжета, проверьте оба ответа, 10 и 2 золотых, возврат из подыстории и End.
  3. Запустите собранную игру за пределами папки проекта. JSON, шрифты и портреты должны быть доступны и там.

Официальная документация

Документация по API и настройке для этого подхода: