VNLEWIKITwórz historie. Łącz światy.
Polski
Podręcznik VNLE

Połącz treść ze swoją grą

VNLE eksportuje teksty, tłumaczenia i utworzony przebieg historii. Gra odczytuje te dane, wyświetla okno dialogu i łączy akcje gry ze swoimi systemami. Możesz użyć własnych skryptów lub integracji społeczności. Przykłady wyjaśniają dane bez konieczności korzystania z gotowego modułu obsługi VNLE.

Pliki, JSON i punkty wejścia

EksportPliki i JSONSprawdź reguły przebiegu

Wszystkie przykłady korzystają z rzeczywistego eksportu The Harbour Key. Długie identyfikatory należą do tego projektu. Zastąp je ID ze swojego eksportu; nazwy i przetłumaczony tekst nie są kluczami odwołań.

story.json · story-index.json · Edytowalny projekt przykładowy

Wybierz silnik

Każda strona wyjaśnia wczytywanie i wyświetlanie danych w danym silniku. Poniższa ścieżka nauki opisuje wspólne reguły danych. Inne silniki mogą korzystać z tego samego kontraktu JSON; nie oznacza to dostępności gotowej wtyczki.

1 · Wyświetl jeden tekst z JSON

Zacznij od „Welcome to the harbour. I am Mira.” Dialog zawiera textRef, a nie sam tekst. Tabela tekstów zawiera segmenty. Ten fragment JavaScript zakłada, że story.json został już odczytany do obiektu story; wczytywanie i wyświetlanie opisano na stronach silników.

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

Pierwszy test używa stałego ID tekstu. Nie myl pustego tłumaczenia z brakującym: istniejące, poprawne puste tłumaczenie może pozostać puste.

2 · Rozpocznij, kontynuuj i zakończ rozmowę

Gracz rozmawia z Mirą. Gra wybiera właściwy punkt wejścia ze story-index.json, otwiera okno dialogu i odczytuje pierwszy węzeł tego punktu. Podążaj za odwołaniami, nigdy za kolejnością właściwości 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.

Po Continue wykonaj continuation. kind: node wskazuje kolejny węzeł przez nodeRef; kind: end kończy rozmowę. Węzeł z kind równym end działa tak samo. Gra zamyka wtedy okno dialogu i przywraca sterowanie. VNLE nie zamyka aplikacji ani automatycznie nie kończy poziomu.

{
  "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"
  }
}

W tym fragmencie „Maybe later” prowadzi do węzła End. Powitanie z przykładu najpierw prowadzi do Call Substory. Prosty skrypt obsługujący tylko dialog i zakończenie musi tam zatrzymać się z wyjaśnieniem, dopóki nie dodasz obsługi Call/Return.

3 · Wyświetl odpowiedzi

Mira oferuje „Buy the key (5 gold)” i „Maybe later”. optionOrder określa kolejność, a options zawiera wpisy. Każdy przycisk przechowuje ID swojej opcji. Po kliknięciu wykonaj tylko efekty i dalszą ścieżkę tej odpowiedzi.

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

Jeśli opcja ma option.condition, warunek określa jej dostępność. Eksport Harbour zawiera tutaj dwie odpowiedzi bez własnych warunków; ilość złota sprawdza następny węzeł branch. Gdy żadna odpowiedź nie jest dostępna, zastosuj whenEmpty: wskazaną kontynuację albo błąd z objaśnieniem.

4 · Sprawdź warunki i zmień wartości

Przy 10 sztukach złota można kupić klucz, przy 2 — nie. Warunek jest już zapisany w JSON. Gra dostarcza bieżącą ilość złota. Fragment pokazuje porównanie gte; inne operatory i zagnieżdżone warunki opisano w regułach przebiegu.

// 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.
Rzeczywiste pola JSON dla warunku i zmiany wartości
{
  "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 jest eksportowane jako action z effects. Tutaj odejmuje pięć sztuk złota i ustawia QuestAccepted na true. Inicjalizuj zmienne z execution.variables tylko przy tworzeniu nowego stanu gry; wczytanie zapisu przywraca zapisane wartości. Jeśli złoto występuje także w systemie ekwipunku, ustal jedno nadrzędne źródło danych i skoordynuj aktualizacje.

5 · Zmień język

Pełny eksport przechowuje tłumaczenia w content.translations. Nie trzeba wczytywać osobnego JSON dla każdego języka. Użyj content.translations[locale][textRef].message, jeśli istnieje, lub content.texts[textRef].sourceMessage. Kod locale pobierz z ustawień gry.

Po zmianie języka ponownie odczytaj bieżący dialog, imię postaci i dostępne odpowiedzi. Zachowaj pozycję, wybrane odpowiedzi i zmienne. Nie wykonuj ponownie poleceń ani zmian zmiennych. Użyj wartości podstawianych zapamiętanych przy pierwszym wyświetleniu bieżącej wypowiedzi.

Podstawiane wartości: „Zostało ci 5 sztuk złota” · Zarządzanie tłumaczeniami w VNLE

6 · Imię, dialog i portret w jednym oknie

  1. Tekst dialogu: odczytaj node.text.textRef.
  2. Mówiąca postać: znajdź content.characters[node.speakerRef] i odczytaj tłumaczenie nameTextRef. Bez speakerRef wypowiedź może być narracją bez przypisanej postaci.
  3. Portret w pełnym eksporcie: użyj portretu wybranej emocji, a w razie jego braku — defaultPortraitRef. Wczytaj content.assets[assetRef].relativePath względem folderu ze story.json. JSON zawiera ścieżkę, nie bajty obrazu PNG.
  4. Odpowiedzi: utwórz przycisk dla każdego dostępnego ID opcji. Przy zwykłym dialogu pokaż Continue.
  5. Przy End zamknij całe okno dialogu, wyłącz przyciski odpowiedzi i w razie potrzeby przywróć sterowanie grą.

Jeśli używasz własnych obrazów silnika albo eksport nie zawiera przypisań portretów, zdefiniuj je w grze. Powiązanie ID postaci → zasób obrazu pozostaje stałe po zmianie nazwy i tłumaczeniu. Dla emocji uwzględnij także ID emocji.

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

Instrukcje dla silników pokazują takie przypisanie z użyciem importowanych tekstur, sprite’ów i atlasów. Eksport VNLE nie wybiera obrazów zależnie od języka. Gdy brakuje obrazu, usuń poprzedni portret i opcjonalnie pokaż obraz zastępczy.

Bardziej złożony przebieg, gdy go potrzebujesz

Jump · Call Substory / Return · Polecenie · Zapisy gry i obsługa błędów

Autor historii tworzy jej przebieg. Osoba programująca implementuje działanie typów węzłów raz i łączy uzgodnione polecenia z grą. Późniejsze zmiany historii korzystają już z wyeksportowanych połączeń.