LogoPixi’VN
Crea il tuo primo

Crea la tua prima Visual Novel

Guida passo-passo alla creazione di una visual novel con Pixi’VN, che copre la configurazione del progetto, la narrazione, gli asset e l'interattività.

Questo tutorial ti guiderà attraverso il processo di creazione della tua prima Visual Novel.

A scopo di test, in questa guida ricreeremo la visual novel Breakdown utilizzando Pixi’VN. Breakdown è una breve storia che possiede 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❤️.

Dato che Pixi’VN ti dà la possibilità di scrivere la tua narrazione scegliendo uno o più linguaggi narrativi disponibili, verranno forniti esempi per ciascun linguaggio attualmente disponibile a ogni passaggio dello sviluppo.

Crea un nuovo progetto

Prima di iniziare, assicurati di aver installato i prerequisiti.

Il primo passo è creare un nuovo progetto. Puoi farlo eseguendo:

npm create pixi-vn@latest

e selezionando il template Visual Novel.

VS Code

Se apri il progetto con VS Code, ti verrà chiesto di acconsentire all'esecuzione di alcuni task del workspace e di installare le estensioni consigliate per il progetto. Si consiglia vivamente di accettare entrambe le cose.

Puoi trovare maggiori informazioni su template, task ed estensioni qui.

Creazione dei personaggi

Ora definiremo i personaggi di questa storia. Per farlo, definiremo nel file /content/characters.ts i personaggi che useremo.

Per maggiori informazioni su come creare e utilizzare i personaggi puoi consultare: Characters

content/characters.ts
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]);

Prima bozza della narrazione

Markup

Tutti i template supportano Markdown e Tailwind CSS, quindi lo useremo per la nostra 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.

Questo è l'esempio:

file_type_ink
ink/start.ink
=== 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.

// ...
-> DONE

Suddividere la narrazione in labels

Non è consigliabile creare labels molto lunghe (anche per le visual novel lineari), ma è consigliabile creare più labels piccole e "chiamarle" quando necessario con le funzionalità di controllo del flusso narrativo.

Per questo motivo, anche se nel nostro caso la storia è lineare, verrà divisa in due labels: la prima sarà quella che abbiamo appena creato (start), mentre la seconda si chiamerà second_part.

Questo è l'esempio:

file_type_ink
ink/start.ink
=== 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

Ora chiederemo al giocatore se vuole continuare con la seconda parte della 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).

Puoi trovare maggiori informazioni sui menu di scelta qui.

Questo è l'esempio:

file_type_ink
ink/start.ink
=== 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

Modificare le informazioni del personaggio e usarle come variabile

Ora darò al giocatore la possibilità di cambiare il nome di mc. Per fare questo, chiederò al giocatore di compilare una casella di input usando le funzionalità di Pixi’VN.

Dopo aver ottenuto il valore di input, puoi impostare il nome del personaggio usando il valore ottenuto.

In ink, _input_value_ è una variabile speciale che contiene il valore inserito dal giocatore. Puoi leggere di più a riguardo qui.

Questo è l'esempio:

file_type_ink
ink/start.ink
=== 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 all'interno dei dialoghi.

In ink, puoi usare le parentesi quadre (ad es. [sly]) per sfruttare il meccanismo di sostituzione del testo: per impostazione predefinita, i template controllano se il testo tra le parentesi corrisponde a un id di un personaggio, e in tal caso lo sostituiscono con il nome di quel personaggio.

In JS/TS, puoi ottenere lo stesso risultato usando i template literals con ${} (ad es. `${sly.name}`).

Questo è l'esempio:

file_type_ink
ink/start.ink
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

Definire gli asset e caricarli

Per caricare e manipolare gli asset (immagini, gif, video...) sarà necessario utilizzare Assets. Assets è una classe con molte funzionalità e proviene dalla libreria PixiJS; se vuoi maggiori informazioni leggi qui.

Uno dei primi passi è scegliere dove salvare gli asset della tua visual novel: puoi usare un servizio di hosting online oppure salvarli localmente nel progetto. Puoi trovare maggiori informazioni su entrambe le opzioni qui.

Se scegli di salvare gli asset localmente, devi solo posizionarli all'interno di src/assets: il template utilizza automaticamente AssetPack per analizzare i file e definirli in src/assets/manifest.gen.json. Se invece scegli di tenerli online, dovrai modificare manualmente assets/index.ts per registrarli.

In entrambi i casi, a ogni asset viene assegnato uno o più alias, che potrai poi usare nel tuo codice per fare riferimento a quell'asset.

icon.png
manifest.gen.json
index.ts
assets/manifest.gen.json
{
  "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": {}
          }
        }
      ]
    }
  ]
}

Aggiungere immagini di sfondo e dei personaggi

Ora è il momento di pensare anche alla parte visiva. Aggiungeremo le immagini di sfondo e dei personaggi al canvas della visual novel.

Nel nostro caso, ogni personaggio è composto da 3 sprite: il corpo, gli occhi e la bocca. Usiamo ImageContainer, un Container che raggruppa figli Sprite, per raggrupparli insieme e comporre il personaggio. Puoi trovare maggiori informazioni su come aggiungere componenti del canvas in questa documentazione.

Questo è l'esempio:

file_type_ink
ink/start.ink
=== 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

Suoni e musica

Per aggiungere suoni e musica alla tua visual novel, puoi usare l'utility sound.

Puoi definire canali audio per gestire diversi tipi di suoni (ad es. BGM, SFX) e controllarne il volume, la pausa, la ripresa, ecc. Si consiglia di definire i canali all'inizio del gioco, ad esempio nel callback then della funzione Game.init.

In questo esempio, definisco due canali: uno per la musica di sottofondo (BGM) e uno per gli effetti sonori (SFX). Imposto anche defaultChannelAlias sul canale SFX, in modo che se non specifico un canale quando riproduco un suono, questo venga riprodotto per impostazione predefinita nel canale SFX. Per definire il canale di sottofondo, imposto la proprietà background su true, in modo che, a differenza degli altri canali, i suoni non vengano interrotti alla fine di ogni step narrativo, ma continuino finché non vengono messi in pausa o interrotti.

main.ts
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;
});

Infine, puoi riprodurre i suoni e la musica nelle labels usando la funzione sound.play. Puoi anche usare i canali per gestire diversi tipi di suoni (ad es. BGM, SFX) e controllarne il volume, la pausa, la ripresa, ecc.

Puoi trovare maggiori informazioni su come usarlo qui.

file_type_ink
ink/start.ink
=== 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

Conclusione

Bene, ora sai come creare una visual novel con Pixi’VN. Da un grande potere derivano grandi responsabilità, quindi usalo con saggezza e crea una grande storia! 🚀

In questa pagina