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

MonoGame

← Integra el contenido en tu juego

MonoGame 3.8 · DesktopGL · C# / .NET

Empieza mostrando una frase. Este pequeño ejemplo lee directamente la exportación real del puerto; no es un reproductor completo de diálogos. Después, conecta los datos a la interfaz de tu juego.

1 · Preparar archivos y escena

Empieza con un proyecto MonoGame DesktopGL. Coloca story.json en Content/ y cópialo sin procesar al directorio de salida (consulta la configuración del proyecto más abajo). Añade ReadWelcome a Game1. En Content Pipeline Tool, crea un SpriteFont llamado DialogueFont, compila el contenido y cárgalo con Content.Load("DialogueFont").

story.json · Explicación de los archivos exportados

2 · Mostrar el primer texto

<ItemGroup>
  <None Update="Content/story.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

Añade los campos SpriteFont _dialogueFont; y string _dialogueLine = ""; a Game1. En LoadContent, usa _dialogueFont = Content.Load("DialogueFont"); _dialogueLine = ReadWelcome("en");. En Draw, dentro del bloque SpriteBatch existente, usa _spriteBatch.DrawString(_dialogueFont, _dialogueLine, new Vector2(24, 24), Color.White);. Si no existe un SpriteBatch, créalo en LoadContent con new SpriteBatch(GraphicsDevice) y utiliza Begin()/End() en Draw.

// Game1.cs: add using System.IO; using System.Text.Json; using System.Text;
// Place this method inside the existing Game1 class.
private string ReadWelcome(string locale)
{
    using var stream = TitleContainer.OpenStream("Content/story.json");
    using var document = JsonDocument.Parse(stream);
    var content = document.RootElement.GetProperty("content");
    const string id = "fd7b61a3-8233-4ba7-8f77-b88b119a91bf";
    var message = content.GetProperty("texts").GetProperty(id).GetProperty("sourceMessage");
    if (content.GetProperty("translations").TryGetProperty(locale, out var table)
        && table.TryGetProperty(id, out var translation))
        message = translation.GetProperty("message");
    var line = new StringBuilder();
    foreach (var segment in message.GetProperty("segments").EnumerateArray())
    {
        if (segment.GetProperty("kind").GetString() != "text")
            throw new InvalidDataException("Text-only example");
        line.Append(segment.GetProperty("text").GetString());
    }
    return line.ToString();
}

Resultado esperado: «Welcome to the harbour. I am Mira.». Con locale = "de" o el parámetro de idioma de: «Willkommen im Hafen. Ich bin Mira.». Si falta un idioma, se usa el texto original. Este ejemplo admite segmentos de texto; no descarta silenciosamente los marcadores de posición.

3 · De una frase a una conversación

El identificador de texto fijo anterior sirve para la primera prueba. En tu juego, elige flowRef/entryRef en story-index.json, lee execution.entries[entryRef].nodeRef y después execution.nodes[nodeRef]. El diálogo actual proporciona text.textRef y speakerRef. La continuación depende del tipo de nodo.

Nombre y diálogo: SpriteBatch.DrawString dentro de Begin/End. Para textos largos, gestiona el salto de línea y comprueba que la fuente incluya los caracteres necesarios. El juego gestiona las zonas de respuesta y la entrada de ratón y teclado; conserva identificadores, no textos traducidos. End: desactiva el dibujo y la entrada con dialogueOpen = false, no con Game.Exit().

  1. Continue y End: seguir las conexiones y cerrar la caja de texto
  2. Opciones: ID de respuesta, orden y continuación seleccionada
  3. Condiciones y variables: diez o dos monedas de oro
  4. Idioma: los mismos IDs, contenido recién resuelto
  5. Command: dar un objeto o abrir una tienda
  6. Call/Return: entrar en una subhistoria y regresar
Examina las respuestas y la comprobación del oro en este lenguaje

Leer los datos de una decisión

Inserta este fragmento donde story esté disponible tras la carga (en RPG Maker: const story = $gameTemp.vnleExample.story;). Muestra los identificadores para examinarlos. Sustituye esa salida por tus botones de respuesta. Cubre las dos respuestas del puerto y la comprobación de oro posterior; no es un intérprete general.

// Inside ReadWelcome while document is alive; inspect data before returning.
var nodes = document.RootElement.GetProperty("execution").GetProperty("nodes");
var choice = nodes.GetProperty("42edfff8-b8fe-46bf-ae0d-9e2347b6e726");
foreach (var idToken in choice.GetProperty("optionOrder").EnumerateArray())
{
    string optionId = idToken.GetString();
    var option = choice.GetProperty("options").GetProperty(optionId);
    System.Console.WriteLine(optionId + " " + option.GetProperty("text").GetProperty("textRef").GetString());
}
var branch = nodes.GetProperty("c1c3c372-0a73-4cd0-851d-ede59d5709bb");
int gold = 10; // Repeat with 2.
int minimum = branch.GetProperty("condition").GetProperty("right").GetProperty("value").GetProperty("value").GetInt32();
var next = branch.GetProperty(gold >= minimum ? "whenTrue" : "whenFalse");
System.Console.WriteLine(next.GetProperty("nodeRef").GetString());
// Copy strings/values you need later before disposing JsonDocument.

Con 10 monedas de oro, next apunta a «Pay five gold»; con 2, a «Not enough gold». En el juego, ejecuta la comprobación solo cuando el recorrido elegido alcance este nodo. Cada respuesta seleccionada aporta su propia continuación.

4 · Asocia tus propias imágenes

Este fragmento adicional espera un objeto de diálogo actual llamado node y los elementos de interfaz indicados en sus comentarios. Para C++ y MonoGame, speakerRef ya se ha leído como SpeakerId o speakerId. La clave es el identificador real del personaje Mira; el nombre y la ubicación de la imagen pertenecen a tu juego. Inserta las líneas en los lugares indicados.

// Game1 class field, so both LoadContent and Draw can access it:
private readonly System.Collections.Generic.Dictionary<string, Texture2D> portraits = new();
// Inside LoadContent(): import/build mira.png as portraits/mira.
portraits["807b2957-7bab-4a28-83a4-1f3d1589bbc2"] = Content.Load<Texture2D>("portraits/mira");
// Inside a SpriteBatch Begin/End in Draw; speakerId comes from speakerRef:
if (portraits.TryGetValue(speakerId, out var portrait))
    _spriteBatch.Draw(portrait, new Vector2(24, 120), Color.White);

Resuelve los nombres de los personajes mediante content.characters[speakerRef].nameTextRef, igual que el texto del diálogo. Si falta una asociación de imagen, oculta el retrato anterior o muestra una imagen de sustitución. Las imágenes no se seleccionan según el idioma.

Usar las referencias de imagen de la exportación completa

5 · Incluye los archivos en el juego

En este ejemplo, no cargues el JSON con Content.Load. TitleContainer abre el archivo sin procesar, que copia el csproj. Compila SpriteFont y las texturas con Content Pipeline. Prueba primero en DesktopGL y verifica otras plataformas por separado.

Comprueba el resultado

  1. Los saludos en inglés y alemán se muestran correctamente; si el idioma no está disponible, se usa el inglés.
  2. Al añadir el flujo, prueba ambas respuestas, 10 y 2 monedas de oro, el regreso de la subhistoria y End.
  3. Ejecuta el juego compilado fuera de la carpeta del proyecto. El JSON, las fuentes y los retratos también deben estar disponibles allí.

Documentación oficial

Referencias de API y configuración para este enfoque: