VNLEWIKICrea historias. Conecta mundos.
Español
Manual de VNLE

Integra el contenido en tu juego

VNLE exporta los textos, las traducciones y el recorrido que has creado. Tu juego lee esos datos, muestra la caja de diálogo y conecta las acciones a sus propios sistemas. Usa tus scripts o una integración de la comunidad. Estos ejemplos explican los datos sin exigir un gestor de VNLE.

Archivos, JSON y puntos de entrada

ExportaciónEntiende los archivos y JSONConsultar las reglas del flujo

Todos los ejemplos usan la exportación real de The Harbour Key. Sus identificadores largos pertenecen a ese proyecto. Sustitúyelos por los de tu exportación; los nombres y los textos traducidos no son claves de referencia.

story.json · story-index.json · Proyecto de ejemplo editable

Elige tu motor

Cada página explica la carga y la visualización en su motor. El recorrido siguiente presenta las reglas de datos comunes. Otros motores pueden utilizar el mismo contrato JSON; eso no significa que exista un plugin listo para usar.

1 · Mostrar un texto de JSON

Empieza con «Welcome to the harbour. I am Mira.». Un diálogo contiene un textRef, no una cadena de texto directamente. La tabla de textos contiene segmentos. Este fragmento JavaScript supone que story.json ya se ha leído en story; las páginas de los motores explican la carga y la visualización.

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

La primera prueba utiliza un identificador de texto fijo. No confundas una traducción vacía con una ausente: una traducción vacía que existe y es válida puede permanecer vacía.

2 · Iniciar, continuar y terminar una conversación

El jugador habla con Mira. Tu juego elige la entrada correspondiente en story-index.json, abre su caja de diálogo y resuelve el primer nodo de la entrada. Sigue las referencias, nunca el orden de las propiedades del 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.

Después de Continue, sigue continuation. kind: node utiliza nodeRef para identificar el siguiente nodo; kind: end termina la conversación. Un nodo cuyo kind es end tiene el mismo efecto. El juego cierra la caja de diálogo y devuelve los controles. VNLE no cierra la aplicación ni termina automáticamente un nivel.

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

En este fragmento, «Maybe later» lleva al nodo End. El saludo del ejemplo lleva primero a Call Substory. Un script básico de diálogo y final debe detenerse ahí con una explicación hasta que admita Call/Return.

3 · Ofrecer respuestas

Mira ofrece «Buy the key (5 gold)» y «Maybe later». optionOrder define su orden; options contiene los registros. Cada botón conserva el identificador de su opción. Al hacer clic, aplica solo los effects y la continuation de la respuesta elegida.

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

Si existe option.condition, determina si se ofrece la respuesta. Aquí, la exportación del puerto tiene dos respuestas sin condiciones propias; la comprobación del oro está en el nodo branch siguiente. Si no hay respuestas disponibles, sigue whenEmpty: una continuación definida o un error explicado.

4 · Comprobar condiciones y modificar valores

Con diez monedas de oro se puede comprar la llave; con dos, no. La condición ya está en el JSON. El juego proporciona la cantidad de oro actual. Este fragmento demuestra la comparación gte; los demás operadores y las condiciones anidadas están en la referencia del flujo.

// 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 JSON reales para la condición y el cambio
{
  "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 se exporta como una action con effects. Aquí resta cinco monedas de oro y establece QuestAccepted en true. Inicializa las variables desde execution.variables solo para un nuevo contexto de juego; cargar una partida restaura los valores guardados. Si el oro también existe en el inventario, define una única fuente de verdad y coordina las actualizaciones.

5 · Cambiar idioma

La exportación completa guarda las traducciones en content.translations. No requiere cargar un JSON distinto para cada idioma. Usa content.translations[locale][textRef].message si existe; de lo contrario, content.texts[textRef].sourceMessage. Obtén locale de los ajustes del juego.

Al cambiar de idioma, resuelve de nuevo el diálogo actual, el nombre del personaje y las respuestas ofrecidas. Conserva la posición, las respuestas elegidas y las variables. No repitas comandos ni cambios de variables. Reutiliza los valores de los marcadores de posición capturados al mostrar la frase actual.

Marcadores de posición: «Te quedan 5 monedas de oro» · Mantener las traducciones en VNLE

6 · Nombre, diálogo y retrato en una barra de diálogo

  1. Texto del diálogo: resuelve node.text.textRef.
  2. Personaje que habla: busca content.characters[node.speakerRef] y traduce nameTextRef. Sin speakerRef, la frase puede ser una narración sin personaje.
  3. Retrato en la exportación completa: usa el de la emoción solicitada si existe; si no, defaultPortraitRef. Carga content.assets[assetRef].relativePath desde la carpeta que contiene story.json. El JSON guarda la ruta, no los bytes de la imagen PNG.
  4. Respuestas: crea un botón por identificador de opción disponible. Para un diálogo normal, ofrece Continue.
  5. En End, cierra toda la barra de diálogo, desactiva los botones de respuesta y devuelve los controles al jugador si es necesario.

Si usas tus propias imágenes del motor o la exportación no tiene una asociación de retratos, defínela en el juego. La relación identificador de personaje → recurso de imagen permanece estable aunque cambie el nombre o la traducción. Para las emociones, incluye su identificador.

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

Las páginas de los motores muestran estas asociaciones con texturas, sprites o imágenes de atlas importados. La exportación de VNLE no selecciona imágenes según el idioma. Si falta una imagen, retira el retrato anterior y, si quieres, muestra una imagen de sustitución.

Flujo avanzado cuando se necesita

Salto · Llamar subhistoria / Volver · Comando · Partidas guardadas y casos de error

Quien escribe la historia crea el recorrido. El desarrollador implementa una vez el comportamiento de los nodos y conecta los comandos acordados al juego. Los cambios posteriores de la historia siguen sus conexiones exportadas.