domingo, 28 de diciembre de 2014

Tiled + Box2D Parte 2

En la parte 1 del tutorial aprendimos que es Tiled y como crear mapas con el, ahora vamos a aprender a como crear objetos con propiedades para utilizarlos dentro de nuestra aplicacion.

Agregar objetos

Los objetos que agregemos son de mucha utilidad ya que pueden nos pueden decir donde va colisionar un cuerpo con otro. Para agregar objetos se necesita una capa de objetos para esto seleccionamos la opcion Layer > Add Object Layer. Ahora tenemos una nueva capa de objetos como se puede ver en la siguiente imagen.


Despues seleccionamos una opcion para agregar un objeto en este ejemplo seleccionamos Add Rectangle:


Seleccionamos el punto en el mapa donde queremos agregar el objeto y creamos el rectangulo. Como se ve en la siguiente imagen se agrego un rectangulo para cada una de las plataformas.


Tambien vamos a agregar un objeto para el piso, pero ahora seleccionamos la opcion de Insert Polyline y ponemos el objeto como se ve en la imagen siguiente


Ya hemos agregado objetos a las plataformas y al piso, tambien vamos a agregar otros pequeños objetos que una vez pasados a Libgdx seran las monedas del juego. Nuestro mapa se vera de esta forma:


Ya se han agregado los objetos de las monedas pero hay un problema cuando sea necesario crear los cuerpos en Box2d ¿como vamos a diferenciar cual es el piso, las monedas y cuales son la plataformas? Para esto es necesario agregar propiedades.

Agregar propiedades a los objetos

En este ejemplo las propiedades van ayudar a diferenciar cada uno de los objetos ya sean las monedas, el piso o las plataformas.
Primero vamos a agregar colores a los distintos tipos de objetos. Seleccianmos Tiled > Preferences... > Object Types. Damos click en el + para agregar un nuevo tipo de objeto y le damos un nombre y seleccionamos un color. Despues de agregar la moneda, el piso y la plataforma debe quedar asi:


Es importante notar que tenemos 3 tipos de moneda diferente esto porque se podra agregar monedas que valen 1, 5 y 10.
El siguiente paso es muy sencillo solo es necesario agregar las propiedades a los objetos, para esto se da click derecho al objeto y se selecciona la opcion Object Properties...


En la ventana que aparece podemos ver que existen muchas propiedades (Name, Type, Position, etc). Nosotros vamos a modificar la opcion Type y vamos a escribir uno de los tipos que agregamos en el paso anterior.


Como se puede ver al seleccionar el tipo, el objeto cambia al color negro de esta forma es mas facil reconocer a que objetos ya le hemos dado propiedades.
Despues de dar propiedades a todos los objetos el mapa se vera de asi:


Con esto ya todos los objetos tienen propiedades por lo que podremos diferenciar cada uno cuando los pasemos a Libgdx.

Con esto terminamos con la parte 2 de este tutorial en la parte 3 podremos ver como pasar el mapa creado a Libgdx asi como crear los cuerpos de Box2d dependiento de cada una de las propiedades que agregamos en Tiled.

viernes, 10 de octubre de 2014

Tiled + Box2D Parte 1

En esta parte del tutorial vamos a utilizar Tiled para crear mapas.

¿Que es Tiled?

Tiled es un editor que permite el uso de "tiles" para crear facilmente mapas. Tambien se pueden agregar mas cosas como las posiciones de los enemigos, las posiciones de monedas, items del juego, etc. La siguiente imagen muestra un juego echo con Tiled.



¿Como funciona Tiled?

La creacion de mapas con Tiled es muy sencilla.
  • Se selecciona el tamaño del mapa
  • Se agregan las imagenes (tilesets) al mapa
  • Se colocan los tilesets en el mapa
  • Se agregan objetos al mapa
  • Se guarda el mapa como un archivo tmx
  • Se importa el archivo tmx al juego


Crea tu primer mapa

En esta parte del tutorial utilizaremos el tileset creados por Kenney. Se puede descargar directamente aqui el tileset que utilizaremos a lo largo de este tutorial.

Configuración

Despues de abrir Tiled vamos a File > New... para crear un nuevo mapa. Aparecera la siguiente ventana donde tenemos que configurar algunas opciones:


Orientation: Existen dos opciones que son isometric y orthogonal. Para este tutorial seleccionaremos Orthogonal. En cuanto a la vista isometrica se puede decir que es una forma de representar un objeto ee 3D en 2D.

Layer Format: Es el tipo de codificacion y compresion del mapa, en este tutorial se utilizara Base64(uncompressed) ya que la documentacion de LibGdx dice que si queremos que el mapa sea soportado en GWT debe tener este formato.

Tile size: Es el tamaño que tiene queda tile del tileset, en este caso cada tile es de 70x70 pixeles.

Map size: Es el tamaño de tiles que tendra nuestro mapa de ancho y de alto, en este caso 15 tiles de ancho y 15 tiles de alto.

Ahora es necesario agregar el tileset. Vamos a Map > New TileSet... Aparece la siguiente ventana:


Name: Nombre del tileset.

Image: Es la ruta donde esta el tileSet. En este tutorial hay que seleccionar el que descargamos anteriormente.

Use transparent color: Esta opcion hace que el color de la transparencia se muestre del color que seleccionamos. No se seleccionara esta opcion.

Tile width y Tile height: Es el ancho y la altura de cada tile, en este caso son de 70x70 pixeles.

Las demas opciones las dejaremos con valor de 0 pixeles ya que no son necesarias para este tutorial.

Al dar clic en OK Tiled se vera asi:


Agregar tiles

El siguiente paso es agregar los tiles al mapa, para esto es necesario ir a parte inferior derecha y seleccionar un tile. Se pueden seleccionar uno o muchos tiles al mismo tiempo en este caso solo seleccionare el tile del piso. Despues nos vamos al area gris donde podemos estampar los tiles en la cuadricula.


Si quisieras borrar algun tile en la parte superior se encuentra la opcion para borrar. Tambien es posible rotar e invertir los tiles presionando las teclas X y Z.

Despues de agregar algunos tiles, se tiene el siguiente mapa:


Agregar tiles en primer plano

En Tiled es posible agregar tiles que se sobrepongan sobre otros tiles para esto es necesario crear una nueva capa. Se selecciona la opcion Layer > Add Tile Layer.
Ahora se tiene una nueva capa llamada Tile Layer 2. Con la capa Tile Layer 2 seleccionamos una roca y la colocamos sobre algun tile del Tile Layer 1.


Despues de agregar algunos tiles en las diferentes capas tenemos un mapa como el siguiente:


Con esto terminamos la primera parte de este tutorial, en la parte 2 vamos a ver como agregar objetos y propiedades a estos para despues utilizalos en Libgdx y crear cuerpos de Box2D.

sábado, 28 de junio de 2014

Libgdx + Gradle + Admob (Google Play Services).

Este tutorial es especifico para android. Vamos a agregar un banner en la parte superior y mostraremos un anuncio de pantalla completa a nuestro juego.

Comenzamos con un proyecto nuevo, pueden revisar el tutorial de como crear proyectos Setting up, al cual agregarmos Google Play Services.


1.- Creamos una interfaz en el proyecto principal (core) en este ejemplo la interfaz se llamara AdHandler.


2.- Agrego un funcion que servira para mostrar el anuncio de pantalla completa cada vez que sea llamada.

3.- Creamos una objeto de la interfaz AdHandler en la clase MyNewGame y lo inicializamos en el constuctor.

4.- Despues creamos una interfaz grafica que solo va contener un boton, cada vez que este sea presionado se mostrara un anuncion de pantalla completa.

Como se puede ver creamos un ImageButton este boton servira para llamar la funcion showInterstitial() de la interfaz AdHandler, En nuestro ejemplo este es llamado en la funcion touchUp().

Si corremos nuestra aplicacion debe aparecer la siguiente pantalla. Podemos presionar la imagen pero no mostrara nada ya que aun no agregamos el codigo de los anuncios.



5.- Agregamos Google Play Services al proyecto de Android. Para esto es necesario abrir el archivo 'build.gradle' y agregar la siguiente linea al proyecto de android
  •   compile "com.google.android.gms:play-services:4.2.+"




6.- Hacemos un "refresh all" para que se actualizen las librerias.


"Despues de realizar el paso 6 la libreria de Google Play Services se debe agregar al proyecto, pero ya que el gradle aun se encuentra en sus etapas iniciales en Eclipse tambien es necesario agregar Google Play Services como una libreria."

Para agregar Google Play Services damos click derecho a nuestro proyecto de android, seleccionamos la opcion de propiedades y se abrira la siguiente ventana. 


Como se puede observar en la parte inferior derecha tenemos la opcion para agregar la libreria, la agregamos y damos click en OK.

7.- Agregamos los permisos necesarios al archivo Manifest.xml

8.- Agregamos el metadata tag y la actividad de admobs al archivo Manifest.xml

9.- Despues vamos a agregar un banner y un anuncio de pantalla completa como se puede ver en el siguiente codigo.
Una vez terminado nuestra aplicación se vera asi:

Recuerden si presionamos el boton del centro debe mostrar un anuncio de pantalla completa.

Y como siempre aqui les dejo el codigo: Codigo fuente

2.- Setting Up (Gradle)

Asumiendo que se han instalado las herramientas mínimas necesarias que son Eclipse y el Java JDK comenzarnos con la creación de nuestro primer proyecto de prueba.

Comenzamos descargando libGDXProject Setup.

1.- Ejecutamos la aplicación y nos mostrara la siguiente pantalla.



2.- Llenamos todos los campos:

  • Name:  Este campo es el nombre que tendra el proyecto
  • Package: El nombre de el paquete
  • Game class: El nombre de la clase principal
  • Destination: La ruta donde se va guardar el proyecto
  • Android SDK: La ruta donde se encuentra nuestro SDK de Android.

Para la mayoria de los tutoriales de este blog utilizaremos Box2D por lo que es necesario seleccionar la Extension Box2d.

3.- Presionamos el boton 'Generate' para crear el proyecto. 

4.- Abrimos Eclipse y seleccionamos la opcion importar proyecto. En esta parte es importate seleccionar la opcion de 'Gradle Project' mucha gente comete el error de seleccionar otras opciones que no importar todo lo necesario. 


5. Seleccionamos la ruta de nuestro proyecto y damos click en la opcion 'Build Model'.


6.- Selecciona los proyectos y da click en 'Finish'.


7.- Ya hemos creado e importado los proyectos a eclipse a continuacion vamos a ejecutar la aplicacion de escritorio.


  • Damos click derecho en el proyecto de escritorio.
  • Seleccionamos 'Run as'
  • Seleccionamos 'Java Application'
Se abrirá una ventana donde seleccionamos la clase DesktopLauncher del paquete que pusimos al generar nuestro proyecto. En mi caso el nombre del paquete es “com.tiarsoft.box2dtest”.



Damos clic en “OK” y veremos nuestra aplicación correr.


En este momento no hemos programado ni una sola línea de código por lo que solo veremos la pantalla de presentación de LibGdx.


En el siguiente tutorial  crearemos la estructura de nuestro banco de pruebas y agregaremos nuestro primer cuerpo a nuestro aplicación.



sábado, 24 de mayo de 2014

Zombi Killer : Juego de plataformas.

Recientemente estuve trabajando en este juego llamado Zombi Killer que consiste en ir matando zombies, recolectar items, comprar mejoras para tu arma, etc. Como se han de imaginar para este juego utilize Libgdx asi como Box2D, Tiled, Scene2D entre otros.

Descargen el juego y no olviden calificarlo. Si quieren que haga un tutorial sobre como se creo alguna parte del juego lo comentan aqui en el blog. Aqui estan algunas capturas.

 



 Google Play App store


domingo, 11 de mayo de 2014

8 .- Cuerpos y sprites (imagenes)

En este tutorial vamos a aprender como dibujar una imagen en nuestro cuerpo, por ejemplo si tenemos un cuerpo circular que representa una pelota dibujaremos una imagen de una pelota junto con nuestro cuerpo. Vemos la siguiente imagen:



Podemos observar que tenemos dos tipos de cuerpos uno circular para las pelotas y uno cuadrado para las cajas. Es necesario almacenar la información de los cuerpos para saber exactamente donde vamos a dibujar los sprites para esto he creado una clase interna llamada Figura donde voy a guardar el tipo, la posición y el ángulo actual del cuerpo.

La clase Figura es muy sencilla, en el constructor asignamos el valor de la posición así como el tipo de la figura, la función update recibe un cuerpo de donde tomaremos la posición y actualizaremos la de nuestro objeto así como el ángulo.  Es importante recordar que para cada caja o pelota que vemos en la imagen se necesita un objeto de la clase Figura.
La clase cuenta con dos constantes que nos servirán para determinar que tipo de figura es TIPO_PELOTA TIPO_CAJA. Tambien existe un vector donde almacenaremos la posición en X y en Y así como una variable llamada angleDeg para guardar el ángulo actual de la figura en grados.

En el constructor solo creamos el objeto position así como asignamos el tipo. Por ultimo en la función update() recibimos como parametro un cuerpo de donde tomaremos la posición y el ángulo y actualizaremos nuestras variables.

Después de crear la clase Figura continuamos con definir las variables que utilizaremos. Para comenzar definimos los objetos renderer y oWorld. Enseguida tenemos una lista que almacenara todos los cuerpos que existan en el mundo así como una para guardar todos los objetos de la clase Figura. Tambien tenemos dos objetos de la clase TextureRegion que prácticamente son las imágenes que vamos a dibujar en los cuerpos.

En el constructor creamos los objetos y las listas, cargamos las imágenes,  creamos el piso y agregamos una pelota y una caja con las funciones crearPelota() y crearCaja().

Las funciones crearPelota() y crearCaja() son muy similares ya que solo difieren en unas cuantas lineas de código  por lo que solo analizaremos crearPelota() a detalle. Para comenzar se crea un objeto de la clase Figura en el constructor asignamos su posición en X y en Y así como el tipo de figura que en este caso es TIPO_PELOTA. A continuación creamos la definición del cuerpo asignamos la posición tomando los valores de X y Y de nuestro objeto obj y decimos que es un cuerpo dinámico bd.type = BodyType.DynamicBody. Enseguida creamos la forma de nuestro cuerpo que en esta caso es circular CircleShape y le asignamos un radio de .15 metros. Luego creamos una fixtura donde le asignamos la forma, densidad, fricción y elasticidad. Por ultimo creamos el cuerpo, le asignamos la fixtura, le asignamos la información al cuerpo que en este caso es el objeto obj y tambien agregamos obj a la lista de figuras arrFiguras.add(obj).

Estas dos ultimas lineas oBody.setUserData(obj) arrFiguras.add(obj) son muy importantes ya que si no las agregamos no podremos actualizar la posición del objeto Figura.


En cuanto a la función crearCaja() la unica diferencia es el tipo de la figura así como la forma que ya no sera circular sino un cuadrado.

En la función update() creamos una figura nueva cada vez que damos clic a la pantalla, hay que recordar que Gdx.input.justTouched() regresa verdadero si la pantalla se acaba de tocar.  Se llama a la función oWorld.step(delta, 8, 6) para que se lleve a cabo la simulación.  Tambien vamos a iterar entre cada uno de los cuerpos que existen en el mundo, para esto primero es necesario obtener los cuerpos que existen esto se hace con la función oWorld.getBodies(arrBodies) que recibe como parametro nuestra lista que habíamos definido antes. Después ya estando dentro del ciclo obtenemos una cuerpo de la lista y la guardamos Body body = i.next() en el objeto body, vemos si la información del cuerpo es de la clase Figura en caso de que sea verdadero llamamos la función update de la figura almacenada en el user data del cuerpo.

Nota: En este ejemplo todos los cuerpos tienen como user data un objeto de la clase Figura y no era necesario hacer la condición if (body.getUserData() instanceof Figura) pero lo hicimos porque es buena practica.

Para finalizar tenemos la función draw() que explicare en dos partes.
En la primera parte actualizamos la cámara de la UI (Interfaz grafica) le asignamos la matriz de proyección a nuestro SpriteBatch (batcher) y dibujamos en pantalla una cadena de texto y los frames por segundo.

En la segunda parte actualizamos la cámara del mundo oCamBox2D asignamos la matriz de proyección al SpriteBatch, y llamamos la función drawFiguras() que explicare mas adelante,  y por ultimo dibuja los cuerpos con la función renderer.render(oWorld, oCamBox2D.combined)


Para finalizar el tutorial tenemos la función drawFiguras() que es la encargada de dibujar las imágenes sobre los cuerpos.
Es necesario iterar entre cada uno de los elementos de nuestra lista arrFiguras dependiendo del tipo de cada objeto vamos a dibujar o una caja o una pelota para esto creamos un objeto keyframe  y lo inicializamos de acuerdo al tipo de objeto. Y con nuestro batcher dibujamos el objeto keyframe.


---

Con esto finalizamos el tutorial 8. Recuerden que pueden descargar el código fuente


jueves, 8 de mayo de 2014

7.- Fuerzas, impulsos y velocidad lineal.

Como sabemos en Box2d existen fuerzas que cuando las aplicamos a los cuerpos crean una simulación real, esto nos permite mover los cuerpos, lanzar pájaros con una resortera, disparar, saltar, etc. 

Para aprender sobre las diferentes fuerzas vamos a crear 3 pelotas como se ve en la siguiente imagen.






Necesitamos un arreglo en donde almacenar los cuerpos de nuestras 3 pelotas al que llamare arrPelotas.

Como pueden ver se crea el arreglo arrPelotas en el constructor así como el vector velocidad, este ultimo es la fuerza "velocidad" que aplicaremos a nuestras 3 pelotas. Así que suponemos que las 3 pelotas se moverán de la misma forma, a la misma velocidad, pero no es así.

Tambien tenemos 4 funciones que son crearPiso(), crearPelotaIzquierda(), crearPelotaCentro() y crearPelotaDerecha(). Como su nombre lo indica la primera crea el piso y las otras crean las pelotas, en este tutorial omitiré la explicación de crear el piso ya que en tutoriales anteriores se ha explicado.

La creación de las pelotas es muy sencilla y si han seguido los tutoriales anteriores no tendrán ningún problema. Primero creamos un BodyDef donde asignamos la posición y el tipo de cuerpo que en este caso es dinamico. Después creamos una figura circular con CircleShape con un radio de .25 metros.
A continuación una fixtura con una densidad de 15 kgm/s^2 (kilogramo * metro / segundo ^2), fricción de .5 y elasticidad de .5.

Noten que guardamos el cuerpo en el arreglo de la siguiente manera arrPelotas[0] = oBody;

Para las 2 pelotas restantes es lo mismo lo unico que se modifica es la posición en X.


Por ultimo no hay que olvidar llamar la función step de nuestro mundo oWorld.step(delta, 8, 6); y tambien dibujar en pantalla los cuerpos del mundo renderer.render(oWorld, oCamBox2D.combined):.

Como pueden ver en la función draw()  tambien dibujamos los Frames por segundo de nuestra aplicación.

Aplicando las fuerzas

Se preguntaran que pasara si corremos la aplicación en este momento. La respuesta es nada ya que no hemos aplicado las fuerzas a las pelotas. Esto lo haremos dentro del constructor después de haberlas creado de la siguiente manera:
Recuerdan que habíamos guardado los cuerpos en el arreglo, esto para poder aplicar las fuerzas.
Si corremos la aplicación vemos que las pelotas no se mueven de la misma forma:


  • La primera a simple vista no hace nada
  • La segunda se mueve solo un poco
  • La tercera se mueve muy arriba


Se preguntaran que es lo que esta pasando. Vamos a analizar cada una de las funciones:

applyForceToCenter: Aplica una fuerza al centro comúnmente en Newtons en este caso hacia arriba, un ejemplo real seria bajarte de tu automóvil y empujarlo por un instante con una fuerza de 50 Newtons. ¿Que distancia recorrería? Normalmente no lo empujarías ni 3 metros. Aplicar una fuerza depende del tiempo y en el caso de nuestra pelota solo lo hacemos un instante por lo que la pelota nisiquiera se levanta.

applyLinearImpulse: Aplica una fuerza comúnmente en Newtons-segundos esto modifica instantáneamente la velocidad del cuerpo en el caso de nuestra pelota hacia arriba, como ejemplo real si empujamos nuestro automóvil con un fuerza de 50 Newtons-segundo ¿Que distancia recorrería?. Pero que pasa si empujamos una caja de carton con la misma fuerza ¿Que distancia recorrería nuestra caja?, La caja de carton alcanzaría una distancia muy grande ya que su masa es muy pequeña. Como podemos ver aplicar un impulso depende de la masa de nuestro cuerpo. 

Como ejercicio modifica la densidad de la pelota del centro, actualmente la densidad es de 15,  ¿Que pasa cuando pones una densidad menor?

setLinearVelocity: Por ultimo esta función cambia la velocidad del objeto sin depender del tiempo ni de la masa del cuerpo. En pocas palabras no importa si modificamos la densidad, la pelota siempre va alcanzar la misma altura.

Nota:

A nuestras pelotas las fuerzas siempre fueron aplicadas a su centro, en caso de que aplicáramos la fuerza en un lugar diferente existiria una rotación de los cuerpos.

---

Con esto finalizamos el tutorial 7. Recuerden que pueden descargar el código fuente

lunes, 5 de mayo de 2014

Codigo fuente tutoriales Box2D

A partir de hoy, subire el código fuente de cada uno de los tutoriales que veamos a lo largo de este blog. Lo pueden descargar y modificar a su gusto. 

Una vez que tengan importen el proyecto a eclipse lo pueden correr y tendrán una aplicación como esta donde podrán probar cada uno de los tutoriales.


Instrucciones

- Si no carga la aplicación utiliza Google chrome o algún navegador con soporte WebGL
- Presiona la tecla ESC para regresar al menu principal.



Descarga el código fuente  


¡Atención!

Este proyecto esta basado en el nuevo sistema Gradle adoptado por LibGdx

domingo, 23 de febrero de 2014

Tutorial parte 2: Flappy Bird basico con Box2D

Este tutorial es la continuación de como hacer un Flappy Bird básico con Box2d y Libgdx. Pueden encontrar la primera parte aquí.

La clase WorldGame

Esta es una de las clases mas importantes de nuestro juego por lo que la explicare en partes.

Las primeras constantes WIDTH y HEIGHT almacenan el tamaño de nuestro mundo, como ya lo habíamos comentado antes Box2D trabaja con metros, segundos, etc por lo que en este caso los valores son 4.8 x 8 respectivamente.

Después tenemos los estados que puede tener la clase WorldGame que son dos corriendo o juego terminado.

La siguiente constante TIME_TO_SPAWN_PIPE es el tiempo en segundos que tarda un tubería en aparecer y la variable timeToSpawnPipe acumula el tiempo transcurrido desde que apareció la ultima tubería.

El objeto oWorldBox  es el encargado de manejar todos los cuerpos, simulaciones físicas, memoria, etc.

En la variable score almacenamos la puntuación actual.

El objeto oBird almacena la información.

Luego tenemos un arreglo llamado arrTuberias este arreglo sirve para almacenar cada una de las tuberías que existen en el juego en un instante, es necesario crear un arreglo porque pueden existir mas de una tubería en pantalla.

Por ultimo tenemos un arreglo arrBodies que utilizaremos para almacenar todos los cuerpos del mundo e iterar sobre ellos para poder actualizar la información de nuestros objetos.


En el constructor creamos nuestro objeto oWorlBox, como ya se habia comentado antes tiene una gravedad negativa en Y de -13 m/s^2. Tambien creamos nuestros arreglos. El tiempo acumulado timeToSpawnPipe lo iniciamos en 1.5f para que las primeras tuberías aparezcan en cuanto iniciamos el juego.

Tenemos 3 funciones que explicaremos mas adelante, en pocas palabras crean al pájaro y ponen un limite superior e inferior en nuestro juego para que el pájaro no se salga de la pantalla.

Por ultimo ponemos el estado del juego en corriendo.


La función createBird() como su nombre lo dice sirve para crear el pájaro. Primero es necesario crear el objeto oBird y los parámetros que recibe son las coordenadas en X y Y donde se encuentra. Luego creamos un cuerpo que se encuentra en la misma posición que el objeto oBird y le ponemos una figura circular con radio de .25 metros.

Una parte muy importante de esta función es oBody.setUserData(oBird) con esta linea de código agregamos al cuerpo la información del pájaro, de esta forma sabres que este cuerpo en especifico pertenece al pájaro.

Las siguientes dos funciones simplemente ponen un cuerpo en la parte superior y otro en la inferior que actuaran como los limites del juego, el pájaro no puede atravesar estos cuerpos.

La función agregarTuberias() sera llamada cada vez que el tiempo acumulado en la variable timeToSpawnPipe alcance los 1.5 segundos. Esta función agrega la tubería inferior, la superior y el contador en medio de las tuberías. Es importante notar que la posición en X donde se agregan las tuberías siempre sera la misma y la posición en Y es la que cambia.

Luego tenemos la función agregarTuberia() aqui es donde se crea un objeto de la clase Tuberia para luego guardarlo en el arreglo de tuberías (arrTuberias). Primero tenemos que saber si es una tubería inferior o una superior y creamos la tubería. Se crea el cuerpo y se le da la velocidad que habíamos asignado en la clase Tuberia que en este caso es de -2 m/s^2. Una vez que se crear el cuerpo y se le asigna su fixtura agregamos al cuerpo la información de la tubería con la función oBody.setUserData(obj) y guardamos en el arreglo la tubería arrTuberias.add(obj).

La función agregarContador() es muy similar a agregarTuberia() la diferencia es que aqui agregamos el objeto contador, como este sera invisible no creamos un arreglo donde almacenar el objeto y solamente se asigna al cuerpo con la función oBody.setUserData(obj).

A continuación la función update() se encarga de actualizar cada uno de nuestros objetos, ya sea el pájaro, las tuberías o el contador. La función  oWorldBox.step(delta, 8, 4); es llamada para comenzar la simulación de los cuerpos dentro del mundo. Luego tenemos la función eliminarObjetos() que se explicara a detalle mas adelante por el momento solo diremos que sirve para eliminar los objetos y cuerpos que ya no se encuentran visibles en la pantalla. Las siguientes lineas sirven para ir acumulando el tiempo transcurrido y cuando se a alcanzado el tiempo necesario se agrega una tubería nueva.

En la siguiente parte tenemos un ciclo que iterara entre cada uno de los cuerpos del mundo, analizamos la información que tiene el cuerpo y la actualizamos conforme a su tipo.

Por ultimo revisamos si el estado del pajaro es Bird.STATE_MUERTO de ser verdadero ponemos el estado del mundo en STATE_GAMEOVER.

La función updateBird() se encarga de actualizar el objeto oBird, es importante recordar que el objeto body es el cuerpo del pájaro.  La primera parte llama a la función oBird.update(delta, body) que como sabes actualiza la posición de acuerdo a la posición del cuerpo y actualiza el stateTime de oBird. A continuación revisamos si la variable jump es verdadera y si el estado del pájaro es STATE_NORMAL si se cumplen las dos condiciones cambiamos la velocidad del cuerpo en Y, esto hace que el pájaro se mueva hacia arriba y podremos evitar las tuberías.

Las funciones updateTuberias() y updateContador() son muy similares, para poder actualizarlas primero revisamos que el estado del pájaro sea igual a Bird.STATE_NORMAL de lo contrario el juego esta por finalizar y ponemos sus velocidades en 0 para que ya no avancen. Si el estado del pájaro si es Bird.STATE_NORMAL llamamos la función update y enseguida revisamos si la posición actual es menor o igual a -5 esto sirve para saber si el objeto esta fuera de la pantalla y removerlo después para que ya no ocupe mas espacio en la memoria.

Cuando una tubería o contador están fuera de la pantalla es necesario eliminarlos para liberar memoria y no saturar nuestro dispositivo, para eso tenemos la función eliminarObjetos() que simplemente itera entre cada cuerpo del mundo y revisa si su estado es STATE_DESTRUIR en este caso destruye el cuerpo del mundo y lo elimina de nuestro arreglo de ser necesario.

La clase interna Colisiones sirve para detectar cuando dos cuerpos hacen contacto entre si, la función beginContact se llama automaticamente cuando dos cuerpos inician contacto y aqui revisaremos si el pájaro colisionó con un objeto contador o con cualquier otra cosa. Lo primero es separar el objeto contact en las 2 fixturas que chocaron, después revisa la información para saber si alguna de estas 2 fixturas es el pájaro de ser verdadero llamamos la función beginContactBirdOtraCosa() esta función recibe 2 parámetros que son la fixtura del pájaro y la del otro objeto con el que se colisionó.

En la función beginContactBirdOtraCosa() revisamos contra que se colisionó si fue el contador revisamos que el estado de este sea igual a STATE_NORMAL incrementamos la puntuación y podemos al contador en STATE_DESTRUIR para que en la siguiente actualización del mundo sea eliminado de la memoria. Si la colisión no fue con el contador solo revisamos si el estado del pájaro es STATE_NORMAL y llamamos la función oBird.getHurt() con lo que el estado cambiara a STATE_MUERTO y en la siguiente actualización el estado del juego se cambiara a STATE_GAMEOVER.

La clase GameScreen

La siguiente clase es la clase GameScreen que en pocas palabras muestra el juego al jugador y le permite interactuar con el. Es muy importante notar que esta clase hereda de Screens (ver tutorial 1). La clase GameScreen consiste de 3 estados: STATE_READY, STATE_RUNNING, STATE_GAME_OVER cada uno de estos estados tendra su propio metodo update() donde se realizaran tareas especificas.


La primera parte consiste en la declaración de las algunas constantes que son los estados del juego, un objeto de la clase WorldGame, un objeto WorldGameRenderer que analizaremos mas adelante, así como 3 imágenes llamadas getReady, tap y gameOver. El constructor inicializa las nuestros objetos asi como a las imágenes, a estas ultimas tambien les da una posición y las agrega getReady y tap al stage.


A continuación tenemos las funciones para actualizar cada uno de los estados: updateReady(), updateRunning() updateGameOver(). Dependiendo de en que estado nos encontremos se llamara a la función correspondiente. Hay que recordar que la función update() se llama automaticamente aproximadamente 60 veces por segundo (60 fps).


Para la función updateReady() revisamos si se a tocado la pantalla con la función Gdx.input.justTouched() en caso de que la condición sea verdadera desvanecemos las imágenes getReady y tap  en un lapso de .3 segundos ademas de cambiar al estado STATE_RUNNING.



En función updateRunning() creamos una variable boleana jump que sera verdadera si se toca la pantalla,  actualizamos el mundo (oWorld) pasándole como parámetros el tiempo y si se toco la pantalla o no. Después revisamos si el estado del mundo es igual a STATE_GAMEOVER de que sea así ponemos el estado de la pantalla GameScreen en STATE_GAME_OVER y agregamos la gameOver imagen al stage.

La función updateGameOver() es muy parecida a updateReady() pero aqui cuando se toca la pantalla en vez de cambiar el estado se pone una nueva pantalla GameScreen con la función game.setScreen(new GameScreen(game)) esto inicia otra vez el juego.


Para dibujar en pantalla nuestros objetos del juego así como la puntuación tenemos la función draw() al igual que la función  update() esta se llama automaticamente. Lo primero que se hace aqui es renderer.render(delta) que dibuja en la pantalla todos los objetos de nuestro objeto oWorld enseguida actualizamos la cámara y dibujamos la puntuación.

La clase WorldGameRenderer

Por ultimo esta clase lo que hace es dibujar todos los objetos del mundo (pájaro, tuberías, fondo) de acuerdo a sus posiciones y estado. 


Para comenzar definimos algunas constantes como son su ancho y  altura recordando que su valor es de 4.8 y 8 respectivamente ( recordemos que 100 pixeles es igual a 1 metro). El objeto renderBox nos permite dibujar las lineas de 'debug' de los cuerpos de nuestro mundo (Box2D).


La función render() que es llamada desde la clase GameScreen divide los objetos que se van a dibujar por su tipo. Primero dibujamos el fondo, enseguida las tuberías y al final el pájaro. Es necesario actualizar la cámara oCam.update() así como la matriz de proyección del SpriteBatch batcher.setProjectionMatrix(oCam.combined).



Este fragmento de código no tiene ciencia alguna solamente dibujamos el fondo iniciando en las coordenadas (0,0) con un tamaño de (4.8, 8) lo que llena completamente la pantalla con la imagen del fondo.


Después vamos a dibujar las tuberías, tendremos que iterar en nuestro arreglo de tuberías arrTuberias que se encuentra en nuestra objeto de la clase WorldGame. Revisamos que tipo de tubería es (superior o inferior) y dibujamos. Es muy muy importante tener en cuenta que el ancho y alto que seleccionemos para dibujar no tiene que ser el mismo ancho y alto que el cuerpo.

En esta linea batcher.draw(Assets.tuberiaDown, obj.position.x - .5f,obj.position.y - 2f, 1f, 4);  observamos que estamos dibujando con un ancho de 1 y altura de 4, e iniciamos en las coordenadas x-.5 y y-2, esta resta es necesaria para centrar la imagen en el cuerpo.


Para finalizar dibujamos el pájaro, primero vemos en que estado se encuentra para saber si repetimos la animación de aleteo o no. Y procedemos a dibujar.


--
Con esta segunda parte concluimos con el tutorial para crear un flappy bird basico. Pueden descargar el código fuente aqui.

Tratare de contestar todas sus preguntas que hagan así que no duden en preguntar.