La gestione delle finestre grafiche

La libreria bi-piattaforma offre una serie di punti di ingresso (entry point) che rendono la gestione delle finestre un po' più facile. Un punto di ingresso, come abbiamo visto prima, è una specie di routine opzionale che i programmatori possono inserire nel loro codice sorgente. Vedete, molto spesso gli scrittori di librerie arrivano ad un punto nel codice della libreria nel quale sospettano che i programmatori potrebbero voler eseguire delle istruzioni personalizzate, oppure no. Così lo scrittore di librerie crea una routine vuota che non fa nulla, ed inserisce la chiamata alla routine in quel punto. Ora il programmatore può scrivere una routine con lo stesso nome, e quando la libreria arriva a quel punto, esegue il codice della routine del programmatore del gioco, che fa qualcosa. O il programmatore del gioco può lasciar perdere, e quando la libreria arriva a quel punto esegue la routine vuota e continua tranquillamente per la sua strada.

Così se non state creando nessuna finestra speciale, o nessun canale sonoro, o nessun riferimento a file, o nessun'altra cosa simile, non dovete preoccuparvi di nessuno dei punti di ingresso che seguono. La libreria si occuperà di gg_mainwin e gg_statuwin per voi, così come di ogni file di salvataggio di gioco che generate. Ma se create i vostri oggetti Glk, come una finestra grafica, è vostra responsabilità tenerne conto. Qui scopriamo come.

Prima di tutto dovete creare una routine per il punto di ingresso che viene chiamato dopo ogni restart, restore e undo. Questa è IdentifyGlkObject(). La ragione per la quale dovete avere una routine IdentifyGlkObject se create le vostre finestre grafiche è che dopo ogni restart, restore o undo, le variabili globali che puntano a queste finestre possono contenere valori errati, e le cose possono diventare brutte se non le azzerate.

La routine IdentifyGlkObject() viene chiamata, in realtà, tre volte, inviando un numero differente alla variabile locale "phase" (fase) ogni volta: prima 0, poi 1, infine 2. In phase 0, il vostro compito è quello di impostare tutte le vostre variabili di puntamento ai vostri oggetti Glk a 0. In phase 1, dovete resettare tutte le vostre finestre, i vostri stream ed i vostri riferimenti ai file. In phase 2 resettate tutti gli altri oggetti che avete creato. La phase 2 è anche quella nella quale dovete aggiornare le vostre finestre per mostrare le immagini giuste, i vostri canali sonori per suonare la musica giusta, e così di seguito.

Così continuando con gli esempi delle sezioni precedenti, diciamo che abbiamo una finestra grafica, gg_picwin, nella quale mostriamo un'immagine della stanza nella quale il personaggio del giocatore si trova. Ma, diversamente dalle sezioni precedenti, non disegnamo l'immagine sulla finestra con comandi come glk_image_draw(gg_picwin, Cucina_pic, 0, 0). Invece creiamo una variabile globale chiamata "current_pic" (immagine corrente), ed una routine che somiglia a questa:

   [ MyRedrawGraphicsWindows;
      glk_image_draw(gg_picwin, current_pic, 0, 0);
   ];

Ora, quando scriviamo la routine che sposta il giocatore dalla sala da pranzo a sud, in cucina, dobbiamo scrivere una cosa così:

   s_to [;
      current_pic = Cucina_pic;
      MyRedrawGraphicsWindows();
      PlayerTo(Cucina);
   ],

Ed alla fine inseriamo il nostro punto d'ingresso IdentifyGlkObject():

   [ IdentifyGlkObject phase type ref rock;
      if (phase == 0) { ! Si azzerano i riferimenti a tutti i nostri oggetti.
         gg_picwin = 0;
         return;
      }

      if (phase == 1) { ! Resettiamo le nostre finestre, stream, e rif. ai file.
         switch (type) {
            0: ! È una finestra.
               switch (rock) {
                  GG_PICWIN_ROCK: gg_picwin = ref;
               }
            1: ! È uno stream.
                  ! Ma non ne abbiamo creato nessuno.
            2: ! È un riferimento a file.
                  ! Ma non ne abbiamo creato nessuno.
         }
         return;
      }

      if (phase == 2) { ! Aggiorniamo i nostri oggetti.
         MyRedrawGraphicsWindows();
      }
   ];

Che cosa abbiamo fatto? All'inizio abbiamo preso la variabile globale per la finestra grafica che abbiamo creato in precedenza (che adesso è riempita con dati sporchi, grazie al reset) e l'abbiamo impostata a 0 -- è la phase 0. Poi la libreria ha trovato questo costrutto, non l'ha riconosciuto, e così ce lo ha passato per determinare che cosa è. Noi abbiamo detto "È il 'rock value' per questo oggetto (che non si è perso durante il reset) è lo stesso del rock value che abbiamo impostato per la nostra finestra grafica, quindi deve essere la nostra finestra grafica! Perciò faremo puntare nuovamente la variabile della finestra grafica a questa cosa. È la phase 1. Poi nella phase 2 abbiamo fatto un aggiornamento della visualizzazione: visto che adesso sappiamo dove inviare l'immagine che deve essere visualizzata a questo punto, possiamo mostrarla.

La finestra grafica, adesso, dovrebbe reagire correttamente ai vari restore, undo e cose simili: se il giocatore sposta il proprio personaggio nella cucina, poi digita "UNDO", non solo il personaggio tornerà nella sala da pranzo, ma la finestra sostituirà l'immagine della cucina con l'immagine della sala da pranzo.

E per quanto riguarda gli eventi esterni che influiscono sul gioco? Ad esempio, il giocatore potrebbe ridimensionare la finestra di Glulxe, o cambiare la risoluzione del monitor -- come affrontare la cosa? Risposta: la libreria ha un ciclo che tiene traccia di queste cose, e questo ciclo fornisce un punto d'ingresso, chiamato HandleGlkEvent(). HandleGlkEvent() richiede due argomenti: "ev" è un array che contiene le informazioni su quello che è appena successo (una finestra ridimensionata? Un click del mouse? La fine di un effetto sonoro?), e "context" (contesto) che vale 0 se la cosa è accaduta durante l'input di una riga (come nei normali comandi o in un prompt YesOrNo() -- ogni volta che il programma deve attendere che il giocatore prema Invio prima di poter rispondere all'input) o 1 se l'evento è avvenuto durante l'input di un carattere (come nei menu, dove il gioco risponde ad ogni pressione di tasto). Ad esempio possiamo gestire la cosa con un routine molto piccola:

   [ HandleGlkEvent ev context;
      context = 0; ! sopprime gli avvertimenti ignorati
      switch (ev-->0) {
         evtype_Redraw, evtype_Arrange:
            MyRedrawGraphicsWindows();
      }
   ];

Questo codice si riduce essenzialmente a "se accade qualcosa che richiede il ridisegno delle finestre grafiche, vai avanti a ridisegnarle usando le istruzioni che abbiamo impartito in precedenza".

E questo è tutto quello di cui avete bisogno per rendere le vostre finestre grafiche robuste abbastanza da gestire la maggior parte degli avvenimenti che possono accadere.


Prossima sezione: Problemi con la grafica
O ritorna al sommario