Realidad Virtual
información adicional
jaume ferrer i rosera
15. Scripting: Cómo conseguir que el comportamiento de un objeto se vea alterado por la presencia del usuario
Consulta:
¿Cómo conseguir que un objeto modifique
su estado en función de la detección del punto de vista del usuario?.
En respuesta a las dudas suscitadas por este y otros ejemplos similares, la
respuesta incluye también una descripición pormenorizada de todo
el script y de sus enlaces con el resto de objetos VRML.
Respuesta:
A continuación se muestra un ejemplo basado en el de la FAQ 11. Tres
cajas empiezan a girar al clicar sobre una esfera y una de ellas además
se desplaza sobre el eje de las x. Cuando supera una cierta posición
invierte el sentido de la marcha y sigue avanzando hasta que supera otro límite
y vuelve atrás y así indefinidamente. Todo se detine si clicamos
de nuevo sobre la esfera. Las tres cajas son instancias 'hijas' de un prototipo
de caja genérica.
Hemos añadido un ProximitySensor a la caja en movimiento cuya misión es detectar la aproximación del usuario o su alejamiento. Cuando la esfera roja ha sido clicada y las cajas están girando y una de ellas además se está desplazando, si el punto de vista del usuario entra en el área de detección del ProximitySensor se envía un SFTime al script que llama a la función proximidad(v,t), la cual se encarga de actualizar una variable global del script (field SFBool dentro). El resultado es que una instrucción if() dentro de la función anima(v,t) ejecuta un cálculo de las posición de la caja alternativo que, en este caso y a modo de ejemplo, consiste en añadir pequeños incrementos aleatorios positivos o negativos, por lo que la caja se pone a titubear sobre la misma posición sin llegar a avanzar claramente. Si el punto de vista retrocede y sale de la zona de detección del ProximitySensor vuelve a generarse un SFTime y la misma función proximidad(v,t) reestablece el valor de la variable dentro, con lo cual la caja puede seguir avanzando con normalidad.
Este ejemplo también permite observar una aplicación de la función Math.random() de JavaScript para obtener números pseudoaleatorios.
Ver código fuente [si lo guardáis en vuestro disco duro y lo editáis con VrmlPad podréis ver el código coloreado]
Cómo trabaja el script:
Para empezar, veamos la definición de campos:
eventIn SFFloat anima
campo de entrada de tipo coma flotante (valor decimal) activado desde el TimeSensor motorGira a cada clic de reloj y que a su vez tiene el mismo nombre que la función anima(v,t), por lo que dicha función se ejecutará constantemente. La ruta que define este enlace es:
ROUTE motorGira.fraction_changed TO ArrancaPara.animaeventIn SFTime allaVamos
campo de entrada de tipo evento de tiempo activado desde el TouchSensor SensorBotonVa y que a su vez tiene el mismo nombre que la función allaVamos(v,t), por lo que dicha función se ejecutará cada vez que cliquemos con el ratón en el objeto BotonVa, que la esfera roja asociada al TouchSensor SensorBotonVa. La ruta que define este enlace es:
ROUTE SensorBotonVa.touchTime TO ArrancaPara.allaVamos
field SFBool como FALSE
campo booleano (sólo puede ser true o false) usado como variable de control para determinar si la animación está funcionando o está parada. Su valor inicial es FALSE, por lo tanto la animación está paradafield SFFloat incremento 0.05
campo de tipo coma flotante (valor decimal) usado como variable donde se almacena el incremento que queremos añadir a las coordenadas de nuestra caja para que cambie de posición. Su valor inicial es 0.05eventOut SFRotation giraCaja
campo de salida de tipo Rotation (define un vector de orientación usando una matriz compuesta por cuatro valores: [0]=eje x, [1]=eje y, [2]=eje z, [3]=ángulo) usado para enviar fuera del script el cambio de rotación de nuestra caja. Las rutas que definen este enlace son:
ROUTE ArrancaPara.giraCaja TO CajaHija1.orientacionCaja
ROUTE ArrancaPara.giraCaja TO CajaHija2.orientacionCaja
ROUTE ArrancaPara.giraCaja TO CajaHija3.orientacionCajaeventOut SFVec3f mueveCaja
campo de salida de tipo Vec3f (una matriz compuesta por tres valores que en este caso corresponderan a las coordenadas de posición de nuestra caja del modo siguiente: a [0]=x, [1]=y, [z]=z) usado para enviar fuera del script el cambio de posición de nuestra caja. La ruta que define este enlace es:
ROUTE ArrancaPara.mueveCaja TO CajaHija1.posicionfield SFNode Cajeando USE CajaHija1
campo de tipo Node (que se refiere a un objeto VRML) usado para referirnos desde dentro del script al objeto CajaHija1. Por ejemplo, para saber la coordenada x de nuestra caja usaremos la expresión Cajeando.posicion[0], puesto que en CajaHija1 hemos definido un campo de tipo Vec3f llamado posicion donde se almacenan las coordenadas de su posición en el espacioeventIn SFTime proximidad
campo de entrada de tipo evento de tiempo activado desde el ProximitySensor detectaUsuario y que a su vez tiene el mismo nombre que la función proximidad(v,t), por lo que dicha función se ejecutará cada vez que entremos o salgamos del área de proximidad definida por los campos size y center de detectaUsuario. Las rutas que definen este enlace son:
ROUTE detectaUsuario.enterTime TO ArrancaPara.proximidad
ROUTE detectaUsuario.exitTime TO ArrancaPara.proximidadfield SFBool dentro FALSE
campo booleano (sólo puede ser true o false) usado como variable de control para determinar si la caja se encuentra dentro o fuera del área delimitada por los límites que no queremos que supere. Su valor inicial es FALSE, por tanto la caja está dentro de los límites
A continuación veamos qué hace cada función:
allaVamos(v,t)
verifica el valor del campo como. Si vale FALSE la pone en TRUE y hace una llamada a la función anima(). Si vale TRUE la pone en FALSE. Con ello conseguimos que al clicar sobre la bola roja, si la animación está parada se ponga en marcha y si está en marcha se pareproximidad(v,t)
verifica el valor del campo dentro. Si vale FALSE lo pone en TRUE. Si vale TRUE lo pone en FALSE. Con ello conseguimos que el script sepa si estamos dentro o fuera del área a detectar por el proximitySensor, por tanto sepa si estamos cerca o lejosanima(v,t)
en primer lugar verifica el estado de como y sólo ejecuta el resto del código si vale TRUE, es decir, si la animación está en marcha:
if(como == TRUE){después asigna una nueva orientación a la caja para hacerla girar sobre sí misma. Esta rotación se consigue asignando un ángulo de giro que sea igual al que ya existe más un incremento de 0.1 radianes. Como los valores del campo giraCaja permanecen en memoria, con ello conseguimos que el ángulo sea siempre igual al anterior más un incremento constante. Para indicar que el giro sólo debe efectuarse sobre el eje de las y usamos la expresión siguiente:
giraCaja[0] = 0
giraCaja[1] = 1
giraCaja[2] = 0
giraCaja[3] = giraCaja[3] + 0.1en segundo lugar verifica el estado de dentro y sólo ejecuta el resto del código si vale FALSE, es decir, si nos encontramos fuera del área de detección, por tanto lejos de los objetos. En caso contrario se ejecutaría el codigo incluido en la parte else{}:
if(dentro == FALSE){
..
}
else{
..
}si dentro vale FALSE (si estamos lejos), entonces verifica si la caja se halla dentro o fuera de los límites establecidos para el recorrido de la animación. Como sólo estamos desplazando la caja horizontalmente, estos límites afectan sólo al eje de la x (concretamente valen x=6 y x=-6) por lo que sólo comprobaremos la coordenada x de la posición de la caja. Si estamos más a la derecha de 6 asignamos a incremento el valor -1, en caso contrario asignamos 1. La expresión Cajeando.posicion[0] equivale a decir posicion x del objeto CajaHija1, tal y como se ha comentado más arriba al explicar el campo Cajeando:
if(Cajeando.posicion[0] > 6){
incremento = -0.1
}
if(Cajeando.posicion[0] < -6){
incremento = 0.1
}finalmente la nueva posición de la caja se calcula añadiendo incremento a las x. Puesto que incremento puede valer +1 o -1, entonces la caja estará avanzando hacia a la derecha o hacia la izquierda según el resultado de la verficación anterior. Podríamos usar cualquier otro algoritmo para recalcular la posición y conseguir otro tipo de desplazamiento:
mueveCaja[0] = Cajeando.posicion[0] + incremento
mueveCaja[1] = Cajeando.posicion[1]
mueveCaja[2] = Cajeando.posicion[2]en caso de que dentro no valga FALSE (por tanto valga TRUE, lo cual significa que nos encontramos cerca de los objetos) entonces la nueva posición de la caja no se calcula añadiendo un incremento constante a las x sinó incrementando cada coordenada de forma aleatoria con valores de incremento que rondaran entre ±0.15 para las x, ±0.1 para las y y ±0.05 para las z. Con ello conseguimos que la caja se comporte de un modo distinto cuando estamos cerca y en lugar de avanzar se ponga a oscilar en torno a la misma posición hasta que nos alejemos de ella. Podríamos usar cualquier otro algoritmo para recalcular la posición y conseguir un comportamiento distinto:
mueveCaja[0] = Cajeando.posicion[0] + ( (0.3 * Math.random())-0.15 )
mueveCaja[1] = Cajeando.posicion[1] + ( (0.2 * Math.random())-0.1 )
mueveCaja[2] = Cajeando.posicion[2] + ( (0.1 * Math.random())-0.05 )