Tot i que el VRML97 dóna molts mecanismes per a definir interacció i comportaments, hi ha coses que no es poden fer diréctament i llavors cal utilitzar la potència d'un llenguatge de programació extern. Això s'aconsegueix a través del node Script i els llenguatges que es poden utilitzar són el Java i el JavaScript.
En aquest tutorial, tan sols entrarem a veure la utilització del JavaScript dins del node Script degut a que és molt més senzill, directe i comú d'utilitzar.
Nota: Partirem del supòsit que el lector ja té un coneixement prèvi del JavaScript com a llenguatge i que el sap utilitzar a nivell bàsic per a realitzar petites utilitats en pantalles de HTML.
El concepte bàsic darrere el node Script és que és un node que permet els següents passos:
Un node Script esta format per dues parts principals: les definicions de camps i esdeveniments, i el codi en el llenguatge que hem triat.
És important el fet que en aquest node hi podem definir camps i esdeveniments segons les nostres necessitats, en contrast amb els altres nodes de VRML que ja tenen predefinits tots els seus components.
El lloc on es posa el codi del llenguatge és en el field url. Aquest field permet escriure tot el codi entre cometes dobles (") o bé referenciar un arxiu extern on hi figuri tot el codi.
A continuació veiem un esquema de l'estructura del node:
|
Eesquema: Estructura del node Script.
|
Aquí es poden observar les dues parts que definiem a dalt. Primer es troben les definicions de camps i esdeveniments. Aquests no han d'estar ordenats de cap manera concreta. En segon lloc trobem el field url on, entre cometes, es defineix el tipus de codi que s'utilitza (en el nostre cas javascript:) i a continuació totes les funcions que configuren els nostres processos.
Hi ha una relació dirécta entre els noms dels eventIn i els noms de les funcions del codi. Aquesta relació s'estableix per tal que quan arribi un esdeveniment d'entrada al node Script en qüestió, ell cridi la funció que té el mateix nom que l'eventIn referenciat i així es pugui capturar el valor que ha rebut i processar-lo.
El mecanisme implementat pel node Script fa que tota funció de JavaScript associada a un eventIn tingui dos paràmetres per defecte: el valor rebut per l'eventIn i l'instant de temps en que s'ha generat aquell esdeveniment. D'aquesta manera, la funció pot llavors utilitzar el valor de l'esdeveniment que ha rebut i inclus discernir-lo d'altres gràcies al fet que també es disposa del temps en que aquell valor s'ha generat.
A continuació donem un exemple on un ProximitySensor envía un esdeveniment de canvi de posició del punt de vista a un Script que mira si la coordenada X d'aquest punt de vista és més gran que 5 i no fa res més (de moment).
|
Exemple1: Definim un ProximitySensor enomenat SensorPuntVista que va detectant el moviment del punt de vista de l'usuari per dins seu. Aquest ProximitySensor va generant eventOuts de nom position_changed, els quals estan encaminats mitjançant un ROUTE al eventIn de nom novaPosicio del Script enomenat SegueixPuntVista.
|
Analitzem aquest codi part per part:
(1) Aixi com en VRML no s'accedeix mai a les components de les dades que pertanyen a tipus no escalars com SFVec3f, SFRotation, SFColor, SFVec2f i tots els MFs, quan hem de programar pot interessar-nos accedir a aquestes components. En aquest cas, un valor d'un d'aquests tipus es comporta com si fos un array de JavaScript i per tant s'accedeix a les components indexant des de 0 (zero) fins al nombre de components menys u. Per exemple: a un field SFColor amb nom colorMeu, se li podrien assignar els valors RGB (1, 0.5, 0.3) des d'un Script indexant de la següent manera: colorMeu[0]=1; colorMeu[1]=0.5; colorMeu[2]=0.3;
Els eventOut es defineixen en la primera part del node Script de forma similar als eventIn. Per tal de poder generar l'esdeveniment de sortida a través de l'eventOut que hem definit, només cal asignar-li un valor des de dintre de la funció que ha de generar l'esdeveniment.
Ampliem el nostre exemple anterior. Ara volem que soni una alarma quan detectem que el punt de vista ha sobrepassat el llindar de 5 unitats en l'eix X. Els elements necessaris són els següents:
|
Exemple2: Definició d'uns esdeveniments de sortida per activar un so d'alarma (afegim a l'exemple anterior).
|
Els elements nous són els següents:
Els field també es defineixen en la primera part del node Script de forma similar als eventIn i als eventOut. Els field serveixen com a variables globals pel Script i per a guardar valors al llarg de tota l'execució de l'entorn. Amb això podem comparar valors d'esdeveniments nous amb valors antics que haguem guardat en fields.
De nou ampliem el nostre exemple. El que farem ara és que soni l'alarma només el primer cop que l'usuari passa el llindar. Si llavors l'usuari torna enrera i després torna a passar el llindar, l'alarma ja no ha de sonar. Els elements necessaris són els següents:
|
Exemple3: Definició d'un field booleà per saber si l'usuari ja havia trespassat el llindar amb anterioritat.
|
Només hem afegit els elements següents:
Inicialment hem dit que, tret del field url, el node Script no tenia cap altre camp predefinit. Això ho hem dit per tal d'evitar complicar l'explicació, però no és cert. De fet el node Script disposa de dos camps predefinits: mustEvaluate i directOutput.
Durant l'execució d'un entorn de VRML, el browser té l'autorització (per especificació) de gestionar els esdeveniments en el moment que li sigui més idoni. Això pot provocar que s'acumulin una sèrie d'esdeveniments durant un lapse de temps i de cop siguin tots avaluats de cop (evidentment en l'ordre en que s'han generat).
Això vol dir que quan programem un Script per gestionar uns esdeveniments, pot passar que no se'ns avalui l'Script cada cop que es genera l'esdeveniment d'entrada que necessitem. En aquest cas passaria que al cap d'una estona s'avaluaria el nostre Script tantes vegades com esdeveniments d'entrada s'haguessin acumulat.
Per tal de controlar això, el node Script disposa del camp predefinit field SFBool mustEvaluate. Per defecte, aquest camp té valor FALSE, cosa que significa que l'avaluació de l'Script pot ser posposada. Si volem que el nostre Script s'avalúi cada cop que es generi un esdeveniment d'entrada dels que gestionem, llavors cal posar el camp mustEvaluate TRUE.
Nota: Cal tenir en compte que posar mustEvaluate TRUE implica imposar un control més exhaustiu al browser i per tant es perd eficiència. Cal fer-ho només quan sigui estríctament necessari.
A vegades és pràctic poder accedir diréctament als exposedFields, eventOuts i eventIns de nodes externs al Script, sense haver de definir tot un conjunt de ROUTEs. Això dóna més control sobre l'entorn ja que no es depén de la gestió d'esdeveniments que fa el browser. Així doncs, podriem tenir definit un node de transformació amb un cub com a geometria, del qual volem saber el valor del camp de translació desde dintre l'Script sense necessitat de que es generi un esdeveniment. Llavors el que fariem seria:
|
Exemple4: Definim un accés directe a un node extern a un Script.
|
Mirem pas a pas el que s'ha fet en aquest exemple:
Amb aquest exemple veiem que podem accedir a informació de nodes externs sense haver d'establir ROUTEs que ens encaminin esdeveniments. Però tal i com està definit, només podem accedir als exposedFields i als eventOuts per llegir-los.
Si volguessim accedir als exposedFields o als eventIns per modificar-los, no podriem a menys que utilitzem el camp que subministren els Scripts, field SFBool directOutput. Aquest camp per defecte té el valor FALSE. Així, per tal de poder tenir accés directe als camps de nodes externs i poder-los modificar, cal posar-lo a TRUE.
Mirem-ho en el següent exemple:
|
Exemple5: Definim un accés directe a un node extern a un Script amb possibilitat de modificació.
|
El que fa l'exemple anterior és que cada cop que s'entra a l'Script, es mou el cub una posició en l'eix de les X fins arribar a 5, moment en el que es torna al cub a l'origen.
Exercicis proposats:
|