|
||||||
ROBOTICA
video
immagini
papers
progettati
costruiti
toolbox
vrml
simulatori
Aggiungiamo un pizzico di interattivita': il nodo TouchSensor |
Con l’introduzione di animazione
abbiamo fatto un grosso salto nella realizzazione di mondi vrml. Ma ancora manca
qualcosa. Il vostro visitatore attraversa la scena, può contemplarne la forma,
può osservare anche oggetti in movimento, ma non può in alcun modo intervenire
nell’evolversi della scena stessa.
Se dunque nel nostro
mondo ci sta venendo contro una pallina, non potremo fare altro che osservarla
attraversarci, senza la possibilità di afferrarla e magari rilanciarla da
qualche altra parte. Manca insomma qualsiasi forma di interattività.
Vrml consente di raggiungere
con lo scopo tramite dei sensori. I sensori sono rappresentati da nodi che
emettono degli eventi in relazione al comportamento dell’utente. In questo
paragrafo analizzeremo più in dettaglio il sensore TouchSensor. Vrml rende
disponibili però anche altri sensori che vedremo brevemente in conclusione
di paragrafo. Torniamo al problema che ci eravamo posti in conclusione del
paragrafo precedente. Come aggiungere un interruttore alla stanza che abbiamo
appena creato.
Si potrebbe legare l’accensione
della luce alla pressione del sensore. Per catturare la pressione di un oggetto
si ricorre al nodo TouchSensor.
Per motivi che ho esposto
già da tempo, preferisco non procedere a gestire l’interruttore per la luce.
Usare le luci in browsers vrml 2.0 al momento (specie se di tipo PointLight
o SpotLight) porta ad ottenere effetti molto diversi con browsers diversi.
Ci si potrebbe chiedere il motivo del valore .1 posto al campo radius del
nodo PointLight del nodo precedente. Il motivo è solo dovuto al fatto che
con Cosmo Player viene bene. Trasportato su Live3d la stanza risulterebbe
molto buia; ma un valore più elevato porterebbe a problemi di sovrailluminazione
in CosmoPlayer. Al momento attuale si devono cercare dei trade-offs in quest’ambito.
Per cui, preferisco introdurre
un altro tipo di interazione. Una volta compresa questa, lo stesso meccanismo
si applica anche all’interruttore della luce.
Supponiamo di volere
riempire un po’ la nostra stanza. Metteremo nel centro della stanza una piccola
colonnina con sopra un cubo. Sulla colonna mettiamo un piccolo interuttore,
alla pressione del quale il cubo comincia a ruotare. Se si ripreme poi l’interruttore
il cubo si ferma.
Gestire questa situazione
è molto semplice. L’animazione viene realizzata sempre nel solito modo ; il
problema è che essa non deve essere sempre attiva. Serve un modo per attivare
o disattivare l’animazione in conseguenza della pressione dell’interruttore.
Ricordandoci che gli interpolatori emettono nuovi valori di posizione in funzione
della frazione di tempo che viene spedita dal nodo TimeSensor, risulta che
il modo più semplice di manovrare l’animazione è quello di pilotare il campo
enabled del nodo TimeSensor da cui dipende l’animazione.
Vediamo come funziona
il sensore TouchSensor. Definito un certo oggetto, basta aggiungere il nodo
TouchSensor nel campo children dove l’oggetto stesso è definito.
Come si può vedere, includendo
il sensore all’interno del campo children, rendiamo l’oggetto cliccabile.
Quando questo viene cliccato il nodo TouchSensor manda in uscita un evento
isActive di tipo booleano.
A questo punto il gioco
è fatto. Ridirigiamo l’evento isActive del sensore al campo enabled del nodo
TimeSensor.
Vediamo ora l’esempio,
da introdurre nel sorgente del precedente paragrafo.
Ho modificato alcune
posizioni per far risultare il mondo piu' vedibile.
Aggiungendo queste istruzioni
al mondo creato in precedenza otteniamo l’effetto. La dimensione del file
è cresciuta ora a 7Kb più texture; comunque una dimensione ancora limitata.
Si noti che non è molto
intuitivo che cliccando sulla placchetta si ruoti il cubo. In mondi meno didattici
dovremmo cercare di evidenziare questa possibilità all’utente in qualche modo.
Per esempio si potrebbe attaccare una texture a forma di pulsante sulla placchetta
stessa. Oppure piazzare sotto di essa una freccia che la indichi o qualcos’altro.
Prima di analizzare il
sensore, faccio notare il modo con cui ho creato il supporto per il cubo (anche
se di fatto il cubo volteggia leggermente sopra di esso). Ho utilizzato ancora
una volta il comando USE per riusare il nodo che definisce il blocco. Questo
mi consente di risparmiare spazio (e molto). Dovendo adattare i blocchi ho
usato il campo scale del nodo Transform il cui significato mi pare ovvio.
Tornando al nodo TouchSensor,
la cosa più importante risulta il campo isActive che esporta. Se proviamo
a far girare questo sorgente troveremo però un inconveniente. L’interruttore
non si comporta proprio come avremmo voluto. Se teniamo premuto l’interruttore
il cubo ruota (dove per premere intendo cliccare sopra l’oggetto cui è associato
il sensore).
Il problema è il seguente.
Il nodo TouchSensor invia un evento sia alla pressione del pulsante che al
suo rilascio. Ne segue che il nodo TimeSensor si attiva alla pressione e si
deattiva al rilascio (quando isActive spedisce il valore 0). Non possiamo
risolvere il problema con i mezzi che abbiamo a disposizione per il momento.
Ci serve qualcosa di più e questo sarà fornito dall’ultimo argomento che analizzeremo
con un po’ di dettaglio : vrmlscript, un linguaggio di scripting molto simile
agli script java.
In conclusione, parlavo
in precedenza della presenza di altri sensori messi a disposizione da vrml.
Il loro funzionamento è molto simile a quello del nodo TouchSensor. Per esempio
abbiamo sensori di prossimità (ProximitySensor) e sensori di visibilità (VisibilitySensor).
DEF placchetta Transform {
translation 0 0 -1
children [
Shape {
appearance Appearance {
material Material { diffuseColor 1 }
}
geometry Box { size .05 .05 .001 }
}
DEF interruttore TouchSensor {}
]
}
DEF base_appoggio Transform {
translation 0 .1 -2
children [
DEF blocco Shape {
appearance Appearance {
material Material { diffuseColor 1 1 1 }
texture ImageTexture {url “textures/marmi/marble.jpg”}
}
geometry Box { size .8 .2 .8 }
}
Transform {
translation 0 .5 0
scale .6 3 .6
children [
USE blocco
]
}
Transform {
translation 0 .8 0
children [
USE blocco
# definisco qui la placchetta (figlia del blocco cui e’ collegata).
Transform {
translation 0 0 .42
children [
Shape {
appearance Appearance {
material Material { diffuseColor 1 1 1 }
texture ImageTexture { url "../textures/generali/x.gif" }
}
geometry Box { size .2 .2 .004 }
}
# definisco ora il sensore per l’interruttore
DEF interruttore TouchSensor {}
]
}
# definisco ora il cubo da ruotare (figlio
# della base di appoggio...)
# se sposto questa il cubo si sposta
# si sposta con essa
DEF cubo Transform {
translation 0 .5 0
children [
Shape {
appearance Appearance {
material Material { diffuseColor 1 0 0 }
}
geometry Box { size .3 .3 .3 }
}
]
}
]
}
]
}
]
}
# definisco ora il sensore del tempo. Lo setto disattivato
# inizialmente.
# L’animazione parte premendo sulla placca.
DEF tempo TimeSensor {
cycleInterval 5
enabled FALSE
loop TRUE
startTime 1
}
DEF orient_cubo OrientationInterpolator {
key [0 .25 .5 .75 1]
keyValue [0 1 0 0, 0 1 0 1.57, 0 1 0 3.14, 0 1 0 4.71, 0 1 0 6.28]
}
ROUTE interruttore.isActive TO tempo.enabled
ROUTE tempo.fraction_changed TO orient_cubo.set_fraction
ROUTE orient_cubo.value_changed TO cubo.set_rotation
Stanza con orologio e cubo