Dopo anni di relativo abbandono, vi è un certo ritorno di interesse per questo genere di giochi, limitato tuttavia a un relativamente ristretto numero di appassionati. L'impegno richiesto e la necessità di scrivere parecchio alla tastiera lo rendono infatti poco attraente per chi sia abituato a comandare il computer a base di clic (ciò è naturalmente una semplificazione, ma non è questo il luogo per un'analisi approfondita).Cosa devo fare? Attacca il drago Solleticato dalla tua lancia, il drago starnutisce riducendoti in cenere.
All'estremo opposto troviamo i libri-gioco di carta: una struttura inflessibile a base di scelte preconfezionate, che tuttavia pare gradita da molti lettori (d'altra parte un libro può essere appassionante anche senza scelte, nella veste classica di racconto immutabile, da leggersi dalla prima all'ultima pagina). Un tipico libro-gioco presenta alternative di questo tipo:
- Se vuoi attaccare il drago, vai al paragrafo 83.Idra è sostanzialmente basato su questa seconda impostazione, ma aggiunge una flessibilità assai superiore rispetto a quella offerta dalla stampa su carta: sia le scelte disponibili che il contenuto stesso di una pagina possono infatti dipendere dal comportamento precedente del lettore.
- Se cerchi di parlare col drago, vai al paragrafo 132.
- Se ti nascondi dietro una colonna, vai al paragrafo 12.
Ho progettato Idra anche per togliermi un dubbio: i giochi di avventura basati su testo (cioè senza grafica) sono poco popolari perché la gente non ha voglia di leggere, o semplicemente perché non ha voglia di scrivere? Ho deciso di provare a togliere questa seconda necessità per vedere quale possa essere il gradimento dei lettori.
L'esperienza dei miei giochi di avventura scritti negli anni '80, strettamente legati al linguaggio Basic nella versione Microsoft, mi ha insegnato a diffidare degli strumenti prodotti da una sola azienda. Per Idra volevo garantire la massima portabilità su diversi sistemi, l'indipendenza da strumenti di un dato produttore e, possibilmente, una ragionevole durata nel tempo.
Linguaggi dinamici come il Perl o il Tcl/Tk sono molto interessanti per questo genere di applicazioni, ma richiedono che il lettore abbia installato il relativo interprete; è inoltre difficile garantirne il corretto funzionamento con diverse versioni e diversi sistemi operativi.
Java ha il vantaggio della disponibilità di un interprete nei principali browser, ma trovo il linguaggio alquanto pesante e burocratico sia per il programmatore (cioè io) che, soprattutto, per l'autore dei racconti. Inoltre, cosa importante, Java non è dinamico: codice e dati sono rigidamente separati; ciò significa che l'autore sarebbe obbligato a compilare il programma a ogni modifica del comportamento del gioco.
Certo, con un po' di lavoro (non poco) si potrebbe definire in Java un completo linguaggio su misura che l'autore possa usare senza bisogno di ricompilazione. A questo punto, però, tanto varrebbe farlo in C per poter distribuire direttamente un programma eseguibile, almeno nelle versioni per i più diffusi sistemi operativi, coè Windows e Linux. Ciò eliminerebbe indubbiamente molti problemi.
Nell'estate del 1999 definii quindi a grandi linee un linguaggio dinamico e iniziai a scrivere le librerie C che ne avrebbero formato i mattoni portanti. Mi fermai quando lessi le specifiche di JavaScript e mi resi conto che era quasi identico a ciò che avevo in mente (almeno nella struttura di base). Inoltre esisteva uno standard indipendente (ECMA-262).
JavaScript richiede un interprete, ma esso è contenuto nei principali browser: avevo quindi quasi gratis un linguaggio dinamico e i vantaggi di HTML. C'era però un prezzo da pagare: l'impossibilità di accedere ai file e l'obbligo di usare i cookie per salvare la situazione di gioco. Decisi di accettare questi limiti e riprogettai di conseguenza l'intero sistema, scegliendo una versione stabile di JavaScript (la 1.1) per avere la massima compatibilità.
Ciasuna pagina del racconto diventa quindi una funzione JavaScript che disegna la pagina in un apposito frame, con la semplice aggiunta del meccanismo per passare da una pagina all'altra. I semplici link HTML non bastano, perché un clic su una scelta può anche comportare l'esecuzione di una serie di istruzioni.
Con riferimento al codice nel file idra.js, le funzioni rinvio() e scelta() ricordano quindi nell'array scelte[] l'azione desiderata (salto a una pagina, o istruzioni da eseguire) e predispongono un link HTML che, in risposta a un clic, chiami la funzione eseguiScelta() passandole il numero dell'azione corrispondente. Tralasciando un problema che vedremo tra breve, un clic su una scelta ha come effetto l'esecuzione di:
eseguiScelta(4)La eseguiScelta() consulta scelte[4] che contiene l'azione richiesta e, con l'aiuto della esegui(), ne esamina il tipo: se è una pagina la presenta usando la consueta vai(), altrimenti la considera codice da eseguire con la funzione eval() tipica dei linguaggi dinamici, che interpreta le istruzioni contenute in una stringa, ad esempio:
eval("lire += 1000; vai(Edicola)")L'unica complicazione in questo semplice ma efficiente meccanismo è causata dal pulsante "Indietro" del browser: se infatti il lettore potesse tornare indietro a piacimento, sarebbe impossibile garantire il corretto funzionamento del racconto-gioco (si dovrebbe memorizzare a ogni passo lo stato delle variabili per poterlo in seguito ripristinare, cosa impossibile a causa dei limiti di spazio nei salvataggi su disco).
Per evitare che il tutto possa finire in uno stato imprevisto a causa di queste operazioni, a ciascuna presentazione di pagina è assegnato un identificatore (idPagina), un semplice numero progressivo che viene anche scritto nei link dalle funzioni rinvio() e scelta(); in questo modo:
eseguiScelta(23:4)In caso di clic, la eseguiScelta() verifica per prima cosa che l'identificatore del link su cui il lettore ha fatto clic (23 nell'esempio) coincida con quello della pagina corrente. Se ciò non avviene, significa che la pagina rappresentata sul video non è quella creata da Idra, tpicamente perché il lettore ha premuto "Indietro" sul browser, per cui viene presentato un messaggio di errore e poi viene chiamata la funzione ridisegna() per mostrare nuovamente al lettore la pagina corretta.
Per rendere ripristinabile lo stato del generatore pseudocasuale, ho dovuto sostituire quello di JavaScript (Math.random()) con la funzione caso() che impiega il semplice algoritmo di molti sistemi Unix; essa è a sua volta chiamata dalla dado() messa a disposizione dell'autore.
La creaStringaStato() ha il compito di 'impacchettare' tutte le informazioni, incluso il nome della pagina corrente, in un'unica stringa che riassume lo stato corrente del racconto-gioco; eventuali stringhe contenute nelle variabili vengono messe tra virgolette, mentre le virgolette contenute nelle stringhe sono sostituite dalla sequenza \" per evitare interferenze. Il risultato è una stringa di questo tipo:
v.inv=1;v.lupi=16;v.capr=2;v.uova=0;v._qui_="P11";v._caso_=271568720La funzione registraCookie() codifica ulteriormente la stringa con la escape() per evitare la presenza di caratteri non ammessi, come ad esempio i segni di interpunzione; vi aggiunge poi il nome e la data di scadenza e salva il tutto in un cookie, se le circostanze lo permettono. Il cookie conterrà una stringa del genere:
v.inv%3D0%3Bv.lupi%3D0%3Bv.capr%3D2%3Bv.uova%3D0%3Bv._qui_%3D%22P11%22%3Bv._caso_%3D271568720%3BVi è qui una certa inefficienza dovuta alla codifica, che potrebbe eliminata con un'elaborazione preliminare per togliere i v. e i simboli (= ;); questa organizzazione della stringa ha comunque il vantaggio di poter ripristinare lo stato del sistema con una semplice eval(), come fa appunto la ripristinaStato().
Infine, nel caso di problemi con i cookie (che possono ad esempio essere disabilitati per scelta del lettore) Idra salva lo stato nella variabile situazioneSalvata: ciò non consente di conservarlo se si esce dal browser, ma lo si può comunque recuperare nell'ambito della stessa sessione, cioè se non si è usciti dal racconto-gioco. È ad esempio possibile salvare prima di fare una scelta rischiosa, per poi riprendere dallo stesso punto nel caso che sia andata male.
v.punti += 15Ciò accadeva ad esempio nel caso di un salvataggio della situazione: la pagina corrente era infatti già stata presentata, e quindi le variabili erano state cambiate di conseguenza; al ripristino doveva però essere mostrata nuovamente, per cui la sua funzione era eseguita una seconda volta, con i possibili effetti collaterali indesiderati di cui sopra.
Per evitare questo problema, la funzione vai() salva l'intero stato nella variabile statoPrecedente prima di mostrare una pagina, in modo da conservare la situazione com'era prima che la pagina fosse presentata. Salvando questa situazione precedente anziché quella attuale, il ridisegno della pagina al momento del ripristino rimetterà le cose esattamente come stavano.
La funzione ridisegna() fa uso appunto dello statoPrecedente per mostrare una pagina senza eseguirne il codice; in realtà essa fa un passo indietro (usa lo stato prima che la pagina fosse disegnata) e uno avanti (disegna la pagina): l'effetto è quello di non alterare né i valori delle variabili né quello degli eventuali tiri di dado.
Dato che la funzione mostra(), a differenza della vai(), non salva lo stato precedente, è sempre possibile mostrare delle pagine temporanee e poi ridisegnare la pagina corrente usando appunto ridisegna() senza rischiare alcun effetto collaterale, purché ovviamente le stesse pagine temporanee non abbiano cambiato il valore di qualche variabile di gioco.
Ho solo dovuto risolvere qualche difficoltà di ordine tecnico, in particolare la tendenza di alcuni browser ad andare in crash in determinate condizioni; ho rilevato sperimentalmente che la suddivisione dell'esecuzione in due parti (eseguiDebug() ed eseguiDebug2()) collegate fra loro con un timer sembra aver eliminato l'inconveniente.
Avrei voluto controllare l'attivazione del debugger con una combinazione di tasti, ma le incompatibilità tra i diversi browser hanno sconsigliato questa soluzione; per evitare di dover modificare il file del racconto (gioco.js) per attivare o disattivare il debugger, ho inserito un automatismo nel frame di controllo ctrl.html: se la pagina viene aperta dentro un frame di nome "Debug", biene mostrato il link corrispondente.
Tuttavia vi sono alcuni punti che potrebbero essere perfezionati, e sui quali potrei un giorno o l'altro lavorare se mai dovessi rimettere mano al programma, ad esempio:
- La compressione dei dati di salvataggio per aumentare lo spazio disponibile.
- Un maggiore controllo di errore, specie sugli argomenti passati alle funzioni.
- L'introduzione di azioni a tempo, inteso come numero di mosse.
- L'utilizzo di immagini con zone attive (<img usemap>).
- L'eventuale aggiunta di una 'traccia' che consenta di sfogliare all'indietro.
Terrò naturalmente in considerazione anche le richieste degli autori che sceglieranno di usare Idra per i propri racconti-gioco; mi pare comunque che Idra offra già parecchie possibilità così com'è e vorrei vederle sfruttate almeno in parte prima di pensare a eventuali versioni successive.
Inoltre, caso mai trovassi il tempo, piacerebbe anche a me scrivere un racconto-gioco; è da un po' che ho un'idea in mente e, lo confesso, è anche per questo che ho scritto Idra.
«-- Precedente | 1 | 2 | 3 | 4 | 5 | 6 | 7 | A | B | Indice | Successivo --» |