Libgdx: Escenas y transiciones

Tras ver lo facil que resulta crear sprites y animaciones en libgdx vamos a ver algo esencial para iniciarl cualquier proyecto, y es la creación de escenas, cambiar de unas escenas a otras y como hacer transiciones entre ellas.

Empezaré por comentaros que todo lo que voy a desarrollar para libgdx lo estoy englobando en un paquete llamado GdxOfcode, y a medida que vaya desarrollando mas cosas irá creciendo .. de momento no publico la rama principal del proyecto porque va a estar creciendo con cada nuevo sample, asi que lo mas posible es que con cada sample se incluya todo el source necesario.

 Escenas Sample 1 – Caso mas básico de cambio de escenas
 Escenas Sample 2 – Añadir transiciones

GdxOfcode: Estructura Global

  • GdxDirector: Esta clase será la encargada de centralizar toda la lógica del juego, será la que tenga las escenas y reciba todos los eventos de ApplicationListener, mandándolos a la escena activa.
  • GdxScene: Esta clase hereda de gdx.scenes.scene2d.Stage de forma que es un contenedor de Actores, además de esto puede controlar movimientos de Scroll y posicionar la Cámara, aunque todas estas cosas de momento no las he investigado y no puedo garantizar que la clase funcione todavía. En esta clase hay que remarcar 2 métodos:
    • start() se llama cada vez que una escena se activa, lo ideal será que la escena solo la creemos una vez en memoria, y en este método hagamos la carga de los recursos que necesitamos. Si algún recurso lo vamos a compartir entre varias escenas es mejor cargarlo en el constructor
    • stop() se llama cada vez que se abandona una escena, en este punto deberemos liberar todos los recursos que no sean esenciales.
  • GdxLayer: Esta clase hereda de gdx.scenes.scene2d.Group esto significa que puede comportarse como un actor y podemos darle Actions, un Group es para libgdx un contenedor de Actores que mantienen alguna jerarquía entre ellos
  • GdxActor: Esta será la clase base de la cual deberá heredar cualquier objeto que queramos meter en un GdxLayer, actualmente hereda de gdx.scenes.scene2d.Actor pero es muy probable que esto cambie y herede de Group ya que esta clase controla mejor algunas transformaciones por lo que he visto.Además de estas clases base he creado los actores mas comunes que vamos a usar, y además he creado un listener para controlar los eventos de los actores y poder sobrecargarlos sobre una Escena en lugar de tener que resolverlos dentro de cada objeto.
  • GdxGame: Adicionalmente se incluye una clase GdxGame que hereda de ApplicationListener y delega todos los métodos sobre la escena activa, se puede heredar de esta clase e implementar únicamente la carga de escenas, o curraros vuestro propio ApplicationListener
    • InitGame(GdxDirector) – En esta método deberemos iniciar todos los recursos que van a ser compartidos durante todo el juego, en general cosas que no queramos liberar en ningún momento, como las fuentes, sonidos … etc
    • BuildScenes(GdxDirector) – Este método nos llama para que construyamos todas las escenas del juego. En muchos sitios te dirán que las escenas no se deben construir hasta que no se necesiten, y esto es cierto, nuestra filosofía de trabajo será crear únicamente la instancia de la escena, pero en el constructor no vamos a cargar ningún recurso. Cada escena ya implementa sus métodos start() y stop() que son los que usaremos para cargar los recursos y liberarlosPodemos decir que este método únicamente le dice a GdxDirector todas las escenas que tiene el juego y que clase es la encargada de gestionar cada una de ellas. Es posible trabajar sin crear las escenas y construirlas únicamente cuando se necesitan (incluso indicar si queremos liberarlas tras finalizar). Pero esto es algo que veremos mas alante
    • StartGame(GdxDirector) – Este método nos llama cuando el juego está listo para empezar, lo único que debemos hacer será activar la escena que corresponda.

Nuestra primera escena

Tras una breve descripción podemos pasar a crear nuestras primeras escenas y ver como cambiar de una a otra, vamos a empezar por un ejemplo muy sencillo

1 – Launcher. En el launcher, antes de lanzar nuestro juego deberemos obtener una referencia a GdxDirector y establecer cual será la resolución sobre la que vamos a trabajar. Esta será la resolución interna de todas las escenas que se ajustará a la resolución REAL del dispositivo:

2 – Nuestro ApplicationListener: Nuestra clase Game va a heredar de GdxGame con lo que solo tendremos que implementar estos 3 métodos, al heredar de GdxGame ya no nos tendremos que preocupar de los eventos Render, Pause, Dispose ni demás .. ya está todos capturados e interpretados en ese nivel.

3 – Como funciona una escena

Vamos a crear una escena muy básica, que simplemente ponga un par de Sprites en pantalla y detecte cuando pulsamos sobre alguno de ellos y en función de cual pulsamos salte a una escena u otra. Esta Escena es solo un ejemplo .. no busquéis el source en ningún sitio ni los gráficos, es únicamente para que veáis la estructura del código:

Siguiendo esta filosofía vamos a crear 3 escenas distintas y ver como nos podemos cambiar de una escena a otra. El resultado lo podéis ver en el siguiente applet: si si si !!! que con libgdx se puede compilar un applet y verlo en directo!! .. aunque hay que tener paciencia con los applets que vienen a tener unos 6 megas de descarga 🙁

 Escenas Sample 1 – Caso mas básico de cambio de escenas
 Sample Escenes1 +GdxOfcode (4.87 Mb)

Transiciones

Trabajar con escenas es muy sencillo, pero el cambio entre escenas es bastante tosco .. en nuestro ejemplo apenas cargamos recursos y es muy rápido el cambio de una escena a otra, pero en condiciones normales tendremos algunas escenas que cuesten mas de iniciar y debemos tener algún paso intermedio entre ambas escenas … y que además queda mejor oiga!!

Hace poco vi un articulo en gemserk sobre como implementar transiciones entre escenas. En este ejemplo trabajaba con el alpha de la escena para hacer un fade a negro y luego intercambiar la escena, conviene echarle un vistazo para ver que nuestra filosofía, aunque similar en el resultado es mas sencilla (a mi humilde parecer).

Este esquema del cambio de escena que tenemos ahora mismo: En nuestra linea constante del tiempo estamos renderizando la Scena1 y llegado el momento de cambiar de escena se llama al Stop de la primera escena, se llama al Start de la segunda escena y se pega el cambiazo de escena activa, pasando en este momento a renderizarse la escena 2

Hay que dar una pequeña vuelta a esta filosofía y tener una escena que se va a renderizar sobre estas 2 para ocultar todo lo que pasa por debajo y que en el momento de pegar el cambiazo de escena esto se haga sin que lo veas:

Esta escena será una capa que se pondrá encima de todo, y tendrá una única imagen en negro, Cuando empieza la transición se nos llamara con un Start(1), en ese momento activamos una animación a la capa negra para que se vuelva solida en 1 segundo, y añadimos un evento para cuando termine esa animación. En ese momento llamamos a director diciéndole que la transición de entrada ha finalizado .transitionCompleted(1); y justo en ese momento cuando ya no se ve lo que pasa por debajo es cuando la clase Director hará el cambio de Escena, y una vez que las haya cambiado nos volverá a llamar mediante start(2) y volvemos a activar la animación pero haciendo la capa transparente para que se pueda ver la nueva escena. del mismo modo cuando terminamos la transición avisamos al Director que se ha terminado mediante .transitionCompleted(2);

1 – la transición será una escena que se usará en cualquier momento, por lo que vamos a cargar sus recursos en el constructor y no se van a liberar en ningún momento

2 – implementamos el método start(int) con los 2 modos de transición, el fadeIn y el FadeOut. Este método será llamado por GdxDirector para hacer cada una de las transiciones

3 – Cambiar de escena mediante una transición

Ya tenemos las escenas y tenemos la transición, ahora solo falta asociar que queremos hacer esa transición al cambiar una escena, que será tan fácil como esto:

Esto es el ejemplo mas sencillo de una transición clásica … y podemos decir que usada para el 90% de nuestros casos, así que todo lo mas cambiaríamos el constructor para pasar como parámetro el color de la transición y generar la imagen dinámicamente … luego os pego este código porque también es muy sencillo.

El caso es que con esta filosofía de tener una Scena superior va a ser muy sencillo implementar cualquier tipo de transición que quede mas integrada con los gráficos y recursos de nuestra aplicación .. aquí podéis ver por ejemplo una sencilla transición con sonido incluido:

 Escenas Sample 2 – Añadir transiciones
 Sample Escenes con Transicion +GdxOfcode (5.05 Mb)

Anexo one, una modificación para hacer un Fade a cualquier color, y sin tener una imagen como recurso
 TransitionSolidColor.java (1.69 Kb)

You may also like...

14 Responses

  1. Una pregunta, como estan manejando los diferentes tamaños de pantallas? hasta ahora no encuentro una solucion viable sobre ese tema 🙁 seria genial si postearan algun tip. Saludos

  2. neofar dice:

    Pues en LibGdx el tema de la resolución de los distintos dispositivos es completamente transparente para el usuario. La clase Scene tiene un parámetro booleano para indicar si quieres que tu scena se ajuste a pantalla o no. Es decir, si vas a trabajar con una scene mas grande que tu pantalla y vas a usar una cámara para moverte por la escena (incluido haciendo zoom) entonces este flag lo pones a false y sabes que estarás trabajando con un tamaño 1:1 … según la pantalla de tu dispositivo se verá más o menos zona de tu scene. Si por el contrario pones el Streech a True entonces la vista de la escena se ajusta a toda la pantalla, es decir … que si tu scena mide 800×480 pixels estarás trabajando siempre a esa resolución, indistintamente de la resolución nativa del dispositivo. De hecho en todos mis samples yo estoy siempre usando esa resolución como base y llego a probar las cosas en un tablet cuya resolución nativa es 1280×768 y se ajusta a pantalla sin problemas. Ahhh y muy importante, ligGdx no solo te ajusta la vista sino que todos los eventos del touch también te los mapea a esa resolución.

    El único problema, lógicamente, es que no todos los dispositivos escalan OpenGL con la misma calidad, si aplica algún filtro de suavizado o no dependerá de cada dispositivo, y si no lo aplica pues está claro que el escalado quedará un poco forzado…
    ya te comento … yo por ahora trabajo con una resolución de 800×480 y el único problema que le veo, por ver alguno, es que en las fuentes si que se nota un poco de sierra .. en lo demás no hay problema

  3. Mi problema es que las imagenes no quedan igual, yo trabajo con una resolucion de 480×320 y queria que se vea parejo en cualquier resolucion, encontre esta solucion http://www.java-gaming.org/index.php?topic=25685.0 , pero no era completa, pero con la explicacion que me diste ya se como terminarla, gracias ;), una pregunta mas, estuve probando ayer en mi tablet y todo genial, pero tambien en un galaxy Ace y aqui, por ratos, en vez de mis sprites aparecian cuadrados blancos, y no seguian un patron, a veces si, a veces no, alguna idea de porque podria estar sucediendo esto

  4. cemurilloc dice:

    Una consulta como integrar el ejemplo de acciones con el este de ejemplo de las transiciones.

  5. Alejo Gómez dice:

    hola
    Hace tiempo te hize varias preguntas gracias a estos tutoriales he aprendido harto acerca del libgdx, el asunto que ahora tengo que es que quiero enfocar la camra en un actor que se mueve por un escenario grande, no se me podrias porfa algun consejo

    • neofar dice:

      Buenas
      Pues es bastante fácil mover la camara, si has heredado de Stage (que sería lo normal, y si no lo has hecho ya es hora de que lo corrijas) tienes un método getCamera() y a partir de ahí tienes los vectores : Position, Direction, Up

      Así que aquí tienes lo que buscas:

      Esto a mi punto de vista solo tiene una pega, y es que posiciona la cámara en ese punto … sin hacer ninguna interpolación ni movimiento … si tu cámara sigue a tu player de esta forma vas a conseguir que tu player este SIEMPRE fijo en el centro y de la sensación que es lo de alrededor lo que se mueve … siempre es recomendable hacer que la cámara ‘siga’ al player con un poco de retardo, de forma que tu empiezas a moverte y tienes esa sensación de libertad de que la cámara intenta seguirte … y cuando te paras te alcanza.

      Yo eso me lo monto con unos métodos de este estilo:

      como extra te diré que también tengo métodos para hacer zoom sobre la cámara y para rotarla

      PD .. el parámetro time no es el tiempo … nunca he tenido tiempo de arreglar esto y ponerlo de verdad en función del tiempo, tiene el problema que en función de los fps que te de la cámara irá mas rápida o mas lenta, pero bueno es un punto de partida para trabajar

  6. Alejo Gómez dice:

    gracias por todo siempre haces muy buenos tutoriales, mira nose si has trabajado con tile map editor o tambien hay uno que se llama tide, el primero tira archivos en .tmx y el segundo en tide, el las ultimas librerias de libgdx esta la opcion para usar estos para cargarlos etc. el drama que he tenido es que no puedo cargar este mapa en la escena, agradeceria mucho si es que tu has trabajado en esto y tienes alguna recomendacion de como hacerlo de antemano muchas gracias

  7. Alejo Gómez dice:

    ya solucione el problema del tmx, una pregunta con respecto a la escenas, cuando le coloco musica y cambio a la otra escena la musica sigue y despues cuando quiero volver a esta se me pone la musica encima de la misma, hay alguna manera de limpiar la escena o destruirla para despues cuando vuelva a ocuparla construirla desde el principio hay otras escenas que quiero que se queden igual

    • neofar dice:

      No vi a tiempo tu pregunta del tmx .. es que estoy de vacaciones ya!! ji ji … y desde que el gmail envía estos correos al tag ‘notificaciones’ ni me doy cuenta. Sobre los tmx te recomiendo que te bajes los sources de libgdx del repositorio o que uses las nightybuilds porque ha cambiado bastante y si estas usando la ultima release oficial va a dejar de funcionarte cuando se actualice :PPP

      Y sobre la segunda pregunta, no me especificas si estas usando mis escenas o las de libgdx, ambos tienen un método que te avisa cuando la escena se libera de memoria, si estas con la mía tienes el método start() que te notifica cuando la escena se activa … pues hay un método ‘virtual stop()’ que te avisa cuando la escena se debe eliminar para que hagas eso, parar cualquier música y liberar todos los recursos gráficos que uses en la escena. Y su usas la ‘stage’ de libgdx te buscas tu como se llama el método 😛

  8. Alejo Gómez dice:

    tengo dos dudas, me compila el juego en celular pero se me cae, y estoy usando dos librerias de gdx, una es la 0.9.3 y la segunda nightybuilds les puse gdx y gdx2 funciona como aplicacion java perfectamente con tus escenas, en el proyecto android donde se ponen las 2 librerias gdx y gdx backend android, ¿la libreria gdx tiene que ser la misma que la del proyecto java? porque si es asi como lo hago para meter estas dos librerias. meti estas dos librerias gdx y gdx2 dentro pero no me deja compilarlo le saco una y me compila pero se me cae en ciertas partes. La libreria nightybuilds no tiene varias clases como AnimationAction, pero si los tilemap y todo el juego lo hize con AnimationAction, entonces necesito usar las 2. si me das algun consejo te lo agradeceria mucho. y la segunda en donde el metodo start() y stop() donde en que clase lo uso especificamente?.

  9. Alejo Gómez dice:

    hola compadre

    estoy haciendo un rpg, por lo que hay ciertas dudas que no he podido resovlver como por ejemplo el inventario, los items son un actor? como listarlos?
    los miembros del grupo como listarlos, como guardarlos en memoria cosa de que despues cuando me salga de la aplicacion se guarde el progreso del juego. si tienes el codigo fuente de un rpg por ahi que use escenas te lo agredeciria mucho, si no algun consejo de como abordar estos problemas, gracias de antemano

  10. Alejo Gómez dice:

    compadre
    no te preocupes ya solucione el problema del inventario, ahora estoy con el manejo de las capas, no se si entiendo bien esto pero las capas se va superponiendo una sobre otra por lo que la capa de mas arriba tiene prioridad sobre la de abajo, el asunto es como cambio una capa de mas abajo por una de mas arriba y viceversa? que método usar?

  11. Alejo Gómez dice:

    hola compadre
    soy yo de nuevo, estoy haciendo unos botones, los cuales quiero que sean touchables, los heredo de la clase GdxActor, lo malo es que cuando hago click sobre este me sale fuera del sprite me podrías ayudar por fa a saber como funciona el touch de tus clases por fa.
    este es mi código del botton:

    public class GdxButton2 extends GdxActor{

    protected Texture pressed,unpressed;
    protected Texture keyFrame;

    public GdxButton2(Texture texture,Texture texture2){
    this.pressed=texture2;
    this.unpressed=texture;
    keyFrame=unpressed;
    }

    public void setButtonPress(){
    keyFrame=pressed;
    }

    public void setButtonUnpress(){
    keyFrame=unpressed;
    }

    public void draw(SpriteBatch batch, float parentAlpha) {

    batch.draw(keyFrame, x – this.width / 2.0f, y – this.height / 2.0f,width,height);

    }

    }

    • neofar dice:

      Hola alejo, pues estoy bastante desconectado de estas clases que hice hace tiempo y ahora mismo no podría indicarte mucho … hace poco las tuve que reconstruir y las simplifiqué mucho. Ademas en libgdx había un trato con ‘Origin’ que no me estaba bien tratado antes. Para libgdx Origin solo se usa para rotar la textura pero no se tiene en cuenta para calcular el punto de touch, y en mis clases esto no lo tenia en cuenta por lo que el touch no iba bastante bien.

      Es algo que con la librería actual si que tengo bien solucionado, así que intentaré subírtelas y heredas de esas clases. Además tengo una interfaz que te avisa del touchdown, TouchUp, touchEnter, Exit y Move

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *