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.


viernes, 21 de febrero de 2014

Tutorial parte 1: Flappy Bird basico con Box2D

En esta serie de tutoriales vamos a aprender como hacer un Flappy Bird básico en Box2d y Libgdx. Para los que no lo saben fue un exitoso juego para dispositivos móviles cuyo objetivo es dirigir un pájaro entre tuberías sin chocar con ellas. 



Así quedaria una vez terminado: 



Pueden descargar el código fuente



Definir el mundo

El primer paso es definir una resolución para nuestro juego, para nuestro flappy bird sera de 480 x 800 pixeles. Es necesario recordar que en Box2D las unidades utilizadas son metros, segundos, etc. Por lo que definir una tubería de 300 pixeles de altura en Box2D seria como tener un edificio de 300 metros de altura, casi tan alto como el Empire State.

Es necesario crear una escala. En mi caso yo siempre utilizo 100 pixeles es igual a 1 metro. Después de hacer esta escala suena mas logico crear una tubería de 3 metros (300 pixeles). 

El siguiente paso es definir los objetos del juego y el tamaño de sus cuerpos (Body) en metros.

  • El pájaro (Bird) sera una figura circular y tendra un radio de .25 metros.
  • Las tuberías serán figuras rectangulares con las siguientes medidas .85 x 4 metros.
  • Otro objeto que necesitaremos sera un rectángulo con el que colisionara el pájaro y se incrementara la puntuación, este objeto lo llamaremos contador, no sera visible y tendra unas medidas de .1 x 1.85 metros.


Con esto ya tenemos los tamaños de los cuerpos.

Otra cosa que necesitamos son las velocidades y aceleraciones que necesitaremos. Estos valores dependen mucho de como queremos que sea el juego, normalmente es necesario cambiarlos varias veces para encontrar los valores correctos. 

  • Para la gravedad tendremos (0 , -13) m/s^2, un poco mas de lo que tiene el planeta tierra.
  • Para el pájaro cada vez que vuela tendra una velocidad de 5 m/s^2 y hay que tener en cuenta que esta solo afectara el movimente en el eje Y, el movimiento en X sera nulo. 
  • Tanto las tuberías como el contador tendrán la misma velocidad que sera de -2 m/s^2, como pueden ver tiene una velocidad negativa por lo que se moverán de derecha a izquierda.

Una vez que hemos definido nuestro mundo el siguiente paso es crear los recursos (Assets) necesarios.

Hay que recordar que la resolución definida es de 480 x 800 por lo que los Assets dependen de esta resolución. 

Para empezar tenemos los elementos de la interfaz grafica:

Tap: 250 x 215 pixeles
Fondo: 480 x 800 pixeles

Get ready: 300 x 86 pixeles

Game over: 300 x 76 pixeles 




















A continuación los elementos del juego:

Para el pájaro es necesario tener 3 imagenes para crear la animación de que esta volando y en cuanto a las tuberías necesitamos una que este hacia arriba y otra hacia abajo.

 

 

Una vez que tenemos todos los Assets de nuestro juego es necesario empaquetarlos para esto utilizaremos un programa llamado Texturepacker y obtendremos un archivo que yo llame atlasMap.txt y una imagen llamada atlasMap.png como se ve en la siguiente imagen:


Texture atlas


Es necesario agregar este archivo dentro de nuestra carpeta assets que se encuentra en el proyecto de Android:



Implementando el juego

Una vez que tenemos los assets del juego el siguiente paso es la implementación del juego que es muy sencilla.

La clase Assets 

Esta clase contendrá referencias estáticas a objetos TextureRegion así como Animation, en pocas palabras en esta clase sera la encargada de cargar nuestros assets para poder utilizarlos mas adelante en el juego:


La función load()  sera llamada una sola vez al inicio del juego, y es responsable de cargar todos los objetos estáticos de la clase. 

La clase MainFlappyBird

Esta clase hereda de Game, desde aqui se llama la función Assets.load() y ponemos la pantalla del juego


La clase Screens

La clase abstracta Screen sirve para evitar crear código que se repite en cada una de las pantallas que creemos, en el caso de este juego solo crearemos una pantalla por lo que su utilidad no se vera reflejada, pero en otros caso ahorra mucho tiempo y lineas de código. 


Clases del juego

Antes de comenzar con la pantalla de juego (GameScreen) es necesario crear las clases que representan cada objeto del juego. Necesitaremos las siguientes:

  • Bird
  • Tuberia
  • Contador
Cada una de estas clases guardara la información como son la posición, el estado actual y el tiempo acumulado en un estado.

Tambien cuentan con un funciones update() que actualizara cada una de sus variables de acuerdo a su comportamiento.

Por ultimo contendrán funciones que alteran su estado, por ejemplo en caso que el pájaro haga colisión con una tubería su estado cambia.

La clase Contador


Esta clase no tiene mucha ciencia ya que solo almacenara la posición actual, el tiempo acumulado indica por cuanto tiempo a durado en el mismo estado.

Por ejemplo si "state==STATE_NORMAL" y el stateTime es de 10, quiere decir que a durado 10 segundos en STATE_NORMAL.

En caso de que "state==STATE_DESTRUIR" indica que este objeto debe ser removido en la siguiente actualización de la fisica del juego como veremos mas adelante.

La clase Tuberia

Esta clase es muy similar a la clase contador la unica diferencia es que aqui tenemos un tipo de tubería, esto quiere decir que si "tipo==TIPO_ARRIBA" se dibujara la tubería que va en la parte superior.


La clase Bird

Igual que en las clases Contador y Tuberia la clase Bird es muy sencilla y parecida a estas.

Como podemos hemos cambiado el nombre de la constante "STATE_DESTRUIR" por  "STATE_MUERTO", esto porque cuando "state==STATE_MUERTO" significa que el pájaro a chocado con una tubería y decimos que el pájaro a muerto =(.

Se ha agregado una función "getHurt()" que sera llamada cuando se detecte una colisión entre un pájaro y una tubería lo que cambiara el estado del pájaro.

--------

Por el momento hemos terminado con la primera parte del tutorial, en la segunda parte hablaremos sobre las clases restantes para que el juego quede funcionando al 100%

jueves, 20 de febrero de 2014

6.- Fricción, densidad y restitución

Como ya vimos antes los cuerpos tienen diferentes propiedades algunas de las más importantes son la fricción, la densidad y la restitución. Estas propiedades afectan el comportamiento de los cuerpos en nuestro mundo.

Densidad

La densidad multiplicada por el área de la figura es la masa del cuerpo. Entre más alta la densidad más pesado el cuerpo.
Es la fuerza de rozamiento que existe entre dos cuerpos en contacto. En box2D esta fuerza se aplica a los cuerpos con valores desde 0 cuando no existe la fricción hasta 1 cuando la fricción es muy alta.



Fricción


Para conocer cómo se comporta la fricción en Box2D creamos un nuevo proyecto base, donde agregamos un piso con una inclinación y ponemos una caja.



Como pueden ver se agregaron las lineas donde a la caja le damos una densidad de 1 y una fricción de 0.

                                           fixDef.density = 1f;
                                           fixDef.friction = 0f;

Corremos nuestra aplicación: 


Podemos observar que la caja se desliza desde la parte superior hasta la parte inferior. Esto debido a que la fricción es de 0 ( sin fricción). ¿Que pasara si le damos a nuestra caja una fricción de 1?

Restitución

Es la capacidad de rebote de un cuerpo, esto quiere decir que tanto va rebotar un cuerpo después de una colisión y como la fricción tiene un valor que va desde 0 que es sin rebote hasta 1 que es rebote máximo.

Para probar la utilizaremos el mismo ejemplo anterior de la caja, pero esta vez agregaremos una restitución de 1 con la siguiente linea de código .

                                           fixDef.restitution = 1f;

¿Que pasa con el cuerpo?. Con una restitución de 1 el cuerpo al colisionar con el piso rebota, ya no solo choca con el piso y se desliza como se ve en la siguiente imagen:


Conclusion
En este tutorial aprendimos sobre algunas de las propiedades mas importantes que pueden tener los cuerpos en el mundo. El simple echo de modificar alguna de estas propiedades puede hacer que el cuerpo actué  de forma totalmente diferente.




5.- Tipos de cuerpos: Dinámicos, estáticos y cinematicos

En box2D existen 3 tipos de cuerpos: dinámicos (dynamic), estáticos (static) y cinemáticos (kinematic).

Cuerpos estáticos (static)

Los cuerpos estáticos no reaccionan a ninguna colisión, fuerza o impulso por lo que no se mueven.


1.- Para nuestro ejemplo anterior agregaremos un cuerpo estático que actuará como el piso, por lo que nuestra pelota al caer no desaparecera de la pantalla. Creamos una funcion llamada crearPiso():


Como podemos ver en la definición del cuerpo decimos que es estático:



                              bd.type = BodyType.StaticBody;

Luego creamos una linea del tamaño del ancho de la pantalla:

                              EdgeShape shape = new EdgeShape();
                              shape.set(0, 0, SCREEN_ANCHO, 0);

Al terminar nuestra aplicacion se vera asi:



Como pueden ver tenemos la misma pelota, pero ahora tenemos una línea verde que no se mueve.

Cuerpos dinámicos (dynamic)

Los cuerpos dinámicos si reaccionan a fuerzas, impulsos, colisiones y a otros eventos del mundo. En nuestra aplicacion la pelota es un cuerpo dinámico que reacciona a la fuerza de gravedad por lo que es atraída hacia abajo.

Cuerpos cinemáticos (kinematic)

Los cuerpos cinemáticos son muy parecidos a los cuerpos estáticos ya que no reaccionan a las fuerzas por ejemplo en una colisión, pero a diferencia de los estáticos los cinemáticos si se pueden mover. 

1.- Creamos una funcion llamada crearKinematic()


Ahora en la definicion del cuerpo decimos que es un cuerpo cinematico

                         bd.type = BodyType.KinematicBody;

Y la figura que tendra sera de una caja, en este caso un rectángulo

                        PolygonShape shape = new PolygonShape();
                        shape.setAsBox(.1f, .25f);

Lo haremos girar en sentido contrario a las manecillas del reloj con la siguiente linea de codigo

                      oBody.setAngularVelocity((float) Math.toRadians(360));

es importante recordar que Box2D trabaja con radianes y no con grados. Nuestra aplicacion se vera asi, donde el cuerpo cinemático es de color azul y se encuentra girando.




Poniendo todo junto

Todo nuestro codigo se veria asi:


Y aqui les dejo la aplicacion funcionando ( si no se puede ver, recomiendo que utilizar google chrome).

Hice una pequeña modificación para cuando toquen la pantalla la pelota caiga nuevamente desde arriba. 








miércoles, 19 de febrero de 2014

4.- El mundo, cuerpos, figuras y fixturas

El mundo (World)

Cada aplicación con Box2D comienza con la creacion de un objeto del tipo World. La clase World se encarga de manejar la memoria, objetos y la simulación.

1.- La creación del mundo es muy sencilla. Primero, necesitamos un vector que define la gravedad.

                    Vector2 gravedad = new Vector2( 0, -9.8f ); // Gravedad de la tierra 9.8 m/s/s

La gravedad afecta a cada cuerpo dinámico que se encuentra en nuestro mundo en este caso x=0 lo que significa que no hay gravedad horizontal e y=-9.8f que es casi la misma gravedad que la Tierra.

Si quisiéramos un mundo sin gravedad nuestro código se vería así

                   Vector2 gravedad = new Vector2( 0, 0);

2.- Otro parametros que necesita nuestro mundo es, si permitimos que nuestros cuerpos puedan dormir. Esto significa que cuando un cuerpo entra en reposo el mundo deja de hacer simulaciones en el lo que incrementa el rendimiento de nuestra aplicacion.

                    boolean dormir = true;

3.- Finalmente podemos crear nuestro mundo de con el siguiente código.

                   World oWorld = new World( gravedad, dormir);

4.- Corriendo las simulaciones: Box2D utiliza un algoritmo computacional llamado "integrator" que simula la física del mundo en pasos discretos de tiempo. Para simular tenemos que escoger un paso de tiempo ( time step). Normalmente los juegos corren a 60fps y tienen un "time step" de  1/60 segundos.
Para nuestros ejemplos utilizaremos el tiempo transcurrido desde el frame pasado y el actual, delta. 

Pero no solo necesitamos un time step. En cada paso de la simulación los cuerpos son actualizados dependiendo de las fuerzas que actúan en el. El algoritmo encargado de esto se llama "constraint solver".

Existen 2 fases en la simulación, la fase de velocidad y la fase de posicion. En la fase de velocidad las cuerpos se mueven de acuerdo a sus impulsos y en la fase de posicion se ajustan las posiciones para prevenir que se traslapen los cuerpos.

Por lo que entre más iteraciones más precisa será nuestra simulación pero menor rendimiento. Para nuestro tutorial utilizaremos 8 iteraciones de velocidad y 6 iteraciones de posicion. por lo que nuestro código se verá así:

                         oWorld.step(delta, 8, 6);


Cuerpos (Bodies)

Los cuerpos son parte fundamental de Box2D, cualquier cosa que se mueva con la que puedas interactuar en el mundo son cuerpos.

Existen cuerpos diferentes tipos de cuerpos: dinámicos, cinemáticos y estáticos.

Los cuerpos tiene distintas formas (figuras) : Circulares, cuadrados o cualquier tipo de polígono.

También tienen diferentes propiedades como son: densidad, fricción , restitución, velocidad,  posicion en el mundo, etc.

1.- Para crear un cuerpo en nuestro mundo el primero paso es crear un BodyDef que es la definición del cuerpo y sirve para mantener toda la información necesaria para crear nuestro cuerpo.

                    BodyDef bd = new BodyDef();
                    bd.position.set( 4 , 2 );
                    bd.type = BodyType.DynamicBody;

Creamos la definición del cuerpo y la posicionamos en el centro de la pantalla. Recordemos que el tamaño de nuestra cámara es de 8f x 4.8f. Y por último decimos que nuestro cuerpo será dinámico.

Nuestro cuerpo tendrá una figura circular por lo que tenemos que crear una figura circular (Shape)

Figuras ( Shapes) 

Una figura es un objeto geométrico, como un circulo, cuadrado o un polígono, en caso de que sea un polígono debe ser un polígono convexo ya que Box2D solo funciona con polígonos convexos.

Para crear nuestra figura circular utilizamos el siguiente código:

                   CircleShape shape = new CircleShape();
                   shape.setRadius(.1f);

Simplemente creamos un objeto circular y le damos radio de .1f, ahora es momento de crear una fixtura para nuestro cuerpo.

"Fixturas" (Fixture)

Las "fixturas" sirven para dar tamaño, forma y otras propiedades como densidad, fricción y restitución. 

Las principales propiedades son:

  • Densidad - Que tan pesado es un cuerpo en relación con su área
  • Fricción - Que tan resbaloso es el cuerpo
  • Restitución - Que tanto rebote tiene el cuerpo
  • Figura (Shape) - Puede ser un cuadrado, polígono o un circulo.
Existen más propiedades que iremos explicando más adelante en estos tutoriales. Por el momento no nos vamos a preocupar de las propiedades y vamos a crear una "fixtura".


                    FixtureDef fixDef=new FixtureDef();
                    fixDef.shape=shape;

Creamos la definición de la "fixtura" y le asignamos el cuerpo circular que creamos anteriormente.

Agregar el cuerpo al mundo

Para agregar nuestro círculo al mundo e creado una funcion llamada crearPelota() y contiene todo lo que aprendimos anteriormente.



a este código se han agregado dos líneas más que son

                    Body oBody = oWorld.createBody(bd);
                    oBody.createFixture(fixDef);

En la primera línea creamos el cuerpo con la definición del cuerpo que creamos anteriormente, y le agregamos a nuestro cuerpo la "fixtura".


Poniendo todo junto

Agregando todo el código a nuestro código base nuestra clase se deberá ver así. 



Ahora solo queda correr nuestra aplicacion como ya habíamos aprendido en tutoriales anteriores. 



Y nuestra aplicacion se vera asi.


Una pelota en medio de la pantalla que cae y desaparece de nuestra vista.
Pueden hacer pruebas cambiando el radio de la pelota, cambiando los valores de la gravedad, etc.

Conclusión

En este tutorial vimos como crear el mundo y como hacer que nuestro mundo corra las simulaciones.

También aprendimos que son los cuerpos, que tipos de formas pueden tener y como agregarlos al mundo.
















lunes, 17 de febrero de 2014

3.- Codigo Base

Después de haber creado nuestro proyecto y haberlo importado a Eclipse  abrimos nuestra clase principal que en mi caso es “MainBox2D.java”.



Al abrir la clase nos encontramos con que tiene ya mucho código, este código es el que sirve para mostrar la pantalla de presentación de Libgdx, Borraremos todo el código que este dentro de la clase y copiamos y pegamos el siguiente código (Teniendo en cuenta de mantener siempre el nombre de nuestra clase igual).


¿Para que sirve el código anterior?

Es el código base que utilizaremos en todos nuestro proyectos, esto quiere decir que cada vez que en un tutorial creemos un proyecto nuevo siempre debe contener por lo menos este código.

En Libgdx nuestra aplicación tiene un ciclo de vida definido, que esta gobernado por los estados de nuestro programa, como son crear, pausar, resumir, etc.

Para poder acceder a estos eventos del ciclo de vida debemos implementar la interfaz
ApplicationListener y pasar una instancia de esta implementación a cada una de las implementaciones de Application en cada unos de nuestros objetivos (Android, iOS, Desktop).

El siguiente diagrama muestra el ciclo de vida de nuestro programa.


Pero ¿donde esta el ciclo principal?

¿Qué es el ciclo principal? (Main loop)

Es el componente principal de cualquier juego desde el punto de la programación. Permite al juego correr fluidamente. Los programas mas tradicionales responden a la interacción del usuario sin hacer nada al respecto. Por ejemplo un procesador de texto muestra el texto conforme el usuario lo ingresa, pero si el usuario no ingresa nada el procesador de texto no hace nada.

En cambio en los juegos es diferente, el juego debe seguir operando aun si el usuario no hace interacción con el juego. Nuestro main loop permite esto.

El pseudo código de un main loop

while( user doesn't exit )  
        check for user input  
        run AI  
        move enemies  
        resolve collisions  
        draw graphics  
        play sounds
end while


En Libgdx no existe un main loop explicito, pero el método render() se puede decir que es el cuerpo de nuestro main loop.

Conclusion
En este tutorial mostramos el código base que tendrán todas nuestras aplicaciones de ejemplo que realizemos a lo largo de estos tutoriales.

Se explico el ciclo de vida de una aplicación de Libgdx y aprendimos cual es el MainLoop de nuestra aplicación.