Crea la tua prima Visual Novel
Guida passo passo alla creazione di una visual novel con Pixi'VN, che copre l'impostazione del progetto, la narrazione, le risorse e l'interattività.
Questo tutorial ti guiderà attraverso il processo di creazione della tua prima Visual Novel.
Cos'è una visual novel? Una visual novel (VN) è una forma di narrativa interattiva digitale. Le visual novel sono spesso associate al medium dei videogiochi, ma non sempre vengono etichettate come tali. Combinano una narrazione testuale con illustrazioni statiche o animate e un grado variabile di interattività. Il formato è più raramente indicato come novel game, una ritrascrizione del termine wasei-eigo noberu gēmu (ノベルゲーム), che è più spesso usato in giapponese.
A scopo di test, in questa guida ricreeremo la visual novel Breakdown utilizzando Pixi'VN. Breakdown è un racconto breve che ha tutte le caratteristiche che una visual novel dovrebbe avere. Josh Powlison, il creatore di Breakdown, ci ha dato il permesso di utilizzare la sua narrazione per scopi didattici❤️.
Poiché Pixi’VN ti dà la possibilità di scrivere la tua narrazione scegliendo un o più linguaggio narrative disponibili, verranno realizzati esempi per ciascun linguaggio attualmente disponibile in ogni step
di sviluppo.
Crea un nuovo progetto
Il primo step
è creare un nuovo progetto. Puoi trovare maggiori informazioni su come creare un nuovo progetto partendo da un template qui. Utilizzeremo il template "Visual Novel - React".
Visual Novel -> React
Una volta completata la creazione, è molto importante leggere il file README.md
che si trova nella cartella principale del progetto. Questo file contiene informazioni importanti sul progetto e su come utilizzarlo.
Nel nostro caso, per avviare il progetto dovremo semplicemente eseguire i seguenti comandi:
npm install
npm start
Creazione dei personaggi
Adesso definiremo i personaggi di questa storia. Per fare ciò, definiremo nel file /values/characters.ts
i personaggi che utilizzeremo. Per maggiori informazioni su come creare e utilizzare i personaggi puoi consultare: Personaggi
Cosa significa mc
? mc
è un'abbreviazione comune per "Main Character". Nelle visual novel è prassi comune usare mc
come nome del personaggio principale.
import { RegisteredCharacters } from "@drincs/pixi-vn";
import Character from "../models/Character";
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]);
Prima bozza della narrazione
Ora possiamo iniziare a scrivere la "prima bozza" della narrazione della visual novel.
Creeremo la prima label
chiamata start
, che sarà l'inizio del gioco.
Dopodiché potremo scrivere i dialoghi che seguiranno nella nostra visual novel. Il template che abbiamo scelto supporta il linguaggio di markup markdown (Linguaggio di markup in ink), quindi lo useremo per la nostra narrazione.
Questo è l'esempio:
=== 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.
steph: Hey! Everyone calls me Steph. I'll shake your hand.
// ...
-> DONE
Dividi la narrazione in più label
Non è consigliabile creare labels
molto lunghe (anche per visual novel lineari), ma è consigliabile creare più labels
piccole e "chiamarle" quando necessario con le funzionalità di controllo del flusso di gioco (ink knot (o label)).
Per questo motivo, anche se nel nostro caso la nostra storia è lineare, verrà divisa in due labels
, la prima sarà quella che abbiamo appena creato, mentre la seconda si chiamerà second_part
.
Questo è l'esempio:
=== 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.
// ...
-> DONE
Menù a scelta
Ora chiederemo al giocatore se desidera continuare con la seconda parte della visual novel.
Per fare ciò, utilizzeremo il menu di scelta.
Questo è l'esempio:
=== start ===
// ...
You want continue to the next part?
* Yes, I want to continue
-> second_part
* No, I want to stop here
-> END
=== second_part ===
// ...
-> DONE
Modifica le informazioni del personaggio e usalo come variabile
Ora darò al giocatore la possibilità di cambiare il nome del mc
.
Per fare ciò, chiederò al giocatore di completare una casella di input utilizzando le funzionalità di Pixi’VN (Utilizzare il prompt di input in ink).
Dopo aver ottenuto il valore di input, puoi impostare il nome del personaggio utilizzando il valore ottenuto (Modifica il nome del personaggio in ink).
Questo è l'esempio:
VAR _input_value_ = ""
=== start ===
// ...
He thrusts out his hand.
# request input type string default Peter
What is your name?
# rename mc { _input_value_ }
// ...
-> DONE
Ora potremmo usare i nomi dei personaggi nei dialoghi (Usa il nome del personaggio nei dialoghi in ink).
Questo è l'esempio:
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!
// ...
-> DONE
Utilizzare la funzionalità "glue" dei dialoghi
Nelle visual novel è spesso utile incollare del testo nel dialogo corrente. Ad esempio, per mettere in pausa una conversazione e farla proseguire in uno step
successivo. Per farlo, possiamo usare la funzionalità glue.
Questo è l'esempio:
=== start ===
// ...
james: Ooh, [mc]! Nice, firm handshake!
<>The last guy always gave me the dead fish.
<>I already think we're gonna get along fine.
james: Come on in and...
// ...
-> DONE
Definisci le assets e caricale
Uno dei primi steps
è scegliere dove salvare le risorse della tua visual novel. In questo caso salveremo gli assets nello storage Firebase (un servizio di hosting).
Per caricare e manipolare risorse (immagini, gif, video...) sarà necessario utilizzare Assets
. Assets
è una classe con molte funzionalità e proviene dalla libreria PixiJS, se vuoi maggiori informazioni leggi qui.
Prima di utilizzare un asset, si consiglia vivamente di inizializzare la matrice degli asset.
Per impostazione predefinita, come puoi vedere nel file assets/manifest.ts
, tutti i template in onLoadingLabel
tentano di caricare in background il "bundle asset" con l'alias uguale all'id della label
. Si consiglia quindi di aggiungere nel manifest
un bundle assets
per ogni label
con alias uguale all'id della label
e contenente le immagini utilizzate in quella label
.
Questo è l'esempio:
import { Assets } from "@drincs/pixi-vn";
import manifest from "../assets/manifest";
/**
* Define all the assets that will be used in the game.
* This function will be called before the game starts.
* You can read more about assets management in the documentation: https://pixi-vn.web.app/start/assets-management.html
*/
export async function defineAssets() {
Assets.init({ manifest });
// The game will not start until these asserts are loaded.
await Assets.loadBundle("main_menu");
// The game will start immediately, but these asserts will be loaded in the background.
// Assets.backgroundLoadBundle("main_menu");
// Assets.backgroundLoad("background_main_menu");
}
Aggiungere lo sfondo e le immagini dei personaggi
Ora è il momento di pensare anche all'aspetto visivo. Aggiungeremo lo sfondo e gli sprite dei personaggi al canvas della visual novel.
Cos'è uno sprite? Nella computer grafica, uno sprite è una bitmap bidimensionale integrata in una scena più grande, il più delle volte in un videogioco 2D.
Nel nostro caso gli sprite dei personaggi sono composti da 3 immagini: il corpo, gli occhi e la bocca. Quindi utilizziamo ImageContainer per comporre il personaggio.
Puoi trovare maggiori informazioni su come aggiungere componenti canvas in questa documentazione (Utilizza componenti canvas in ink).
Questo è l'esempio:
=== 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: ...
// ...
-> DONE
Caricamento intelligente degli assets
Nel nostro caso abbiamo salvato le immagini del gioco su un servizio di hosting (Firebase). Per questo motivo il caricamento delle risorse non avviene tempestivamente.
Per evitare che il giocatore percepisca troppi caricamenti, dovremmo raggrupparli in determinate fasi del gioco. Nel mio caso caricherò le immagini più utilizzate all'inizio della label
.
Puoi trovare maggiori informazioni su come gestire i caricamenti qui.
Questo è l'esempio:
=== start ===
# load assets bg01-hallway
# load assets m01-body m01-eyes-grin m01-eyes-smile m01-eyes-wow m01-mouth-grin00 m01-mouth-smile00 m01-mouth-smile01
# load assets fm01-body fm01-eyes-smile fm01-eyes-upset fm01-mouth-serious00 fm01-mouth-serious01 fm01-mouth-smile00
# load assets fm02-body fm02-eyes-joy fm02-eyes-nervous fm02-eyes-wow fm02-mouth-nervous00 fm02-mouth-smile00
# show image bg bg01-hallway
# show imagecontainer james [m01-body m01-eyes-smile m01-mouth-neutral01] xAlign 0.5 yAlign 1 with movein direction right speed 300
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: ...
// ...
-> DONE
Utilizzare le transizioni
Per rendere più dinamica la visual novel, è possibile utilizzare le transizioni per mostrare le immagini. Puoi trovare maggiori informazioni sull'uso delle transizioni qui (Uso delle transizioni in ink).
Questo è l'esempio:
=== start ===
// ...
# show image bg bg01-hallway
# show imagecontainer james [m01-body m01-eyes-smile m01-mouth-neutral01] xAlign 0.5 yAlign 1 with movein direction right speed 300
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: ...
// ...
-> DONE
Creare un'animazione
Per rendere più dinamica la visual novel, è possibile utilizzare le animazioni. Puoi trovare maggiori informazioni su come utilizzare le animazioni qui (Utilizzo delle animazioni in ink).
Consiglio di utilizzare Typescript se è necessario impostare molte proprietà, in questo modo si ha un maggiore controllo sull'animazione, più funzionalità e feedback sul tipo.
Nel mio caso l'animazione rimuoverà steph
dalla scena e la reinserirà nel steph
successivo. La specchierò anche sull'asse x per assicurarmi che sia rivolta nella direzione giusta.
Per far entrare/uscire steph
userò le funzioni moveOut
e moveIn
. Per l'effetto specchio userò il ticker ZoomTicker
.
Una caratteristica importante delle transizioni è che mettono momentaneamente in pausa tutte le animazioni collegate a quel componente e le riprendono al termine della transizione.
Quindi, nel mio caso, userò prima della funzione moveIn
la funzione addTicker
per aggiungere il ticker ZoomTicker
. In questo modo steph
verrà specchiato sull'asse x una volta completata la transizione.
Inoltre, poiché per questa animazione utilizzerò TypeScript, ho creato un'label
per questa animazione. In modo che possa essere chiamato anche da altri linguaggi che non siano JS/TS.
import { canvas, moveIn, newLabel, ZoomTicker } from "@drincs/pixi-vn";
export const animation01 = newLabel("animation_01", [
async () => {
canvas.addTicker(
"steph",
new ZoomTicker({ type: "zoom", limit: 1, speed: 70 })
);
await moveIn("steph", {
value: ["fm02-body", "fm02-eyes-joy", "fm02-mouth-smile01"],
options: { xAlign: 0.8, yAlign: 1, scale: { y: 1, x: -1 }, anchor: 0.5 },
}, { direction: "right", speed: 300 }
);
},
]);
Ora posso chiamare questa label
animation_01
dall'label
principale start
. (Come spiegato qui da ink è possibile chiamare labels
scritte in ts e viceversa.)
=== start ===
// ...
# show imagecontainer james [m01-body m01-eyes-grin m01-mouth-grin00]
# show imagecontainer sly [fm01-body fm01-eyes-smile fm01-mouth-smile00]
# show imagecontainer steph [fm02-body fm02-eyes-upset fm02-mouth-nervous00]
# remove image steph with moveout direction left speed 300
[steph_fullname] goes through the opposite door,
# call animation_01
<> and returns with a HUGE tinfoil-covered platter.
// ...
-> DONE
Conclusione
Bene, ora sai come creare una visual novel con Pixi'VN. Da un grande potere derivano grandi responsabilità, quindi usalo saggiamente e crea una grande storia! 🚀
Ecco un esempio interattivo con un'interfaccia utente minimale (HTML). Scorrendo verso il basso è possibile vedere lo stesso risultato utilizzando un'interfaccia utente completa (template React).