Animación de objetos con JME

Se que hace monton de tiempo dije que haria un tutorial de JME, se que deje ese tutorial a medias, también se que despues dije que ahora si haria el tutorial de JME y finalmente, tambien estoy al tanto de que despues de que dije eso no escribi nada de JME xD. Pues en esta ocasión vengo a acabar con la costumbre de decir que escribire tutoriales de JME para luego no hacer nada y publicare este tutorial de animación de nodos con JME sin anunciarlo xD.

En esta ocación les explicaré como realizar una animación de nodos básica con JME (daré por hecho que ya tienen su entorno configurado para usar JME, si no lo tienen pueden leer esta entrada del blog).



Lo que hará nuestro ejemplo es dibujar una caja y crear una animación de una pelota rebotando dentro de ella, para esto partiremos de la clasica clase heredada SimpleGame, la cual explicaré brevemente (si, esto es algo que debi escribir en los tutoriales anteriores que jamas escribí xD, pueden encontrar una referencia a esta clase en esta entrada del blog).

import com.jme.app.SimpleGame;
public class Pelota extends SimpleGame {
    public static void main(String args[]){
        Pelota app = new Pelota();
        app.setConfigShowMode(ConfigShowMode.AlwaysShow);
        app.start();
    }
    @Override
    protected void simpleInitGame() {   
    }
}

La implementación de SimpleGame es muy sencilla, basicamente crea el bucle que se generará una y otra vez en el juego, unicamente creamos un objeto de nuestro juego y lo iniciamos con el método start(), este metodo inicializará el juego llamando al método simpleInitGame() dentro del cual debemos inicializar todos los valores que usarán en el juego.

Ahora que he explicado superficialmente como funciona la clase SimpleGame regresemos al tutorial.
Lo primero que haremos será crear las figuras que necesitamos (una esfera para la pelota y una caja para... si, la caja xD), estas se encuentran dentro de la clase com.jme.scene.shape

//Los argumentos del constructor son(String nombre, Vector3f esquina, Vector3f esquinaOpuesta)
Box caja = new Box("Caja", new Vector3f(0,0,0), new Vector3f(5f,5f,5f));
//Los argumentos del constructor son(String nombre, int muestrasEnZ, int muestrasRadiales, float radio)
Sphere pelota = new Sphere("pelota",30,30,1);

Las esferas se dibujan mediande formas triangulares, por lo que a mayor numero de muestras se tendra una esfera mas redonda, pero esta tomara mas tiempo en procesarse.

Estas dos clases heredan de com.jme.scene.Spatial por lo que podemos agregarlas a un nodo método attachChild().

Ya tenemos los elementos necesarios para nuestra animación, resta crear la animación, para esto haremos uso de la clase com.jme.animation.SpatialTransformer creando un objeto de ella, esta clase nos permite modificar objetos de tipo Spatial, y como recordarán haber leido un par de lineas, nuestra caja y nuestra esfera heredan de dicha clase.

//el argumento es (int numDeObjetos)
SpatialTransformer transformer = new SpatialTransformer(1);

Podemos agregar multiples objetos para animar a un solo abjeto de tipo SpatialTransformer, esto es lo que indicamos como argumento en el constructor.

Finalmente agregamos el objeto que vamos a animar (en este caso la pelota) a nuestro objeto SpatialTransformer, esto con la ayuda del método setObject

//Los argumentos son (Spatial objeto, int indice, int indicePadre)
transformer.setObject(pelota, 0, -1);

El argumento indice indica el indice del objeto que queremos animar en el arreglo de objetos que contiene nuestro objeto SpatialTransformer (redundante no? xD) y el argumento indicePadre indica el indice de la clase Padre del objeto, si en indicePadre se usa un valor de -1 significa que no tiene padre. Por el momento siempre usaremos -1 en indicePadre.

Finalmente agregamos los puntos a los que viajará nuestro objeto en la animación, esto lo haremos con el método setPosition (ten en cuenta que las esferas se colocan a partir de su centro, por lo que tienes que ajusar sus valores de acuerdo a su radio, de lo contrario podrás ver que la esfera pasa a través de la caja).

//Los argumentos son (int indiceEnElArreglo, float Tiempo, Vector3f puntoDestino)
transformer.setPosition(0,1,new Vector3f(1f,1f,2.5f));
transformer.setPosition(0,2,new Vector3f(2,2,1f));
transformer.setPosition(0,3,new Vector3f(4,4,2f));
transformer.setPosition(0,4,new Vector3f(2,2,4));
transformer.setPosition(0,5,new Vector3f(1f,1f,2.5f));

Es importante recordar que si la animación sera ciclica, el ultimo movimiento debe regresear a la posicion inicial, de lo contrario la animación dará un salto muy poco estético. Ya que estamos con la animación ciclica hablemos de los tipos de interpolación que nos ofrece JME.

JME nos ofrece tres tipos diferentes de interpolación dentro de la clase import com.jme.scene.Controller, estos son:
  • RT_CLAMP - La animación es de forma 0 1 2 3 4 5
  • RT_CYCLE - La animación es de forma 0 1 2 3 4 5 4 3 2 1 2 3 ...
  • RT_WRAP - La animación es de forma 0 1 2 3 4 5 0 1 2 3 4 5 ...
En este caso deseamos que la pelota bote en 4 caras de nuestro cubo, por lo que la interpolación mas que mas nos conviene es RT_WRAP, pero suponiendo que quisieramos hacer un bote vertical podriamos hacerlo con RT_CYCLE definiendo unicamente dos puntos (nos ahorramos el de regreso al punto inicial). El valor por defecto es RT_CLAMP.

Ahora si, indicamos el tipo de interpolación usando el metodo setRepeatType() (creo que se explica solo) y indicamos que inicie la interpolación llamando al método interpolateMissing()

transformer.setRepeatType(Controller.RT_WRAP);
transformer.interpolateMissing();

Ya tenemos listo todo en nuestro SpatialTransformer, pero nuestra pelota un no sabe quién la va a controlar, esto se lo indicamos con el método addController (método que tambien se explica solo)

pelota.addController(transformer);

Y finalmente agregamos la pelota y la caja al nodo raiz, si no hacemos esto no podremos ver nada

rootNode.attachChild(pelota);
rootNode.attachChild(caja);

Si todo salió bien ahora puedes correr la aplicación y verás algo como esto

No, esto no es un timo, solo que la pelota esta rebotando dentro de la caja, puedes mover la camara dentro de la caja usando las declas w,a,s,d en tu teclado para desplazar la camara y el ratón para hacerla girar. Lleva la camara dentro de la caja y podrás ver como la pelota botando (juro que intente sacar una captura dentro de la caja, pero se veia muy piratona xD). Y si, no se parece a la primera imagen de la entrada, pero para lograr ese efecto tenemos que aplicar transparencias y para eso hay que aplicar cosas completamente ajenas a este tuto, ya después les contaré somo se le hace para lograr la transparencia. De momento les dejo el codigo completo del ejemplo:


import com.jme.animation.SpatialTransformer;
import com.jme.app.SimpleGame;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.scene.Controller;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Sphere;
import com.jme.scene.state.BlendState;
import com.jme.scene.state.MaterialState;
public class pelotita extends SimpleGame {

    public static void main(String args[]){
        pelotita app = new pelotita();
        app.setConfigShowMode(ConfigShowMode.AlwaysShow);
        app.start();
    }
    @Override
    protected void simpleInitGame() {
        Box caja = new Box("Caja", new Vector3f(0,0,0), new Vector3f(5f,5f,5f));
        Sphere pelota = new Sphere("Pelota", 50, 50, 1);   
        SpatialTransformer transformer = new SpatialTransformer(1);
        transformer.setObject(pelota, 0, -1);
        transformer.setPosition(0,1,new Vector3f(1f,1f,2.5f));
        transformer.setPosition(0,2,new Vector3f(2,2,1f));
        transformer.setPosition(0,3,new Vector3f(4,4,2f));
        transformer.setPosition(0,4,new Vector3f(2,2,4));
        transformer.setPosition(0,5,new Vector3f(1f,1f,2.5f));
        transformer.interpolateMissing();
        transformer.setRepeatType(Controller.RT_WRAP);   
        pelota.addController(transformer);   
   
        rootNode.attachChild(caja);   
        rootNode.attachChild(pelota);
    }
}

Pueden agradecer este tuto/ejemplo a mi mostra (no, no me equivoque al escribirlo xD) de POO. Si no fuera por que dejo hacer este ejercicio en equipo no lo habría hecho (si me siguen en twitter sabrán que nome gusta hacer tarea xD).

Recuerden que si tienen dudas o necesitan ayuda con algo  pueden contactarme en los comentarios o enviarme un correo.