VNLEWIKICrie histórias. Conecte mundos.
Português (Brasil)
Manual do VNLE

Integre o conteúdo ao seu jogo

O VNLE exporta textos, traduções e o fluxo que você criou. Seu jogo lê esses dados, exibe a caixa de diálogo e conecta as ações aos próprios sistemas. Use seus scripts ou uma integração da comunidade. Estes exemplos explicam os dados sem exigir um componente de execução específico do VNLE.

Arquivos, JSON e pontos de entrada

ExportarEntenda os arquivos e o JSONConsulte as regras de fluxo

Todos os exemplos usam a exportação real de A chave do porto. Os IDs longos pertencem a esse projeto. Substitua-os pelos IDs da sua exportação; nomes e textos traduzidos não são chaves de referência.

story.json · story-index.json · Projeto de exemplo editável

Escolha sua engine

Cada página explica como carregar e exibir o conteúdo na engine correspondente. O passo a passo abaixo apresenta as regras de dados comuns. Outras engines podem usar o mesmo contrato JSON; isso não significa que exista um plugin pronto.

1 · Exiba um texto do JSON

Comece com “Welcome to the harbour. I am Mira.” Um diálogo guarda uma textRef, não uma string diretamente. A tabela de textos contém segmentos. Este trecho JavaScript pressupõe que story.json já foi lido para o objeto story; as páginas das engines mostram como carregar e exibir os dados.

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

O primeiro teste usa um ID de texto fixo. Não confunda uma tradução vazia com uma ausente: uma tradução vazia, presente e válida pode permanecer vazia.

2 · Inicie, avance e encerre uma conversa

O jogador fala com Mira. Seu jogo escolhe o ponto de entrada correspondente em story-index.json, abre a caixa de diálogo e resolve o primeiro nó. Siga as referências, nunca a ordem das propriedades do 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.

Após Continue, siga continuation. kind: node usa nodeRef para identificar o próximo nó; kind: end encerra a conversa. Um nó cujo kind é end tem o mesmo efeito. O jogo então fecha a caixa de diálogo e restaura os controles. O VNLE não fecha o aplicativo nem encerra automaticamente uma fase.

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

Neste trecho, Maybe later leva ao nó End. Já a saudação do exemplo leva primeiro a Call Substory. Um script básico que só aceite diálogo e encerramento deve parar ali e explicar o motivo, até que Call/Return esteja implementado.

3 · Ofereça respostas

Mira oferece Buy the key (5 gold) e Maybe later. optionOrder define a ordem; options contém os registros. Cada botão preserva o ID da opção. Ao clicar, aplique apenas os effects e a continuation daquela resposta.

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

Se existir, option.condition determina se a resposta será oferecida. A exportação do porto tem duas respostas sem condições próprias neste ponto; a verificação de ouro fica no nó branch seguinte. Se nenhuma resposta estiver disponível, siga whenEmpty: uma continuação definida ou um erro explicado.

4 · Verifique as condições e mude os valores

Dez moedas de ouro compram a chave; duas não. A condição já está no JSON, e o jogo fornece o valor atual de ouro. Este trecho mostra a comparação gte; os demais operadores e as condições aninhadas estão na referência de fluxo.

// 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.
Campos reais do JSON para a condição e a alteração
{
  "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 é exportado como action com effects. Aqui, subtrai cinco moedas de ouro e define QuestAccepted como true. Inicialize as variáveis de execution.variables apenas para um novo estado de jogo; ao carregar um save, restaure os valores salvos. Se o inventário também controla o ouro, defina uma única fonte de referência e coordene a atualização.

5 · Troque o idioma

A exportação completa guarda as traduções em content.translations, sem exigir um JSON separado para cada idioma. Use content.translations[locale][textRef].message quando existir; caso contrário, content.texts[textRef].sourceMessage. Obtenha locale nas configurações do jogo.

Ao trocar o idioma, resolva novamente o diálogo atual, o nome de quem fala e as respostas disponíveis. Preserve a posição, as respostas escolhidas e as variáveis. Não execute comandos ou alterações de variáveis de novo. Reutilize os valores dos placeholders capturados quando a fala atual foi apresentada.

Placeholders: “Você tem 5 moedas de ouro restantes” · Mantenha as traduções no VNLE

6 · Nome, diálogo e retrato na mesma barra

  1. Texto do diálogo: resolva node.text.textRef.
  2. Personagem que fala: procure content.characters[node.speakerRef] e traduza nameTextRef. Sem speakerRef, a fala pode ser uma narração sem personagem.
  3. Retrato na exportação completa: prefira o da expressão solicitada; caso contrário, use defaultPortraitRef. Carregue content.assets[assetRef].relativePath a partir da pasta de story.json. O JSON contém o caminho, não os bytes da imagem PNG.
  4. Respostas: crie um botão para cada ID de opção disponível. Em um diálogo comum, ofereça Continue.
  5. Em End, feche toda a barra de diálogo, desative os botões de resposta e restaure os controles do jogador, se necessário.

Se usar imagens próprias da engine ou se a exportação não tiver mapeamento de retratos, defina-o no jogo. A associação ID do personagem → recurso de imagem continua estável após renomeações e traduções. Para expressões, inclua o ID da expressão.

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

Os guias das engines demonstram esse mapeamento com texturas, sprites ou imagens de atlas importadas. A exportação do VNLE não seleciona imagens por idioma. Quando faltar uma imagem, remova o retrato anterior e, se quiser, mostre uma imagem substituta.

Fluxos avançados, quando necessário

Salto · Chamar sub-história / Retornar · Comando · Saves e situações de erro

Quem cria a narrativa define o fluxo. O desenvolvedor implementa uma vez o comportamento dos nós e conecta os comandos combinados ao jogo. Depois, as alterações da história passam a seguir as conexões exportadas.