ROBOTICA

video
immagini
papers
progettati
costruiti
toolbox
vrml
simulatori

links

 

Papers
meccanica
sistemi
documents

Capitolo 25° Vrmlscript Capitolo 27°

Siamo ormai giunti al termine del tutorial. Ma abbiamo ancora un problema da risolvere. L’interruttore dell’esempio precedente non funziona a dovere.

Sono innumerevoli i problemi che possiamo incontrare che non sono risolvibili con i mezzi che conosciamo. Fino ad ora abbiamo fatto leva sui meccanismi di animazione offerti da vrml. Esiste un meccanismo molto flessibile per programmare comportamenti anche molto complessi. Questo meccanismo consiste nella possibilità di inserire script all’interno del nostro mondo.

Parlando di scripting ci si deve avvicinare ai concetti soliti della programmazione tradizionale. Non faremo qui un’analisi dettagliata di vrmlscript. Ci fermeremo ad una sua descrizione sommaria e tale da consentirci di risolvere il problema precedente e altri simili.

Vrmlscript è un linguaggio di scripting molto simile agli script java.
Per definire uno script in vrml dobbiamo fare ricorso al nod Script. Analizziamone la struttura.

Script {
  ...
  eventIn tipo nomecampo
  eventOut tipo nomecampo
  field tipo nomecampo   initial value
  exposedField url [ ]
}

Ho tralasciato alcune parti. I primi 3 campi riportati sono solo a tipo di esempio. Un nodo Script può contenere un qualsiasi numero di eventIn, eventOut o fields.

Un campo eventIn sarà un campo che viene settato da un altro nodo. Uno eventOut verrà esportato verso l’esterno. Un field agirà solo all’interno dello script; posso dunque settargli un valore iniziale.

I campi da inserire in uno script e il loro tipo saranno decisi dal programmatore del mondo.

Il campo url è il più interessante e contiene le funzioni necessarie a gestire gli eventi in ingresso al nodo Script.

Vediamo un esempio per chiarire a fondo questi concetti. Nella spiegazione assumo un minimo di conoscenza del linguaggio C.

Come esempio, riconsideriamo il caso dell’interruttore. Vogliamo che quando si clicca sull’interruttore il cubo ruoti sino ad un futuro click. Abbiamo visto che non riusciamo a risolvere il problema con i soliti meccanismi built-in. Proviamoci allora con uno script.

Riflettiamo sugli ingressi che questo deve avere. Vogliamo che delle azioni vengano effettuate in conseguenza del click dell’utente sulla placchetta. Per cui dovremo avere un evento in ingresso legato all’evento in uscita del nodo TouchSensor. Pensiamo poi a cosa vogliamo comandare. Vogliamo attivare o deattivare il campo enabled del nodo TimeSensor. Dovremo dunque avere un eventOut che serve a tale scopo.

Il tipo del campo in ingresso dovrà essere un SFBool perché il campo isActive del nodo TouchSensor emette un valore booleano. Lo stesso per quanto riguarda il campo in uscita in quanto deve matchare con il campo destinazione che è il campo enabled del nodo TimeSensor.

Per cui a questo punto so già che il mio script avrà un eventIn e un eventOut, entrambi di tipo SFBool.

Se pensiamo ancora un po’ a come è realizzato un interruttore, non possiamo fare a meno di associargli uno stato, una qualche memoria. Infatti il suo funzionamento dipende dallo stato in cui si trova. Se il cubo sta ruotando devo mandare FALSE al campo enabled. Viceversa dovrò mandare TRUE. Definisco allora una variabile interna che serve appunto a ricordarsi dello stato (attivato o meno) dell’interruttore. Questo campo non necessita di essere visto dall’esterno e di certo non nasce da un evento esterno. Ne segue che avremo bisogno di un field. Chiamiamo questo field con il nome di ‘stato’. Il tipo del campo sarà ancora un booleano e verrà inizializzato a FALSE, ad indicare che il cubo inizialmente non ruota e l’interruttore è disattivato.

Per cui possiamo scrivere la prima parte dello script.

DEF accendi_spegni Script {
  eventIn SFBool     clicked
  eventOut SFBool  click_changed
  field SFBool  active FALSE
  ...
}

Ciò che manca ora è la parte di risposta agli eventi. Lo script reagisce solo se riceve in ingresso degli eventi. Dobbiamo dunque scrivere una funzione per ogni eventIn dichiarato. Essa specifica cosa fare quando si riceve un certo evento. Il campo in uscita esporta il suo valore ogniqualvolta viene modificato.

Per cui vediamo il nodo completo :

DEF accendi_spegni Script {
  eventIn SFBool clicked
  eventOut SFBool click_changed
  field SFBool active FALSE
  url “vrmlscript :
      function clicked(button_ pos) {
            if (button_pos == 0) {
               if (active == 0)
                  active = 1 ;
               else
                  active = 0 ;
                click_changed = active ;
             }
      }
“
}

ROUTE interruttore.isActive TO accendi_spegni.clicked
ROUTE accendi_spegni.click_changed TO tempo.enabled 

Come si vede faccio il routing di isActive del nodo TouchSensor al campo clicked che è appunto un eventIn. Ho quindi pronta una funzione per gestire questo evento. Come avevo detto in precedenza il nodo TouchSensor in occasione di un click emette due eventi, uno per la pressione e uno per il rilascio del pulsante del mouse. Quando si spedisce un evento si mandano due parametri alla funzione che lo gestisce. In questo caso ne uso solo uno e lo chiamo button_pos. I due parametri inviati sono il valore associato al campo in eventOut e il timestamp dell’evento stesso (il tempo in cui l’evento è stato elevato). Nel caso del nodo TouchSensor si spedisce un primo evento con isActive che vale TRUE e un secondo evento con isActive che vale FALSE quando rilascio il pulsante. Ricordando che un valore è FALSE quando vale 0, si vede che consideriamo solo il secondo evento, tralasciando il primo. Così facendo riceviamo un solo evento per ogni click. Per ogni evento considerato ci si chiede quale era lo stato precedente e lo si inverte. Alla fine della funzione assegnamo il nuovo valore alla variabile click_changed che, ricordo, è un eventOut.

Se osserviamo l’ultimo comando ROUTE, si vede che la variabile click_ changed è collegata al campo enabled del nodo TimeSensor da cui dipende l’animazione. Dato che il valore di click_ changed è cambiato, essa emette un evento che viene catturato dal nodo TimeSensor. Questo setta il campo enabled con il valore che gli viene passato.

Risultato, il cubo continuerà a girare sino al prossimo click e noi avremo ottenuto il nostro interruttore.

Ovviamente il sorgente sopra riportato va inserito nell’esempio sviluppato nei precedenti paragrafi.

Stanza con orologio e cubo che gira a dovere!

Possiamo avere più eventIn. In questo caso avremo più funzioni, definita una dopo l’altra tra gli apici del campo url. La scritta vrmlscript è necessaria per indicare quale sia il linguaggio di scripting da utilizzare.

Le specifiche in realtà parlano di javascript ; ma sia Cosmo Player che Live3d supportano vrmlscript per cui ho utilizzato questo. Del resto le differenze tra i due in pratica non sussistono.

Abbiamo raggiunto ormai la fine. Ora dovreste essere in grado di costruire mondi molto interessanti dal punto di vista del realismo e dell’interazione.

Nelle prossime sezioni vedrò di spiegare molto rapidamente come inserire fonti sonore e descriverò a livello qualitativo l’interfaccia per Java e CGI.