sabato 20 febbraio 2016

Riprodurre un video

Ciao ragazzi, eccomi qui con un tutorial dopo un pò di mesi.
Come ho spiegato nel precedente articolo, l'unico modo in cui per ora posso continuare a parlare del funzionamento di AGS è il blog.

Come avete letto dal titolo oggi si parla di come riprodurre video in AGS, una domanda che mi è stata posta molte volte.

In AGS esistono due funzioni adatte allo scopo: PlayVideo e PlayFlic.

PlayVideo serve per riprodurre file in diversi formati video (spiegherò quali nell'aritcolo), mentre PlayFlic per le animazioni Flic (formato .FLC) .

Premetto che sulle animazioni Flic non ho trovato molto, non sono abbastanza documentato, pertanto non so dirvi come possono essere create.
So che si tratta di un tipo di animazione obsoleto e utilizzato nelle avventure grafiche con Colour Depth a 8-bit, quindi molto datate. In questo tipo di giochi infatti PlayVideo non funziona.

Qualche riga sul funzionamento di PlayFlic verrà comunque spesa a fine articolo.

PlayVideo


I giochi con Colour Depth 16/32-bit possono utilizzare la funzione PlayVideo.
Vediamola nel dettaglio:

PlayVideo (string filename, VideoSkipStyle, int flags)

Questa funzione prende in input 3 parametri:
  • filename: una stringa, ovvero il nome del file da riprodurre (ad esempio "stanzarossa.avi");
  • VideoSkipStyle: una costante che ci verrà suggerita dall'editor e che rappresenta la modalità con cui al giocatore è permesso (o non permesso) di saltare il video;
  • flags: un numero intero per specificare alcuni aspetti della riproduzione del video (proporzioni e audio);

Filename


Esso altro non è che il nome del file compresa la sua estensione.
I file video devono essere inseriti nella cartella Compiled che trovate all'interno della cartella principale del gioco.

Va bene anche creare una o più sottocartelle dentro Compiled, ma, dovrete specificarla/le nel filename affinchè AGS lo possa trovare e riprodurre (ad esempio "introduzione/stanzarossa.avi" nel caso "introduzione" sia una sottocartella di Compiled).

I formati video supportati da AGS sono: AVI, MPG, OGG Theora o qualsiasi altro tipo di file supportato dal Media Player del vostro pc (su quest'ultimo punto non sono sicurissimo, ma così ho interpretato quello che l'help dinamico dice di PlayVideo).

Per quanto riguarda la compilazione del gioco (ovvero quando con F7 create l'eseguibile del vostro gioco finito), bisogna distinguere tra due tipi di formati video: gli OGG Theora e tutti gli altri tipi.

Per gli OGG Theora, AGS ha un supporto incorporato, quindi qualunque giocatore sarà in grado di vedere un video in questo formato.
I video OGG Theora sono inoltre integrati nel file EXE del gioco nel momento in cui il gioco viene compilato (accertatevi che il file abbia un estensione .OGV e sia posizionato nella cartella principale del progetto, non in Compiled).

Il secondo tipo di file che AGS può riprodurre è un qualsiasi formato supportato da Windows Media Player.
La lista include i formati AVI, MPG ed altri. Comunque, per fare in modo che questi tipi di formati vengano riprodotti, dovrete avere i codec video installati.
Per esempio, se create un video con codifica XVid, il player dovrà disporre del codec XVid installato.

A tal proposito, se già non ce l'avete, consiglio di scaricare e installare K-Lite Code Pack, pacchetto contenente codec video e audio per la riproduzione di tutti i formati video.

Attenzione però, questo tipo di video non può essere incluso nell'EXE del gioco, per cui dovrete darli al giocatore insieme all'eseguibile.

Questo significa che il giocatore avrà sia l'eseguibile che i video. Anche in questo caso, la struttura delle cartelle deve permettere ad AGS di trovare i file video che richiamate dal codice.

Ricapitolando: i video OGG Theora bisogna inserirli nella cartella principale del gioco (dove c'è anche il file .agf, non in Compiled) ed essi verranno incorporati nell'exe per cui anche a gioco compilato non avremo bisogno di una cartella apposita per i video che l'exe dovrà andare a caricare.

Gli altri formati invece devono essere inseriti in Compiled e non verranno incorporati nel gioco. Pertanto dovranno essere inseriti in una cartella apposita a gioco compilato e l'utente potrà anche aprirli volendo.

VideoSkipStyle


VideoSkipStyle definisce come il giocatore può saltare il video:
  • eVideoSkipNotAllowed: il giocatore non può saltare il video;
  • eVideoSkipEscKey: il giocatore può premere ESC per saltare il video;
  • eVideoSkipAnyKey: il giocatore può premere qualsiasi tasto per saltare il video;
  • eVideoSkipAnyKeyOrMouse: il giocatore può premere qualsiasi tasto o cliccare col mouse per saltare il video;

Flags


VideoSkipStyle definisce come il giocatore può saltare il video:
  • 0: the il video verrà riprodotto alle dimensioni originali, con audio AVI;
  • 1: il video verrà teso in altezza e larghezza a schermo intero, con le appropriate bande nere per mantenere le proporzioni e audio AVI;
  • 10: dimensioni originali, l'audio del gioco continuerà a venire riprodotto (audio AVI muto);
  • 11: teso in altezza e larghezza a schermo intero, l'audio del gioco continuerà a venire riprodotto (audio AVI muto);
Vediamo un esempio di come utilizzare questa funzione:

function room_Load()
{
  PlayVideo("stanzarossa.avi", eVideoSkipAnyKey, 0);
}


In questo caso, prima del caricamento della stanza verrà riprodotto il video chiamato "stanzarossa.avi". Il giocatore potrà saltarlo premendo un tasto qualsiasi della tastiera e verrà riprodotto alle dimensioni originali, con audio AVI;

Per comodità e ordine, nel prossimo esempio, invece di mettere il file del video semplicemente in Compiled, ho creato una cartella dentro Compiled chiamata "video".


function room_Load()
{
  PlayVideo("video/stanzarossa.avi", eVideoSkipAnyKey, 0);
}


Questo semplicemente per sottolineare che dentro Compiled possiamo creare più sottocartelle al fine di organizzare meglio il progetto.

Pensate se nel vostro gioco ci fossero decine di filmati, che guazzabuglio si verrebbe a creare considerando che in Compiled non ci sono solo i video.

Ecco un esempio di come può essere utilizzato un video.

Il codice è:

// room script file

function room_Load()
{
  PlayVideo("video/stanzarossa.avi", eVideoSkipAnyKey, 0);
}

function room_AfterFadeIn()
{
  aIce_Flow.Play();
  cEgo.Walk(200, 356, eBlock, eWalkableAreas);
}


In questo caso mi sono divertito a ricreare quell'effetto di filmato che si fonde con la grafica in-game visibile in alcuni titoli che utilizzano sfondi pre-renderizzati.

Prima del caricamento della stanza viene eseguito un filmato con l'audio (l'audio è del video, non è un file audio caricato in AGS).
Dopodichè viene caricata la stanza, che ha come sfondo l'ultimo frame del video cosicchè non si nota il passaggio dal video alla stanza.

AGS di default, carica la stanza con un effetto fade in. Se fosse stato così anche nel mio esempio, dopo il video avremmo visto il fade in della stanza, il che avrebbe rovinato l'effetto di continuità dal video al gioco.

Per togliere l'effetto fade in andate in General Settings, poi nella lista di opzioni, nella sezione Visual (verso la fine dell'elenco), cambiate l'opzione "Default transition when changing rooms" da "FadeOutAndIn" a "Instant".

Dopo il caricamento della stanza verrà riprodotta una traccia audio (questa volta caricata in AGS) e il nostro personaggio farà il suo ingresso nella stanza.

Ecco il risultato.



PlayFlic


Di questa funzione mi limiterò a riportarvi la traduzione direttamente dall'help dinamico.

PlayFlic (int flic_number, int options)

Riproduce un'animazione FLI o FLC. AGS cercherà un file nominato FLICx.FLC e FLICxFLI (dove x sta per il parametro flic_number) e se ne trova uno, lo riprodurrà.

 Il parametro options ha i seguenti significati:
  • 0: il giocatore non può saltare l'animaizone;
  • 1: il giocatore può premere ESC per saltare l'animazione;
  • 2: il giocatore può premere qualsiasi pulsante o cliccare col mouse per saltare il video;
  • +10 (es.10,11,12): non tendere in altezza e lunghezza, non ripeodurre a schermo intero, riproduci semplicemente alle dimensioni del file flc;
  • +100 non pulire lo schermo prima della riproduzione;
Il gioco andrà in pausa mentre l'animazione viene riprodotta.

Esempio:

PlayFlic(2, 1);

Riprodurrà il file flic2 e il giocatore sarà in grado di saltare l'animazione premendo ESC.

Ragazzi, anche per questa volta è tutto. Ci vedremo in futuro, purtroppo non so quando... il periodo è un pò così. Comunque sia, alla prossima!

sabato 2 gennaio 2016

Un procione in letargo.

Buon anno ragazzi, è da tanto tempo che non ci si sente.

Negli ultimi mesi sono scomparso dalla circolazione, mi scuso con coloro che attendevano nuovi video su AGS.

Il fatto è che in questo ultimo periodo le ore da dedicare al canale si sono ridotte drasticamente e, quando questo succede si vorrebbe utilizzare le ore rimanenti nel migliore dei modi per se stessi.
Mi dispiace dire che sempre più spesso dedicarmi al canale sta diventando un lavoro, un compito, ed è dura destinare le ore di svago ad una cosa che si sente più come un dovere più che un piacere.

Non voglio annoiarvi con tutto il procedimento che uso per girare un video del canale, ma vorrei soltanto sottolineare che è non è una cosa veloce, richiede ore e non si fa in un pomeriggio.
Potreste pensare che sia troppo pignolo, troppe complicazioni per girare un benedetto video da 20 - 30 minuti... Invece è proprio per velocizzare il tutto che prima di mettermi di fronte al microfono, preparo con cura ogni puntata.

Al contrario, le prime le giravo senza scaletta, senza provare prima quello che vi avrei spiegato e quindi via di errori, imprevisti, tagli, parti in mezzo al video che venivano ri-girate, montaggio lunghissimo, durata del video maggiore ma puntata più dipersiva nei contenuti.

Insomma, gli errori che scaturivano dalla scarsa organizzazione rendevano la realizzazione di un video ancor più lunga di quanto lo sia ora, diminuendo anche la qualità del video stesso.

Certamente, il procedimento che uso tutt'ora ha abbassato il numero di ore per creare un video, ma è anche vero che le ore sono ancora troppe per essere qualcosa di piacevole (o almeno non pesante) da fare.

Tra le cose che ho in mente c'è quella di scrivere o su questo blog o farne uno nuovo in Wordpress con un dominio mio.

Fare puntate in forma scritta è certamente più semplice che fare dei video, anche se il video ha il pregio di mostrare quello che viene spiegato in tempo reale, ma dovevo trovare un compromesso.

Dopo questo articolo pubblicherò in forma scritta quello che doveva essere un video (in cantiere da più di due mesi!) sul riprodurre video in AGS, argomento sul quale mi sono arrivare diverse email.

Dopo l'articolo sul riprodurre video vedrò cosa fare (blog o non blog, e il canale?), ma non so dirvi quando avrete una risposta precisa sul futuro dei tutorial di AGS (al momento sono confuso anch'io), scusate.

Non so in quanti leggeranno questo post, ma mi premeva dare una spiegazione un pò più dettagliata a chi mi segue sulla mia assenza da youtube.

Per ora, ci vediamo al prossimo articolo.
Un abbraccio e felice anno nuovo!

mercoledì 8 aprile 2015

Enigma a codice in AGS

Ciao ragazzi,

nel video relativo al ciclo while promisi una puntata su come creare un enigma a codice. Purtroppo però nelle ultime settimane non ho avuto modo di registrarlo.
Avevo però il tutorial pronto da tempo e mi dispiaceva farvi attendere oltre, quindi alla fine ho deciso di farlo in forma scritta così da non dover ancora aspettare il momento giusto per registrare.

Non appena avrò il tempo per farlo, caricherò comnque una versione video di questo tutorial sul canale youtube.

Prima di cominciare, breve premessa per le parti di codice che vedrete.
Le righe in neretto rappresenteranno le nuove righe di codice aggiunte rispetto allo script mostrato in precedenza.

function room_RepExec()

 
}

function room_RepExec()

  if(enigmaRisolto == true)
  {
    Display("Bravo!");
    Display("Bravissimo!");
    Display("Sei un genio!");
    Display("Ma come hai fatto?!");
    Display("Ok basta, non fa ridere...");
  }
}

I tre puntini invece indicheranno che in quel punto è presente del codice che però per quell'esempio non è importante mostrare ancora, questo per non rendere troppo lunghi gli esempi di codice.

function room_RepExec()

  if(enigmaRisolto == true)
  {
    ... //I Display dell'esempio precedente
    
    cEgo.ChangeRoom(2); //Nuova riga di codice
  }
}

Alla fine dell'articolo troverete comunque tutti gli script completi.
Bene ragazzi, cominciamo!

Innanzi tutto, apriamo il nostro progetto in AGS. Può essere un gioco nuovo o esistente nel caso vogliate inserire in esso l'enigma.

Il mio progetto prevede una sola stanza con una porta chiusa elettronicamente. A destra della porta c'è un tastierino numerico sul quale digitare un codice per sbloccarla.
Eccola qua:


Sul tastierino è posto un Hotspot chiamato hTastiera (blu) e sulla porta un altro chiamato hPorta (verde).


Ok, preparata la stanza cominciamo col creare la GUI che rappresenta il tastierino numerico. La chiamerò gTastiera.

Questa GUI contiene i seguenti elementi:

  • lTastiera: una Label per visualizzare il codice man mano che viene digitato dall'utente;
  • bNum0...bNum9: nove pulsanti per i numeri da 0 a 9;
  • bBackSp: un tasto per cancellare una cifra alla volta il codice partendo da destra, esattaamente come il tasto Backspace della tastiera del nostro pc.
  • bEsci: un pulsante per uscire dall'enigma;

Questa GUI contiene il minimo indispensabile per permettere all'utente di provare a risolvere l'enigma e uscire da esso nel caso avesse ancora bisogno di cercare ulteriori elementi per risolverlo.

Impostiamo la proprietà Visibility come Normal, initially off dato che all'inizio del gioco questa GUI non deve essere visibile.


Ora iniziamo a dare vita all'enigma scrivendo il codice per l'Hotspot del tastierino e per gli elementi della GUI.

Dato che questa GUI comparirà quando l'utente interagisce sul tastierino di fianco alla porta, entriamo nell'evento Interact hotspot di hTastiera e inseriamo il codice per rendere visibile gTastiera.

function hTastiera_Interact()
{

  gTastiera.Visible = true;
}

Ok, il tastierino compare, ma ovviamente se proviamo a premere un qualsiasi pulsante nulla accade. Ce ne occuperemo dopo.

Ora è venuto il momento di capire come, dal lato della programmazione, determinare la risoluzione dell'enigma.
La soluzione più semplice (che come accade spesso in programmazione non è comunque l'unica) è quella di confrontare il codice inserito dall'utente con un codice scelto da noi.

Se i due codici combaciano, l'enigma è risolto, la GUI scompare da sola e la porta si sblocca.
Altrimenti non succede nulla, la GUI rimarrà visibile dandoci la possibilità di provare altri codici o di uscire per cercare ulteriori indizi su come risolvere l'enigma.

Per operare questo confronto ci serviremo di due variabili: codiceUtente che conterrà il codice inserito dall'utente, e codiceEnigma conterrà il codice da noi scelto e che l'utente dovrà digitare per andare avanti nel gioco.
Entrambe le variabili saranno di tipo String e saranno globali poichè necessiteranno di essere lette e scritte anche fuori dalla Room.

Potreste chiedervi come mai due variabili che contengono numeri vengano dichiarate String invece che int. Il perchè è presto detto: sebbene entrambe le variabili contengono numeri, con questi numeri non abbiamo bisogno di compiere alcuna operazione aritmetica, avremo invece bisogno di trattarle come "parole".

Potrete infatti utilizzare il metedo che sto per spiegarvi anche per enigmi di codici alfabetici e alfanumerici in quanto in tutti i casi il codice sarà comunque una stringa, un parola.
Inoltre, dichiarare queste variabili come stringhe ci permettarà di utilizzare alcune funzioni che si adattano perfettamente al nostro scopo.

Andiamo quindi in Global variables e creiamo le due variabili. Per creare una variabile globale basterà cliccare sulla tabella (vuota, se il gioco è nuovo) delle variabli globali e selezionare Add new variable....

La prima variabile avrà come nome codiceUtente, sarà di tipo String e non avrà alcun valore predefinito, sarà vuota poichè popolata mano a mano che il giocatore digita il codice.



La seconda variabile avrà come nome codiceEnigma, anch'essa di tipo String e avrà come valore predefino un codice numerico di quattro cifre, nel mio caso 7514.




Volendo, potreste anche avere un codice più corto o più lungo di quattro cifre. Tuttavia per ora sarebbe meglio che vi atteniate al mio esempio. Una volta capito il meccanismo, poi, potrete modificare i miei script per adattarli al vostro enigma.



Bene, ora è venuto il momento di rendere funzionali i tasti numerici di gTastiera.
Ogni tasto permetterà l'inserimento di una cifra che andrà a comporre il codice di quattro cifre.

Quando un tasto numerico verrà premuto, la cifra corrispondente verrà visulaizzata in lTastiera, che fungerà da display del nostro tastierino numerico.

Facciamo quindi doppio click sul tasto numero del numero 1 (bNum1) per entrare nel suo evento OnClick.

function bNum1_OnClick(GUIControl *control, MouseButton button)
{

}

Riepilogando, abbiamo la variabile codiceUtente che conterrà il codice inserito dal giocatore, variabile che verrà popolata man mano che l'utente digita il codice.
Ad ogni pressione di un tasto numerico, dunque, viene aggiunta una cifra a codiceUtente.

Le stringhe in AGS dispongono di una funzione che fa proprio al caso nostro, trattasi della funzione Append.

Per aggiungere quindi la cifra corrispondente al numero del tasto scriviamo:

function bNum1_OnClick(GUIControl *control, MouseButton button)
{

  codiceUtente = codiceUtente.Append("1");
}

Ovvero, codiceUtente viene impostata come codiceUtente con l'aggiunta del numero 1.
Se bNum1 sarà premuto per immettere la prima cifra del codice, codiceUtente (che in quel caso sarà vuota), diventerà 1.
Se bNum1 sarà premuto per immettere la terza cifra del codice (avendo precedentemente premuto bNum0 e bNum6), codiceUtente diventerà 061.

Quindi Append aggiunge una cifra in coda a quelle precedenti.

Ok, ora la variabile codiceUtente comincia ad essere popolata una cifra per volta, ma come visualizzare questo codice in tempo reale man mano che viene digitato?

Basterà impostare la proprietà Text di lTastiera come uguale a codiceUtente.

function bNum1_OnClick(GUIControl *control, MouseButton button)
{

  codiceUtente = codiceUtente.Append("1");
  lTastiera.Text = codiceUtente;
}

Facciamo lo stesso per tutti gli altri tasti numerici cambiando soltanto la stringa tra le parentesi della funzione Append in modo che rispecchi il numero sul tasto premuto.
So che è un pò tedioso ripetere le stesse righe di codice per altre nove volte, ma per ora non ho trovato soluzione migliore.

Bene, i tasti numerici ora funzionano e  possiamo vedere le cifre comparire sul display del nostro tastierino.


Veniamo al tasto bBackSp. Per cancellare una cifra per volta partendo da destra ci avvarremo di una funzione simile ad Append chiamata Truncate.
Truncate chiede come argomento un numero intero che rappresenta la lunghezza della stringa una volta operata la troncatura.
Ad esempio il codice:

String colore = "Verde Smeraldo";
colore.Truncate(3);
Display("%s", colore);

ci farà vedere a video la stringa "Ver", ovvero le prime 3 lettere della stringa "Verde Smeraldo".
La stringa colore è stata quindi troncata eliminando i caratteri dal quarto in giù.

Entriamo quindi nell'evento OnClick di bBackSp.

function bBackSp_OnClick(GUIControl *control, MouseButton button)
{
 
}

E inseriamo:

function bBackSp_OnClick(GUIControl *control, MouseButton button)
{
  codiceUtente = codiceUtente.Truncate(codiceUtente.Length - 1);
  lTastiera.Text = codiceUtente;

}

Quando il giocatore preme bBackSp codiceUtente viene troncato lasciando un numero di caratteri uguale alla lunghezza di codiceUtente meno 1:

codiceUtente.Length - 1

Se codiceUtente in quel momento è lungo 4 (ad esempio, 0567), premendo bBackSp esso viene troncato in modo da lasciare solo le prime 3 cifre (4 - 1 = 3, ovvero 056) e così via se bBackSp viene premuto più volte.
Dopodichè mostriamo sul display del nostro tastierino il codice troncato aggiornando la proprietà Text di lTastiera.

Ok, tutto bello ma... ci sono dei bug!
Il codice di sicurezza è composto da 4 cifre, ma nulla vieta al giocatore di immetterne di più, anche 1000!



Inoltre, il giocatore può premere quante volte vuole il tasto Backspace anche quando si sono già cancellate tutte le cifre del codice, cosa che porta a un errore in AGS, in quanto la funzione Truncate non può troncare una stringa che è già di per sè lunga zero cifre.


Dobbiamo quindi disabilitare i tasti numerici quando la lunghezza del codice digitato (codiceUtente) arriva a 4 cifre e abilitare il tasto Backspace solo se codiceUtente è di almeno una cifra.

Dato che questo controllo deve essere operato in tempo reale - ovvero AGS deve costantemente tenere d'occhio la lunghezza di codiceUtente per capire se abilitare o disabilitare i tasti - questo controllo deve avvenire nell'evento Repeatedly execute della stanza in cui si trova l'enigma.

Andiamo quindi in Repeatedly execute della stanza.

function room_RepExec()
{


}

Cominciamo con il tasto bBackSp. Come detto poco fa, questo tasto deve essere abilitato solo se il codice digitato è di almeno una cifra. Il che tradotto in codice è:

function room_RepExec()

  if(codiceUtente.Length > 0)
  {
    bBackSp.Enabled = true;
  }
  else
  {
    bBackSp.Enabled = false;
  }
}

Come ricorderete, codiceUtente.Length altro non è che la lunghezza in caratteri del codice immesso dal giocatore.
Adesso quando il codice digitato è di 0 cifre il tasto Backspace verrà disabilitato, cosa che riconosceremo dal fatto che il tasto sarà di un grigio più chiaro.

Backspace abilitato

Backspace disabilitato

Ora veniamo ai tasti numerici. Anche qui, ricordando quando detto prima, se la lunghezza del codice arriva a 4 cifre, questi tasti verranno disabilitati. In altre parole i tasti devono essere abilitati solo se la lunghezza di codiceUtente è inferiore a 4.

Il che in codice diventa:

function room_RepExec()

  ...

  if(codiceUtente.Length < 4)
  {
    //Abilito i tasti
  }
  else
  {
    //Disabilito i tasti
  }

}

Ho omesso di proposito il codice per abilitare e disabilitare i tasti perchè devo fare una premessa.
I tasti, come avete visto con bBackSp si abilitano e disabilitano mediante la proprietà Enabled.
bBackSp è un tasto solo, quindi sia per abilitarlo che per disabilitarlo ce la siamo cavati con una riga di codice.

La faccenda è diversa se dobbiamo abilitare o disabilitare dieci tasti numerici. Dovremmo scrivere dieci righe sia per abilitarli tutti che viceversa.

function room_RepExec()

  ...

  if(codiceUtente.Length < 4)
  {
    bNum0.Enabled = true;
    bNum1.Enabled = true;
    bNum2.Enabled = true;
    bNum3.Enabled = true;
    bNum4.Enabled = true;
    bNum5.Enabled = true;
    bNum6.Enabled = true;
    bNum7.Enabled = true;
    bNum8.Enabled = true;
    bNum9.Enabled = true;
  }
  else
  {
    bNum0.Enabled = false;
    bNum1.Enabled = false;
    bNum2.Enabled = false;
    bNum3.Enabled = false;
    bNum4.Enabled = false;
    bNum5.Enabled = false;
    bNum6.Enabled = false;
    bNum7.Enabled = false;
    bNum8.Enabled = false;
    bNum9.Enabled = false;
  }


È leggitimo procedere in questo modo e non c'è nulla di sbagliato, ma c'è un modo per accorciare notevolmente il codice.

Vi ricordate gli array e il ciclo while? Ecco una situazione in cui possiamo utilizzarli.

Tutti i controlli inseriti in una determinata GUI possono essere richiamati sia nel modo classico, ovvero scrivendo il loro nome (bNum5, bEsci, lTastiera e così via), sia richiamando l'array Controls abbinato a quella GUI.

Nel nostro caso, i controlli della GUI gTastiera possono essere raggiunti attraverso l'array Controls di gTastiera, richiamabile scrivendo gTastiera.Controls.

In questo array ogni cella è occupata da un controllo della GUI. L'indirizzo a cui e situato ogni controllo è dato dalla proprietà ID del controllo stesso.

Ad esempio, bNum7 ha un ID pari a 6. Quindi nella cella numero 6 dell'array Controls di gTastiera troviamo bNum7.
In codice potremmo dire che in gTastiera.Controls[6] troviamo bNum7.


Gli ID vengono dati in base all'ordine in cui i controlli vengono da noi inseriti nella GUI e partono da 0, per cui se avrò inserito per primo bNum1 e poi bNum2, essi avranno rispettivamente come ID 0 e 1.

Nel mio caso i tasti numerici sono stati i primi elementi ad essere inseriti nella GUI dunque nell'array Controls occupano le celle da 0 a 9.

Il ciclo while per abilitarli e disabilitarli a questo punto dovrebbe apparirvi sensato:

function room_RepExec()

  ...

  if(codiceUtente.Length < 4)
  {
    int i = 0;

    while(i <= 9)
    {
      gTastiera.Controls[i].Enabled = true;
      i++;

    }
  }
  else
  {   

    int i = 0;
    while(i <= 9)
    {
      gTastiera.Controls[i].Enabled = false;
      i++;

    }
  }
}

La variabile i inizializzata a 0 e incrementata a ogni giro del ciclo permette di riferirci giro dopo giro ad una cella specifica dell'array.

Adesso i tasti del tastierino numerico si bloccheranno quando il codice digitato arriva a una lunghezza di 4 cifre.

Tasti numerici abilitati

Raggiunte 4 cifre: tasti numerici disabilitati
Bug risolti!

Occupiamoci del tasto Esci che ci farà uscire dall'enigma, rimuovendo la GUI dallo schermo.
Entriamo nell'evento OnClick di bEsci e inseriamo gTastiera.Visible = false.

function bEsci_OnClick(GUIControl *control, MouseButton button)
{
  gTastiera.Visible = false;
}

La GUI verrà così rimossa dallo schermo. C'è però un piccolo bug.
Mettiamo che, tentando di risolvere l'enigma immetta un paio di cifre. Poi, non riuscendo a risolverlo, esca dalla GUI senza cancellare alcuna cifra.
Poco dopo voglio provare nuovamente a risolverlo e interagisco di nuovo con il tastierino di fianco alla porta richiamando così la GUI.
Noterete che le cifre immesse dall'ultimo tentativo sono ancora lì, sulla Label della GUI.
Questo perchè precedentemente avevamo impostato lTasitera in modo che mostrasse il valore di codiceUtente.

Al nuovo richiamo di gTastiera, la Label lTastiera continuerà giustamente a mostrare il valore di codiceUtente che è quello da noi assegnatoli nel tentativo precedente di risolvere l'enigma.

Quindi, una volta premuto bEsci dobbiamo, oltre a rimuovere la GUI, svuotare la variabile codiceUtente e aggiornare la proprietà Text di lTastiera in modo che rispecchi il valore aggiornato di codiceUtente, ovvero vuoto.

function bEsci_OnClick(GUIControl *control, MouseButton button)
{
  gTastiera.Visible = false;
  codiceUtente = "";
  lTastiera.Text = codiceUtente;

}

Ad ogni nuovo tentativo di risolvere l'enigma, lTastiera sarà pulita.

Ora, occupiamoci di confrontare il codice immesso dal giocatore con quello da noi scelto per determinare la risoluzione dell'enigma.
A tal proposito possiamo sfruttare l'if che abilita e disabilita i tasti numerici scritto in precedenza.

Se vi ricordate, i tasti numerici vengono disabilitati quando codiceUtente raggiunge i 4 caratteri di lunghezza e questo è anche il momento migliore per confrontare il codice immesso dal giocatore con quello che abbiamo scelto noi.

Annidiamo in questo braccio dell'if il seguente if:

function room_RepExec()

  ...

  if(codiceUtente.Length < 4)
  {
    ...
  }
  else
  {   

    int i = 0;
    while(i <= 9)
    {
      gTastiera.Controls[i].Enabled = false;
      i++;

    }
    
     if(codiceUtente == codiceEnigma)
     {
       Display("Porta sbloccata");         
       codiceUtente = "";
       gTastiera.Visible = false;
     }

  }
}

Con questo if chiediamo se il codice immesso dall'utente è lo stesso scelto da noi programmatori per l'enigma.
Se la risposta è si, ovvero se la condizione risulta true mostreremo la scritta "Porta sbloccata", svuoteremo la variablie codiceUtente e faremo scomparire la GUI del tastierino.


Svuotare la variabile codiceUtente, anche se a prima vista non si direbbe, è molto importante. Perchè? Perchè se non lo facessimo essa continuerebbe sia ad avere una lunghezza uguale a 4 - facendoci così entrare nell'else del if - sia ad essere uguale a codiceEnigma, facendoci entrare addirittura nell'if annidato ed eseguendo le righe al suo interno.
Cosa che causerebbe il continuo riproponimento della scritta "Porta sbloccata" nel mio caso.

C'è un ultimo aspetto da prendere in considerazione.

Anche dopo la risoluzione dell'enigma, qualora il giocatore cliccasse sull'Hotspot del tastierino, la GUI gTatstiera comparirebbe nuovamente, cosa che non ci serve più ora che l'enigma è stato risolto.

Per risolvere questo problema basterà creare una variabile booleana che nel mio casao sarà enigmaRisolto e che avrà il valore iniziale di false dato che l'enigma all'inizio del gioco non è risolto.
Quando l'utente clicca sull'Hotspot del tastierino verrà eseguito un if che controllerà il valore di enigmaRisolto.
Se essa è true, ovvero l'enigma è già stato risolto, faremo dire a Ego la frase: <<Ho già sbloccato la porta!>>. Se invece è false mostreremo la GUI dando la possibilità all'utente di rsolvere l'enigma.

Non è necessario dichiararla globale, in quanto essa verrà solamente utilizzata nella Room dove si trova anche l'enigma.

Dichiariamo quindi questa variabile alla prima riga della pagina degli script della Room in modo da renderla accessibile a tutti gli eventi della pagina.

bool enigmaRisolto = false;

Ora facciamola entrare in azione. Il suo valore cambierà in true nel momento in cui l'enigma sarà risolto.
Andiamo allora nell'if che confronta codiceUtente con codiceEnigma e iseriamo il cambio di valore di enigmaRisolto.

function room_RepExec()

  ...

  if(codiceUtente.Length < 4)
  {
    ...
  }
  else
  {   

    int i = 0;
    while(i <= 9)
    {
      gTastiera.Controls[i].Enabled = false;
      i++;

    }
    
     if(codiceUtente == codiceEnigma)
     {
       Display("Porta sbloccata");

       enigmaRisolto = true;         
       codiceUtente = "";
       gTastiera.Visible = false;
     }

  }
}

Inseriamo infine un if che controlla il valore di questa variabile all'interno dell'evento Interact di hTastiera per dare o meno l'accesso all'enigma come spiegato in precedenza.

function hTastiera_Interact()
{

  if(enigmaRsolto == false)
  {   
    gTastiera.Visible = true;
  }
  else
  {
    cEgo.Say("Ho già risolto l'enigma!");
  }
}

Possiamo anche sfruttare engimaRisolto con l'Hotspot sulla porta blindata (hPorta) per permettere o meno il cambio di stanza.
Con un if possiamo controllare il valore di questa variabile. Se è false Ego dirà: <<La porta sembra bloccata elettronicamente.>>, viceversa andremo avanti col gioco (ad esempio un cambio di stanza o l'inizio di una cutscene).

function hPorta_Interact()
{
  if(enigmaRisolto == false)
  {
    cEgo.Say("La porta sembra bloccata elettronicamente.");
  }
  else
  {
    cEgo.ChangeRoom(2);
  }
}

Bene ragazzi, il succo del tutorial è questo, spero vi sia piaciuto e che vi sia stato utile.
Da qui in poi potrete modificare, espandere e rendere anche più complesso l'enigma.

Come già detto più volte, potete anche utilizzare questo metodo per un codice alfabetico o alfanumerico.
Basterà scegliere un valore diverso per codiceEnigma, creare una GUI con tasti sia numerici che alfabetici e adattare l'if e i cicli while che controllano l'abilitazione/disabilitazione dei tasti, nonchè quello che confronta codiceUtente con codiceEnigma.

Ecco gli script completi.

Script della stanza:

bool enigmaRisolto = false;

function hTastiera_Interact()
{
  if(enigmaRisolto == false)
  {
    gTastiera.Visible = true;
  }
  else
  {
    cEgo.Say("Ho gia' risolto l'enigma!");
  }
}

function hPorta_Interact()
{
  if(enigmaRisolto == false)
  {
    cEgo.Say("La porta sembra bloccata elettronicamente.");
  }
  else
  {
   cEgo.ChangeRoom(2);
  }
}

function room_RepExec()
{
  if(codiceUtente.Length > 0)
  {
    bBackSp.Enabled = true;
  }
  else
  {
    bBackSp.Enabled = false;
  }

  if(codiceUtente.Length < 4)
  {
    int i = 0;
    while(i <= 9)
    { 
      gTastiera.Controls[i].Enabled = true;
      i++;
    }
  }
  else
  {
    int i = 0;
    while(i <= 9)
    {
      gTastiera.Controls[i].Enabled = false;
      i++;
    }
   
    if(codiceUtente == codiceEnigma)
    {
      Display("Porta sbloccata");
      enigmaRisolto = true;
      codiceUtente = "";
      gTastiera.Visible = false;
    }
  } 
}

Script della GUI:

function bNum1_OnClick(GUIControl *control, MouseButton button)
{
  codiceUtente = codiceUtente.Append("1");
  lTastiera.Text = codiceUtente;
}

function bNum2_OnClick(GUIControl *control, MouseButton button)
{
  codiceUtente = codiceUtente.Append("2");
  lTastiera.Text = codiceUtente;
}

function bNum3_OnClick(GUIControl *control, MouseButton button)
{
  codiceUtente = codiceUtente.Append("3");
  lTastiera.Text = codiceUtente;
}

function bNum4_OnClick(GUIControl *control, MouseButton button)
{
  codiceUtente = codiceUtente.Append("4");
  lTastiera.Text = codiceUtente;
}

function bNum5_OnClick(GUIControl *control, MouseButton button)
{
  codiceUtente = codiceUtente.Append("5");
  lTastiera.Text = codiceUtente;
}

function bNum6_OnClick(GUIControl *control, MouseButton button)
{
  codiceUtente = codiceUtente.Append("6");
  lTastiera.Text = codiceUtente;
}

function bNum7_OnClick(GUIControl *control, MouseButton button)
{
  codiceUtente = codiceUtente.Append("7");
  lTastiera.Text = codiceUtente;
}

function bNum8_OnClick(GUIControl *control, MouseButton button)
{
  codiceUtente = codiceUtente.Append("8");
  lTastiera.Text = codiceUtente;
}

function bNum9_OnClick(GUIControl *control, MouseButton button)
{
  codiceUtente = codiceUtente.Append("9");
  lTastiera.Text = codiceUtente;
}

function bNum0_OnClick(GUIControl *control, MouseButton button)
{
  codiceUtente = codiceUtente.Append("0");
  lTastiera.Text = codiceUtente;
}

function bBackSp_OnClick(GUIControl *control, MouseButton button)
{
  codiceUtente = codiceUtente.Truncate(codiceUtente.Length - 1);
  lTastiera.Text = codiceUtente;
}

function bEsci_OnClick(GUIControl *control, MouseButton button)
{
  gTastiera.Visible = false;
  codiceUtente = "";
  lTastiera.Text = codiceUtente;
}

Se avete domande scrivetemi sotto questo articolo, sotto il video da cui questo articolo è linkato o al mio indirizzo email: triventrive@gmail.com.
Vi ringrazio per essere arrivati fin qui e non mi dilungo oltre.

A presto!

domenica 29 dicembre 2013

Le Conversazioni in Adventure Game Studio

Ben tronati cari utenti, è da un pò di giorni che devo fare un video sui dialoghi in AGS, arogmento di cui io stesso, lo ammetto, sapevo poco.

Dopo aver appreso le regole per scrivere un dialogo in AGS grazie a questo, questo, questo e questo video di densming ho pensato di fare finalmente questo benedetto video.
Però, ecco che una serie di problemi si presenta: come faccio a scrivere un dialogo che racchiuda tutti i concetti che voglio spiegare, un esempio "maestro"? E come spiegare questi concetti in modo succinto senza ingarbugliarmi, rendendo tutto ben chiaro anche a coloro che guardano il video?

Così ho deciso, per risparmiare a voi ulteriori attese e spiegazioni prolisse, e a me pomeriggi persi a imprecare contro il microfono cercando di dare ad una frase il senso voluto, di racchiudere (come ho già fatto con gli articoli su Blender) la teoria in un post scritto e di usare i video per gli esempi pratici.

E quale miglior spiegazione teorica di quella che potete trovare nel manuale integrato in AGS?
Per questo ho deciso di tradurre in italiano la parte relativa a questo argomento. In - praticamente - una pagina trovate tutte le regole per scrivere i vostri dialoghi e far reagire il gioco secondo i dettami che avete stabilito.
Se avessi dovuto fare un video con quello che trovate in questa pagina, probabilmente sarebbe durato un'ora circa.

Dopo questo articolo, farò sì anche dei video, ma essi saranno di esempi pratici, scevri da tutta la teoria che avrebbe fatto inevitabilmente lievitare la durata di essi.

La pagina è pressochè formattata nello stesso modo di quella che trovate nel manuale, anche se in un paio di occasioni ho aggiunto una breve nota laddove la traduzione dall'inglese all'italiano lasciava spazio a fraintendimenti.
Per scaricarla, cliccate sul link alla fine dell'articolo.
Il suo scopo è quello di essere propedeutica per i prossimi video.

Le Conversazioni in Adventure Game Studio

lunedì 11 febbraio 2013

Make Human: un programma utilissimo.

Bentornati, in questo articolo vorrei parlarvi di Make Human un programma Open Source gratuito che ci permette di creare modelli 3D di corpi umani altamente personalizzati.

Premetto che lo conosco piuttosto superficialmente in quanto lo uso principalmente per creare modelli a cui "scatterò" delle foto delle quali mi servirò per modellare i miei personaggi. Pertanto non esplorerò tutte le sue funzioni.

Ma è bene che ve lo introduca perchè ci sarà utile nei prossimi articoli dedicati alla modellazione e animazione del personaggio per il gioco Adventure Game Studio.

Make Human si può scaricare dal sito ufficiale alla pagina release.


Nella pagina sono presenti le versioni per diversi sistemi operativi. Avendo Windows, utilizzerò la vesione per questo sistema operativo.


La cosa interessante, è che (come potete vedere andando alla pagina Make Human Mesh License) le Mesh create con questo programma hanno un licenza Creative Commons CC0. Possono quindi, tra le altre cose, essere utilizzate per progetti commerciali e non, senza chiedere il permesso all'autore del programma.


Si tratta insomma della licenza più libera che ci sia.
Vi invito comunque a dare un'occhiata alla pagina in questione per maggiori informazioni.

Una volta scaricato e installato Make Human, avviamolo.

Ecco che vediamo un modello standard.

Col Tasto Destro del mouse possiamo ruotare a piacimento il modello, scorrendo la rotella, o tenendola premuta e muovendo il mouse avanti e indietro possiamo zoomare nelle due direzioni, mentre con il Tasto Sinistro sposteremo il modello lungo la finestra.

Il modello, per ora ha tutti i valori impostati a metà.


In esso è al 50% maschio e 50% femmina, avrà un'età media, sarà di altezza media, ecc...

Agendo sui vari parametri ci sarà possibile decidere appunto:

- il sesso;
- l'età;
- il tono muscolare;
- il peso;
- l'altezza;
- L'etina (possiamo anche mescolare le etnie in diverse misure).



Nella scheda Torso potremo agire sul busto.


Anche qui le possibilità sono varie. Ad esempio scorrendo veso destra o sinistra il quadrato di ciascuna opzione possiamo decidere:


- lo spessore del busto;
- la sua larghezza;
- la sua ampiezza in altezza (per utilizzare una terminologia alla Blender, quanto esso è scalato lungo l'asse Z).
- quanto è spostato verso destra o sinistra;
- quanto è spostato verso l'alto o verso il basso;
- quanto è spostato in avanti o indietro.


Nella finestra a Category a destra, potremo apportare modifiche anche ai fianchi (Hip) al bacino (Pelvistone), allo stomaco (Stomach) e al fondoschiena (Buttocks).


Nella scheda Face, come potete immaginare modificheremo il viso.


Potremo modificare la forma di esso, anche mischiando più forme insieme in diverse percentuali.

Inoltre, sempre spostandoci nella finestra Category avremo addirittra la facoltà di cambiare ogni minimo connotato del viso nei modi più disparati. Modi che ci verranno mostrati nella finetra a sinistra.

Quest'ultima cambierà ogni volta che selezioniamo il connotato da modificare.


In Arms and Legs potremo modificare in svariati modi anche mani, piedi, braccia e gambe.


Capite quindi quante opzioni ci siano e quanto sia personalizzabile ogni modello.

E non è finita qui, ce ne sono altre che putroppo non conosco molto e che se elencate farebbero di questo articolo un lungo manuale del programma!

Per citarne alcune, si va dal colore della pelle, alle espressioni facciali, più qualche capigliatura, vestito e altro ancora.
Alcune di queste le potete trovare nella cartella superiore Library, sottoscheda Expressions per quanto riguarda le espressioni facciali.


Comunque, una volta deciso come dev'essere il nostro modello, potremo anche importarlo in Blender, completo di una struttura (chiamata anche "Armatura" o in inglese "Rig") sulla quale possiamo agire per fargli assumere diverse posizioni.

Per esportarlo, spostiamoci nella cartella superiore Files e nella sottoscheda Export.


Nella tabella Format selezioniamo Blender Exchange (mhx), lasciamo tutto com'è, diamo un nome al modello e clicchiamo su Export.


Una finesta ci avviserà che il modello è stato salvato all'interno della cartella Documenti/makehuman/exports.

Ora apriamo Blender e dal menù a tendina in basso a sinistra, invece che 3D View selezioniamo User Preferences.


Dopodichè entriamo nella scheda Addons e nel campo di ricerca a sinistra digitiamo "makehuman" (anche solo "make" andrà bene) per cercare e attivare l'Addon che ci permetterà di importare i modelli creati con Make Human.


Attiviamo l'Addon spuntando la casella.


Ora ritorniamo nelle 3D View selezionandola dallo stesso menù a tendina dal quale prima abbiamo selezionato User Preferences e cancelliamo il Cubo.

Clicchiamo su File, Import, MakeHuman(.mhx).


Selezioniamo il modello da caricare e infine clicchiamo su Import MHX.


Ecco che il modello verrà caricato in tutta la sue imponenza (in effetti è bello alto!) completo di un utilissima Armatura per metterlo in varie posizioni e animarlo (vicino alla faccia c'è n'è anche una per i muscoli del volto).


Ora il modello (o meglio l'Armatura) è in Pose Mode, modalità particolare delle Armature che ci permette di muovere la struttura e vederne gli effetti sul modello.

Ad esempio, se selezioniamo la parte del'Armatura dedicata alla coscia sinistra (parte colorata di giallo)...


... e andiamo in vista ortogonale laterale destra, premendo R potremo ruotare la coscia. Di conseguenza verranno anche ruotati il polpaccio e il piede.


Potremo agire in questo modo con tutte le altre parti del corpo.


Abbiamo insomma un modello completamente snodabile che possiamo utilizzare in tantissimi modi.

Ad esempio, possiamo animarlo facendolo camminare (cosa che spiegherò nella prossima serie di video), salvare poi i vari frame della camminata e usarli come riferimento in Gimp, disegnandoci sopra (con un livello trasparente) il personaggio, frame per frame, per poi importare questi ultimi in Adventure Game Studio.

Oppure, dato che Make Human ci permette anche di vestire il nostro personaggio e, con un Addon per Blender chiamato Make Clothes di salvare gli abiti, potremo anche vestirlo, importarlo in Blender, animarlo, salvare i frame e importarli direttamente in AGS. Il tutto senza dover modellare di persona il personaggio.

Ora, purtroppo non conosco bene il discorso vesititi in Make Human, ma in rete ci sono tutte le spiegazioni.

Come ho detto all'inizio, questo programma lo uso più che altro per creare i cosiddetti Model Sheet (purtroppo non ho trovato un corrispettivo nella nostra lingua) con modelli "nudi".

I Model Sheet non sono altro che immagini con il modello ripeso da diverse angolazioni che poi verranno utilizzate come riferimento per modellare il personaggio vero e proprio.

Questo è un Model Sheet molto semplice fatto con un modello di Make Human:


Come vedete, una volta renderizzato, noteremo che il modello fatto con Make Human è anche già fornito di textures.

Bene spero che questo articolo vi abbia fornito qualche spunto per la soluzione al problema del disegno dei frame di animazione del personaggio.

Se non lo avesse fatto, non preoccupatevi, nei prossimi video (preferisco mostrarlo in video che in forma scritta, altrimenti verrebbero articoli troppo lunghi) illustrerò il procedimento che uso per modellare un personaggio, applicargli un'Armatura fornitaci da Blender, animarlo e salvare i frame di animazione da importare in AGS.

Alla prossima!