Интеграция контента в игру
VNLE экспортирует тексты, переводы и созданные автором переходы. Игра читает эти данные, показывает окно диалога и связывает игровые действия со своими системами. Используйте собственные скрипты или интеграцию сообщества. Примеры объясняют данные без обязательного специального обработчика VNLE.
Файлы, JSON и точки входа
Экспорт → Файлы и JSON → Посмотрите правила переходов
Все примеры используют реальный экспорт «Ключа от гавани». Длинные ID принадлежат этому проекту. Замените их ID из своего экспорта: имена и переводы не являются ключами ссылок.
story.json · story-index.json · Редактируемый проект примера
Выберите движок
Каждая страница объясняет загрузку и отображение в соответствующем движке. Ниже общие правила работы с данными разобраны один раз. Другие движки могут использовать тот же контракт JSON, но это не означает наличия готового плагина.
1 · Покажите один текст из JSON
Начните с «Welcome to the harbour. I am Mira.». Диалог хранит textRef, а не строку напрямую. Таблица текстов содержит сегменты. Этот фрагмент JavaScript предполагает, что story.json уже разобран в объект story; загрузка и вывод описаны на страницах движков.
// 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);Первый тест использует фиксированный ID текста. Не путайте пустой перевод с отсутствующим: существующий допустимый пустой перевод может оставаться пустым.
2 · Начните, продолжите и завершите разговор
Игрок обращается к Мире. Игра выбирает нужную точку входа из story-index.json, открывает окно диалога и получает первый узел. Следуйте ссылкам, а не порядку свойств JSON.
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.После Continue следуйте continuation. kind: node использует nodeRef для перехода к следующему узлу, kind: end завершает разговор. Узел с kind = end даёт тот же результат. Затем игра закрывает окно диалога и восстанавливает управление. VNLE не закрывает приложение и не завершает уровень автоматически.
{
"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"
}
}В этом фрагменте Maybe later ведёт к End. Приветствие из примера сначала ведёт к Call Substory. Простой скрипт, поддерживающий только диалог и завершение, должен остановиться здесь с пояснением, пока не реализованы Call/Return.
3 · Предложите ответы
Мира предлагает Buy the key (5 gold) и Maybe later. optionOrder задаёт порядок, options хранит записи вариантов. Каждая кнопка сохраняет ID варианта. При нажатии применяйте только effects и continuation выбранного ответа.
// 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.Если задано option.condition, оно определяет доступность ответа. В экспорте гавани здесь два ответа без собственных условий; проверка золота находится в следующем узле branch. Если ответов нет, следуйте whenEmpty: указанному продолжению или понятной ошибке.
4 · Проверка условий и изменение значений
За десять золотых ключ купить можно, за две монеты — нет. Условие уже есть в JSON, игра предоставляет текущее количество золота. Фрагмент показывает сравнение gte; остальные операторы и вложенные условия описаны в справочнике переходов.
// 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.Реальные поля JSON для условия и изменения
{
"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 экспортируется как action с effects. Здесь оно вычитает пять золотых и устанавливает QuestAccepted в true. Инициализируйте переменные из execution.variables только для нового состояния игры; при загрузке восстанавливайте сохранённые значения. Если золото хранится и в инвентаре, определите единый источник достоверного значения и согласуйте обновления.
5 · Смените язык
Полный экспорт хранит переводы в content.translations. Отдельный JSON для каждого языка не нужен. Используйте content.translations[locale][textRef].message, если он есть, иначе content.texts[textRef].sourceMessage. Получайте locale из настроек игры.
При смене языка заново получите текущую реплику, имя персонажа и доступные ответы. Сохраните позицию, выбранные ответы и переменные. Не выполняйте команды или изменения переменных повторно. Используйте значения подстановочных полей, зафиксированные при первом показе текущей реплики.
Подстановочные поля: «У вас осталось 5 золотых» · Управляйте переводами в VNLE
6 · Имя, реплика и портрет в одной панели диалога
- Текст диалога: получите node.text.textRef.
- Говорящий персонаж: найдите content.characters[node.speakerRef] и переведите nameTextRef. Если speakerRef отсутствует, реплика может быть повествованием без персонажа.
- Портрет в полном экспорте: используйте портрет выбранной эмоции, иначе defaultPortraitRef. Загрузите content.assets[assetRef].relativePath относительно папки с story.json. JSON хранит путь, а не байты изображения PNG.
- Ответы: создайте кнопку для каждого доступного ID варианта. Для обычного диалога предложите Continue.
- При End закройте всю панель диалога, отключите кнопки ответов и при необходимости верните управление игроку.
Если используются собственные изображения движка или экспорт не содержит соответствий портретов, задайте их в игре. Связь ID персонажа → ресурс изображения не меняется при переименовании и переводе. Для эмоций добавьте ID эмоции.
// 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.Страницы движков показывают эти соответствия на импортированных текстурах, спрайтах или изображениях атласа. Экспорт VNLE не выбирает картинки по языку. Если изображение отсутствует, уберите прежний портрет и при желании покажите заглушку.
Более сложные переходы при необходимости
Переход · Вызов подистории / Возврат · Команда · Сохранения и обработка ошибок
Автор истории задаёт переходы. Разработчик один раз реализует поведение узлов и подключает согласованные команды к игре. После этого изменения истории выполняются по экспортированным связям.