Crea tu primera Visual Novel
Guía paso a paso para crear una visual novel con Pixi’VN, que abarca la configuración del proyecto, la narrativa, los recursos y la interactividad.
Este tutorial te guiará a través del proceso de creación de tu primera Visual Novel.
Con fines de prueba, en esta guía recrearemos la visual novel Breakdown usando Pixi’VN. Breakdown es una historia corta que tiene todas las características que debería tener una visual novel. Josh Powlison, el creador de Breakdown, nos ha dado permiso para usar su narración con fines educativos❤️.
Dado que Pixi’VN te da la posibilidad de escribir tu propia narración eligiendo uno o más lenguajes narrativos disponibles, se realizarán ejemplos para cada lenguaje actualmente disponible en cada paso del desarrollo.
Crear un nuevo proyecto
Antes de empezar, asegúrate de haber instalado los requisitos previos.
El primer paso es crear un nuevo proyecto. Puedes hacerlo ejecutando:
npm create pixi-vn@latesty seleccionando la plantilla Visual Novel.
VS Code
Si abres el proyecto con VS Code, se te pedirá que aceptes ejecutar algunas tareas del espacio de trabajo y que instales las extensiones recomendadas para el proyecto. Se recomienda encarecidamente aceptar ambas.
Puedes encontrar más información sobre plantillas, tareas y extensiones aquí.
Creación de personajes
Ahora definiremos los personajes de esta historia. Para ello, definiremos en el archivo /content/characters.ts los personajes que utilizaremos.
Para más información sobre cómo crear y usar personajes puedes consultar: Personajes
import Character from "@/models/Character";
import { RegisteredCharacters } from "@drincs/pixi-vn";
export const mc = new Character("mc", {
name: "Me",
});
export const james = new Character("james", {
name: "James",
color: "#0084ac",
});
export const steph = new Character("steph", {
name: "Steph",
color: "#ac5900",
});
export const sly = new Character("sly", {
name: "Sly",
color: "#6d00ac",
});
RegisteredCharacters.add([mc, james, steph, sly]);Primer borrador de la narrativa
Markup
Todas las plantillas tienen soporte para Markdown y Tailwind CSS, así que lo usaremos para nuestra narración.
Ahora podemos empezar a escribir el "primer borrador" de la narración de la visual novel.
Crearemos el primer label llamado start, que será el comienzo del juego. Después podremos escribir los diálogos que seguirán en nuestra visual novel.
Este es el ejemplo:
=== start ===
james: You're my roommate's replacement, huh?
james: Don't worry, you don't have much to live up to. Just don't use heroin like the last guy, and you' fine!
mc: ...
He thrusts out his hand.
james: James!
mc: ...Peter.
I take his hand and shake.
james: Ooh, Peter! Nice, firm handshake! The last quy always gave me the dead fish. I already think we'r gonna get along fine.
james: Come on in and...
james: ...
james: I know you're both watching, come on out already!
sly: I just wanted to see what the new guy was like. Hey, you, Peter- be nice to our little brother, or you'll have to deal with *us*.
mc: ...
james: Peter, this is Sly. Yes, that is her real name.
I put out my hand.
sly: I'm not shakin' your hand until I decide you're an all-right dude. Sorry, policy.
mc: Fair enough, I'm a pretty scary guy, or so l've been told.
james: The redhead behind her is Stephanie.
// Example of using Tailwind CSS
steph: <span class="inline-block motion-translate-y-loop-25">Hey</span>! Everyone calls me Steph. I'll shake your hand.
// ...
-> DONEDividir la narrativa en labels
No es recomendable crear labels muy largos (incluso para visual novels lineales), sino que es recomendable crear múltiples labels pequeños y "llamarlos" cuando sea necesario con las funciones de control de flujo de la narración.
Por esta razón, aunque en nuestro caso nuestra historia es lineal, se dividirá en dos labels: el primero será el que acabamos de crear (start), mientras que el segundo se llamará second_part.
Este es el ejemplo:
=== start ===
james: You're my roommate's replacement, huh?
james: Don't worry, you don't have much to live up to. Just don't use heroin like the last guy, and you' fine!
mc: ...
He thrusts out his hand.
james: James!
mc: ...Peter.
// ...
-> second_part
=== second_part ===
She enters my room before I'VE even had a chance to. \\n\\n...I could've just come back and gotten the platter later...
She sets it on a desk. I throw my two paper bags down beside the empty bed.
steph: They got you a new mattress, right? That last guy was a druggie, did James tell you that?
sly: *We're* the reason he got expelled!
steph: Sly! If word gets out about that... well, actually, it wouldn't matter, *he's* the one who shot himself up.
I'm fumbling for a new subject.
// ...
-> DONEMenús de elección
Ahora le preguntaremos al jugador si quiere continuar con la segunda parte de la visual novel.
To do this, we will set narration.choices to an array of choice options created with newChoiceOption (to jump or call a label) and newCloseChoiceOption (to close the choice menu without navigating anywhere).
Puedes encontrar más información sobre los menús de elección aquí.
Este es el ejemplo:
=== start ===
// ...
You want continue to the next part?
* Yes, I want to continue
-> second_part
* No, I want to stop here
-> END
=== second_part ===
// ...
-> DONEEditar la información del personaje y usarla como variable
Ahora le daré al jugador la posibilidad de cambiar el nombre del mc.
Para ello, le pediré al jugador que complete un cuadro de entrada usando las funciones de Pixi’VN.
Después de obtener el valor de entrada, puedes establecer el nombre del personaje usando el valor obtenido.
En ink, _input_value_ es una variable especial que contiene el valor introducido por el jugador. Puedes leer más al respecto aquí.
Este es el ejemplo:
=== start ===
// ...
He thrusts out his hand.
# request input type string default Peter
What is your name?
# rename mc { _input_value_ }
// ...
-> DONEAhora podríamos usar nombres de personajes dentro de los diálogos.
En ink, puedes usar corchetes (por ejemplo, [sly]) para aprovechar el mecanismo de reemplazo de texto: por defecto, las plantillas comprueban si el texto entre corchetes coincide con un id de personaje, y si es así, lo reemplazan con el nombre de ese personaje.
En JS/TS, puedes lograr el mismo resultado usando template literals con ${} (por ejemplo, `${sly.name}`).
Este es el ejemplo:
VAR steph_fullname = "Stephanie"
=== start ===
// ...
sly: I just wanted to see what the new guy was like. Hey, you, [mc]- be nice to our little brother, or you'll have to deal with *us*.
mc: ...
james: [mc], this is [sly]. Yes, that is her real name.
I put out my hand.
sly: I'm not shakin' your hand until I decide you're an all-right dude. Sorry, policy.
mc: Fair enough, I'm a pretty scary guy, or so l've been told.
james: The redhead behind her is [steph_fullname].
steph: Hey! Everyone calls me [steph]. I'll shake your hand.
She puts out her hand, and I take it.
mc: Thanks, good to meet you, [steph_fullname].
steph: WOW, that is, like, the most perfect handshake I've ever had! Firm, but also gentle. [sly], you *gotta* shake his hand!
// ...
-> DONEDefinir recursos y cargarlos
Para cargar y manipular recursos (imágenes, gifs, vídeos...) necesitarás usar Assets. Assets es una clase con muchas funciones y proviene de la biblioteca PixiJS; si quieres más información, lee aquí.
Uno de los primeros pasos es elegir dónde guardar los recursos de tu visual novel: puedes usar un servicio de alojamiento en línea o guardarlos localmente en el proyecto. Puedes encontrar más información sobre ambas opciones aquí.
Si eliges guardar los recursos localmente, solo necesitas colocarlos dentro de src/assets: la plantilla usa automáticamente AssetPack para analizar los archivos y definirlos en src/assets/manifest.gen.json. Si en cambio eliges mantenerlos en línea, tendrás que editar manualmente assets/index.ts para registrarlos.
En ambos casos, a cada recurso se le asigna uno o más alias, que luego puedes usar en tu código para hacer referencia a ese recurso.
{
"bundles": [
{
"name": "images",
"assets": [
{
"alias": [
"images_icon",
"icon"
],
"src": [
"images/[email protected]",
"images/icon-VA-NtA.webp",
"images/[email protected]",
"images/icon-E_ZC2g.png"
],
"data": {
"tags": {}
}
}
]
}
]
}Añadir imágenes de fondo y de personajes
Ahora es el momento de pensar también en la parte visual. Añadiremos las imágenes de fondo y de los personajes al canvas de la visual novel.
En nuestro caso, cada personaje está compuesto por 3 sprites: el cuerpo, los ojos y la boca. Usamos ImageContainer, un Container que agrupa hijos Sprite, para agruparlos y componer el personaje. Puedes encontrar más información sobre cómo añadir componentes de canvas en esta documentación.
Este es el ejemplo:
=== start ===
# show image bg bg01-hallway
# show imagecontainer james [m01-body m01-eyes-smile m01-mouth-neutral01] xAlign 0.5 yAlign 1
james: You're my roommate's replacement, huh?
# show imagecontainer james [m01-body m01-eyes-grin m01-mouth-smile01]
james: Don't worry, you don't have much to live up to. Just don't use heroin like the last guy, and you'll be fine!
# show imagecontainer james [m01-body m01-eyes-smile m01-mouth-grin00]
mc: ...
// ...
-> DONESonidos y música
Para añadir sonidos y música a tu visual novel, puedes usar la utilidad sound.
Puedes definir canales de sonido para gestionar diferentes tipos de sonidos (por ejemplo, BGM, SFX) y controlar su volumen, pausa, reanudación, etc. Se recomienda definir los canales al inicio del juego, por ejemplo en el callback then de la función Game.init.
En este ejemplo, defino dos canales: uno para la música de fondo (BGM) y otro para los efectos de sonido (SFX). También establezco defaultChannelAlias en el canal SFX, de modo que si no especifico un canal al reproducir un sonido, se reproducirá en el canal SFX por defecto. Para definir el canal de fondo, establezco la propiedad background en true, de modo que, a diferencia de los otros canales, los sonidos no se detendrán al final de cada step narrativo, sino que continuarán hasta que se pausen o detengan.
import { Game, sound } from "@drincs/pixi-vn";
import { BGM_CHANNEL_NAME, SFX_CHANNEL_NAME } from "@/constans";
Game.init(body, {
// ...
}).then(() => {
sound.channels.add(BGM_CHANNEL_NAME, { background: true });
sound.channels.add(SFX_CHANNEL_NAME);
sound.defaultChannelAlias = SFX_CHANNEL_NAME;
});Finalmente, puedes reproducir los sonidos y la música en los labels usando la función sound.play. También puedes usar canales para gestionar diferentes tipos de sonidos (por ejemplo, BGM, SFX) y controlar su volumen, pausa, reanudación, etc.
Puedes encontrar más información sobre cómo usarlo aquí.
=== start ===
# show image bg bg01-hallway
# play sound sfx_whoosh delay 0.1
# show imagecontainer james [m01-body m01-eyes-smile m01-mouth-neutral01] xAlign 0.5 yAlign 1 with movein direction right ease circInOut type spring
james: You're my roommate's replacement, huh?
# play sound sfx_whoosh channel bgm loop true
# show imagecontainer james [m01-body m01-eyes-grin m01-mouth-smile01]
james: Don't worry, you don't have much to live up to. Just don't use heroin like the last guy, and you'll be fine!
# show imagecontainer james [m01-body m01-eyes-smile m01-mouth-grin00]
mc: ...
// ...
# pause all sounds
# show imagecontainer steph [fm02-body fm02-eyes-smile fm02-mouth-smile00]
# play sound sfx_whoosh delay 0.1
# remove image james with moveout direction right ease circInOut type spring duration 0.5 delay 0.05
# remove image sly with moveout direction right ease anticipate duration 0.5
# remove image steph with moveout direction left ease easeInOut duration 0.5 delay 0.1
You want continue to the next part?<># continue
* Yes, I want to continue
-> second_part
* No, I want to stop here
-> END
=== second_part ===
# show text bg "(A few minutes later...)" style { fontFamily: "Arial", dropShadow: { alpha: 0.8, angle: 2.1, blur: 4, color: "0x111111", distance: 10, }, fill: "\#ffffff", stroke: { color: "\#004620", width: 12, join: "round" }, fontSize: 60, fontWeight: "lighter" } with fade
# edit text bg align 0.5
# pause
# resume all sounds
# show image bg bg02-dorm align 0 with fade
# play sound sfx_whoosh delay 0.4
// ...
She enters my room before I'VE even had a chance to.
// ...
-> DONE
Conclusión
Bien, ahora ya sabes cómo crear una visual novel con Pixi’VN. Un gran poder conlleva una gran responsabilidad, ¡así que úsalo con sabiduría y crea una gran historia! 🚀