lunes, 28 de octubre de 2013

Acciones

Acciones de un actor.


Saludos voy a dedicar éste articulo a explicar algunas de las acciones, en libGdx las acciones están para facilitarnos el trabajo a los programadores, y permiten a un actor hacer cosas de manera sencilla, mover, escalar, etc...

Acciones.

¿Donde creamos las acciones? Las acciones las podemos crear dentro de una classe actor o bien fuera en la logica del juego, y en cualquier momento podemos añadirle una acción a un actor, la acción se ejecutará y cuando acabe se eliminará.

Acciones de Movimiento.

MoveToAction : Mover hacia una coordenada (X,Y).

Intanciamos la acción.

MoveToAction acc = new MoveToAction();
//Hacia la posición 700x,300y
acc.setPosition(700, 300);
//3 Segundos
acc.setDuration(3f);
//Añadir la acción al actor.
this.addAction(accion);
 
MoveByAction: Mover X e Y unidades un actor.

Intanciamos acción.

MoveByAction acc2 = new MoveByAction();
acc2.setDuration(10f);
//Cantidad de movimiento, en éste caso se moverá 300 unidades hacia abajo.
acc2.setAmount(0, -300);
 
RotateToAction: Rotar hasta un ángulo determinado.

 acc = new RotateToAction();
//Angulo a rotar. Un valor positivo lo hará girar en sentido contrario a las agujas del reloj, y //viceversa.
 acc.setRotation(360);
//Tiempo
acc.setDuration(1f);
//Añadir Acción
this.addAction(acc);
 
RotateByAction: Rotar tantos grados un actor.

 acc = new RotateByAction();
 acc.setAmount(360);
acc.setDuration(1f);
this.addAction(acc);


Origen de Rotación, al yo rotar una imagen tengo que tener en cuenta el punto de origen central, por defecto, el punto de origen de un actor viene a 0, la esquina inferior izquierda, al yo modificar éste punto puedo hacer que gire sobre si mismo, sobre una esquina, basta con usar el método setOriginX(float) y setOriginY(float). Adjunto una imagen explicativa.





IMPORTANTE! No basta únicamente con crear la acción en el actor y a funcionar, una acción cambia los atributos del actor, que quiero decir con ésto, yo en mi actor, tengo un constructor y un método draw en el que dibujo lo que me interese, al dibujarlo, tengo que darle al método draw los atributos del actor, si pongo un número únicamente no me variará en absoluto y la acción se ejecutará bien, pero el método draw seguirá dibujando ese valor, pongo un  breve ejemplo de nuestro pez:

package com.Firedark.libgdxspain;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.actions.MoveByAction;
import com.badlogic.gdx.scenes.scene2d.actions.RotateToAction;

public class Pez extends Actor {
    LibgdxSpain game;
    TextureRegion RegionPez;
    Texture pez;
    RotateToAction rotate;
    MoveByAction move;
    public Pez(LibgdxSpain game){
       
        this.game = game;
        //Atributos del actor. Posicion, Tamaño, y Origen de rotación.
        setPosition(50,50);
        setHeight(64);
        setWidth(64);
        setOriginX(getWidth()/2);
        setOriginY(getHeight()/2);
        //Mi accion Move
        move = new MoveByAction();
        move.setAmount(0, -300);
        move.setDuration(10f);
        //Mia Accion Rotate
        rotate = new RotateToAction();
        rotate.setRotation(-720);
        rotate.setDuration(1f);
        //añadir acciones
        this.addAction(move);
        this.addAction(rotate);
       
        pez = new Texture(Gdx.files.internal("data/Images/pez.png"));
       
        RegionPez = new TextureRegion(pez);
   
    }
   
   
    public void draw(SpriteBatch batch,float parentAlpha){
       
        //Metodo Draw, como veis, me traigo todos los atributos de la classe actor, de ésta manera
        //Si yo creo una acción rotate, el valor de mi actor Rotation variará en los segundos que
        //he asignado.
        //Observar que he usado un método draw mas completo que en situaciones anteriores, existen varios
        //métodos para texturas y regiones de textura.
        batch.draw(RegionPez, getX(), getY(), getOriginX(), getOriginY(), getWidth(), getHeight(), getScaleX(), getScaleY(), getRotation());

       
    }

}


Acciones Paralelas y Repetitivas.

Puede que necesitemos crear una acción repetitiva, que se ejecute continuamente o bien hacer 2 acciones paralelamente dentro de una sequencia, para ello podemos usar los tipos de acción:

Repetir acciones:

//Éste es un ejemplo para repetir continuamente "FOREVER" la acción rotate anterior del pez.
        RepeatAction repeat;
        repeat = new RepeatAction();
        repeat.setCount(repeat.FOREVER);
        repeat.setAction(rotate);
        //Añadimos al actor únicamente la acción repeat, que ya contiene la rotate.
        this.addAction(repeat);
Acciones paralelas:
     
        //Mucho no hay que explicar, és una accion que contiene las 2 acciones anteriores, la cual
        //las ejecuta a la vez.
        ParallelAction paralel;
        paralel = new ParallelAction();
        paralel.addAction(move);
        paralel.addAction(repeat);
       
        this.addAction(paralel);

Aunque parezca que las acciones paralelas no sirvan para nada no es cierto, en el siguiente tutorial vereís que usamos una sequencia de acciones, la cual se ejecutan una detrás de otra, sin las acciones paralelas seria imposible ejecutar 2 acciones a la vez dentro de una secuencia. Aqui teneís el tutorial de la semana:

Tutorial práctico: Splash Screen, acciones alpha y secuencias.

Las accion alpha nos servirá para modificar el canal alpha de una imagen, no podemos tratar directamente con Texturas y perderemos la capacidad de rotación y movimiento, pero nos permitirá hacer cosas interesantes. Como ejemplo práctico vamos a crear una pantalla de transición al empezar nuestro juego, con un logo que crearemos previamente.


1er Paso, Crear nuestra Screen, añadirle el Stage, lógica, etc , podeís mirar el tutorial del articulo anterior:

http://libgdxspain.blogspot.com.es/2013/10/actores-y-escenarios.html

2º Paso, preparar nuestras imagenes:



  Rectangulo Negro de 1024x1024.

 Logo de 1024 x 1024.

Podeís crear una imagen de 1024x1024 y insertar las 2 dentro para aprovechar espacio ya que sólo usaremos 800x480 en cada una de ellas.

3er Paso, Crear nuestro actor.

package com.Firedark.libgdxspain;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.actions.AlphaAction;
import com.badlogic.gdx.scenes.scene2d.actions.DelayAction;
import com.badlogic.gdx.scenes.scene2d.actions.SequenceAction;
import com.badlogic.gdx.scenes.scene2d.ui.Image;

public class SplashBlack extends Actor {
    LibgdxSpain game;
    //Atributos de las acciones alpha, Delay y la acción sequencia.
    AlphaAction alpha,alpha2;
    //La acción delay es una simple acción de espera, no hace nada.
    DelayAction delay;
    //Insertaremos nuestras acciónes en otra accion secuencia que nos ejecutará las cosas en orden.
    SequenceAction sequence;
    //Textura del Rectangulo, Region...
    Texture BlackRectangle;
    TextureRegion br;
    //Imagen, éste es el objeto que nos permite modificar su color alpha.
    Image ImgBR;
    //Nos servirá para saber cuando a terminado la acción.
    boolean fin;
   
    public SplashBlack(LibgdxSpain game){
       
        this.game = game;
        //Instanciamos Secuencia
        sequence = new SequenceAction();
        //Creamos las acciones alpha 1 y 2, como veís solo le damos la cantidad de alpha que queremos
        //alcanzar en 2 segundos de duracion.
        alpha = new AlphaAction();
        //Alpha a 0, Opaco.
        alpha.setAlpha(0f);
        alpha.setDuration(2f);
       
        alpha2 = new AlphaAction();
        //Alpha a 1, Transparente.
        alpha2.setAlpha(1f);
        alpha2.setDuration(2f);
        //La accion de espera de 3 segundos.
        delay = new DelayAction();
        delay.setDuration(3f);
        //Añadimos a la sequencia las tres acciones.
        sequence.addAction(alpha);
        sequence.addAction(delay);
        sequence.addAction(alpha2);
        //Y finalmente añadimos al actor la sequencia.
        addAction(sequence);
        //Cargamos Textura y Region.
        BlackRectangle = new Texture(Gdx.files.internal("data/Images/black.jpg"));
        br = new TextureRegion(BlackRectangle,0,0,800,480);
        //Creamos el objeto Imagen y le pasamos como parametro la region de nuestra textura.
        ImgBR = new Image(br);
        //Colocamos a 1 el valor alpha de nuestra imagen, por lo tanto la hacemos transparente.
        ImgBR.getColor().a = 1;
    }
   
   
    public void draw(SpriteBatch batch,float parentAlpha){
        //Continuamente le cargamos el valor de nuestro actor alpha, al valor de la imagen alpha.
        ImgBR.getColor().a = getColor().a;
        //Así se dibuja una imagen.
        ImgBR.draw(batch, parentAlpha);
        //Las acciones una vez finalizadas se eliminan no? Ésto nos ayuda a que cuando no queden acciones
        //En la secuencia habrá terminado.
        if(sequence.getActions().size == 0){
            //Un pequeño ejemplo para ir de una pantalla a otra, al terminar nuestra acción nos cambiará
            //de pantalla.
            game.setScreen(game.pJuego);
            this.remove();
        }
    }

}


 
4º Paso, añadir en nuestra logica el actor creado:

package com.Firedark.libgdxspain;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.ui.Table;

public class LogicalSplash extends Table {
    LibgdxSpain game;
    //Creamos el atributo para nuestro actor anterior Background
    Texture Tbackground;
    TextureRegion background;
    SplashBlack sb;

    public LogicalSplash(LibgdxSpain game, SplashScreen screen){
       
        //El metodo setbounds coloca un Rectangulo (X,Y,Width,Height)
        Tbackground = new Texture(Gdx.files.internal("data/Images/firegames.jpg"));
        background = new TextureRegion(Tbackground,0,0,800,480);
        setBounds(0, 0, 800,480);
        setClip(true);
        this.game = game;
       
        //Instanciar Actores
   
       
        sb = new SplashBlack(game);
        //Añadiendo Actores
       
   
        addActor(sb);
       
    }
   
   
    //Metodo act se ejecuta al igual que el render, es donde insertaremos la lógica.
    @Override
     public void act(float delta) {
      super.act(delta);
   
    }
   
     @Override
     public void draw(SpriteBatch batch, float parentAlpha) {
      batch.setColor(Color.WHITE);

//Aqui dibujamos nuestro fondo.
      batch.draw(background,0,0);
      super.draw(batch, parentAlpha);
     }

}


 
Y éste será nuestro resultado!





Espero que haya sido un artículo productivo y hayaís aprendido ya a mover, rotar y manejar las acciones de un actor, hay alguna acción mas que podeis estudiaros en la API de libgdx, recomiendo por lo menos mirar para que sirven.


Gracias a todos y se agradecen como siempre, comentarios, quejas, +1, etc... Espero que si estaís haciendo algo interesante me lo enseñeís! Suerte a todos y nos vemos la próxima semana con listeners!! ^^

lunes, 21 de octubre de 2013

Actores y Escenarios.

Actores y Escenarios.

Saludos de nuevo, ésta semana nos vamos a meter de lleno en la creación de escenas 2D, al crear una escena 2D tendremos varias ventajas que hasta ahora no teniamos, entre ellas podremos crear actores, que son objetos con su propio sistema de coordenadas, control de gestos con listeners y creación de acciones.

Hay muchas maneras de programar y muchas maneras de hacer las cosas, yo en éste blog explico como sabreís  mis experiencias personales y mis conocimientos adquiridos, digo ésto porque me gusta separar lo más posible la estructura y tener un poco de orden, que al principio tal vez no haga falta, pero con el tiempo es prácticamente necesario.

Estructura Actual.






Estructura Nueva.




Como veís en la nueva estructura añadiremos una classe exclusivamente para la lógica del juego en la cual podremos cargar los diferentes actores. El escenario lo tendriamos en la classe Screen y le añadiriamos el actor Logica que es una "Table" una classe preparada para alojar más actores, imaginaosla como una pizzarra en blanco. Bien, vamos a empezar por el final, y iremos escalando hasta la classe Screen que ya conocemos.

Actores.

Primero de todo vamos a crear una classe que extienda de "Actor" y vamos a añadirle un par de métodos para luego moverlo.


package com.Firedark.libgdxspain;
import com.badlogic.gdx.scenes.scene2d.Actor;

public class Background extends Actor{
   
     //Método Constructor
    public Background(){

    }
   
    //Método Draw, funciona para dibujar el actor, si el actor tiene alguna imagen podemos dibujarla
    //aqui con con el SpriteBatch del método.
    public void draw(SpriteBatch batch,float parentAlpha){    
    }
}

Ésto es un pequeño ejemplo de un actor vacío, dentro del mismo podriamos añadir acciones, listeners, etc, no obstante nos concentraremos en éste articulo a la creación de la estructura así que voy a crear un actor estático sin nada más que nuestro fondo.

package com.Firedark.libgdxspain;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Actor;

public class Background extends Actor{
   
    //Atributos, me traigo a mi actor la instancia de "Game" de siempre.
    LibgdxSpain game;
    //Creo los atributos para la gestión de la imagen del fondo.
    TextureRegion background;
    Texture Tbackground;
   

    public Background(LibgdxSpain game){
        this.game = game;
        //Éstos són de los métodos básicos que nos ofrecen los actores, posición y tamaño.
        setPosition(0,0);
        setHeight(480);
        setWidth(800);
        //Instancio las texturas y la region.
        Tbackground = new Texture(Gdx.files.internal("data/Images/oceanbackground.jpg"));
        background = new TextureRegion(Tbackground,0,0,800,480);
    }
   
   
    public void draw(SpriteBatch batch,float parentAlpha){
//En el método draw uso otros métodos de "Actor", recibiendo la coordenada X y Y que he puesto
//con el método del constructor setPosition(X,Y) .
        batch.draw(background, getX(), getY());
       
    }

}

Bien, éste actor al añadirlo a un Stage, nos dibujará nuestro fondo, así que vamos a crear nuestra classe donde almacenaremos y añadiremos todos nuestros actores.


Logica. El actor Table.

El actor Table va a ser nuestro intermediario entre el Stage (Escenario) y los actores, y nos servirá para alojar toda la lógica de esa pantalla, en caso de que sea el juego que hemos ido creando en los tutoriales, pondriamos toda la parte de la gamba y el pez, las rocas, los bucles de las colisiones, etc.

Pongo el código y lo explico en los comentarios.

package com.Firedark.libgdxspain;

import com.badlogic.gdx.scenes.scene2d.ui.Table;

//extiende de Table
public class LogicalSplash extends Table {
    LibgdxSpain game;
    //Creamos el atributo para nuestro actor anterior Background
    Background bg;
   
    public LogicalSplash(LibgdxSpain game, SplashScreen screen){
        //El metodo setbounds coloca un Rectangulo (X,Y,Width,Height)
        setBounds(0, 0, 800, 480);
        setClip(true);
        this.game = game;
       
        //Instanciar Actores
        bg = new Background(game);                       
        //Añadiendo Actores
        addActor(bg);
       
    }
       
    //Metodo act se ejecuta al igual que el render, es donde insertaremos la lógica.
    @Override
     public void act(float delta) {
      super.act(delta);    
    }
    //Método Draw contiene el SpriteBatch para dibujar.
    @Override
     public void draw(SpriteBatch batch, float parentAlpha) {
      batch.setColor(Color.WHITE);
      super.draw(batch, parentAlpha);
     }
}

Añadiendo lógica al Screen

Finalmente vamos a añadir nuestra lógica al Screen:

 package com.Firedark.libgdxspain;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.scenes.scene2d.Stage;

public class SplashScreen implements Screen{

    private LibgdxSpain game;
    //Creamos un Stage y el actor LogicalSplash
    private Stage stage;
    private LogicalSplash ls ;
  
    public SplashScreen(LibgdxSpain game){
        this.game = game;
        //Instanciamos el Stage.
        this.stage = new Stage();
      
    }
  
  
    @Override
    public void show() {
        //Ésto es necesario para procesar entradas dentro del stage, para listeners y demás.
        Gdx.input.setInputProcessor(stage);
        //Instancio el actor LogicalSplash anterior
        ls = new LogicalSplash(game,this);
        //Se lo añado al Stage.
        stage.addActor(ls);
    }
  
    @Override
    public void render(float delta) {
        //Ésto pintará de color Negro la pantalla para cuando usemos otras resoluciones si decimos que
        //mantenga las proporciones.
            Gdx.gl.glClearColor(0, 0, 0, 1);
            Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
            //Llama al metodo act y draw del stage.
            stage.act(delta);
            stage.draw();
      
    }

    @Override
    public void resize(int width, int height) {
        //Ésto mantendrá la resolución de la pantalla y ajustará la camara del Stage.
        stage.setViewport(game.w, game.h, true);
        stage.getCamera().translate(-stage.getGutterWidth(),
                -stage.getGutterHeight(), 0);
      
    }


    @Override
    public void hide() {
        //Al ocultar perdemos el procesador de entradas del stage.
         Gdx.input.setInputProcessor(null);
      
    }

    @Override
    public void pause() {

      
    }

    @Override
    public void resume() {
            
    }

    @Override
    public void dispose() {

      
    }

}

Con ésto ya nos cargará el Stage y el actor Background. Próximamente añadiré acciones y listeners de los actores así como detectores de gestos, espero hacerlo antes de la semana que viene. Gracias y espero que sea útil el contenido nuevo sobre las scenes2d. Saludos!

martes, 15 de octubre de 2013

Preferencias

Preferencias

Saludos de nuevo, en ésta nueva entrada vamos a tocar el tema de las preferencias, las preferencias son datos permanentes que se alojan en la memoria del dispositivo, pc, etc... Las preferencias nos serán muy útiles para guardar los datos de usuario más comunes, el volumen que quiere en la aplicación, los records personales de nuestro arcade, partidas jugadas, y cualquier cosa que necesitemos que se mantenga en memoria  una vez se cierre nuestro juego.




Instanciando el objeto Preferences

Para guardar nuestras preferencias primero deberemos instanciar la clase Preferences de libgdx, recomiendo usar una clase aparte, donde escribir todos los métodos y llamarlos desde cualquier sitio.

//Creo Atributo Preferences
private Preferences prefes;
//También creo una instancia de game, por si necesito acceder a alguna variable general del juego.
LibgdxSpain game;



//Metodo Constructor
    public Prefer(LibgdxSpain game){
    this.game = game;
    }
   
//Aqui instancio mi atributo preferences, basta con acceder a Gdx.app.getPreferences("nombre")
//El nombre puede ser cualquiera, aunque es recomendable usar el mismo nombre de tu aplicacion.

      public void GetPreferences(){
          prefes = Gdx.app.getPreferences("libgdxspain");       
    }

//Aqui se añaden los métodos de Guardar o Traerse datos.
    }
 
Ésta clase la instanciaremos en nuestra clase principal, y únicamente llamaremos a métodos de la clase Prefer para añadir y traernos los datos, así mantenemos la gestión de las preferencias únicamente en la clase de preferencias.

Guardando datos en las preferencias.

 Bien, ya teniendo el objeto Preferences creado podemos llamar a varios métodos para guardar los datos.

        prefes.putFloat("Musica",0.6f);
        prefes.putBoolean("Inicial", true);
        prefes.putInteger("Niveles",2);

        prefes.putString("Nombre","Firedark");
        prefes.putLong("Tiempo",100000000);
        prefes.flush();


public void setName(){
String name = prefes.setString("Nombre","Paco");
prefes.flush();
}
 

Como vereís, podemos guardar Floats, Booleanos, integers, cadenas de texto, valores long, tenemos que darle la "clave" que representaria el "nombre de variable" dentro de las preferencias, luego en el 2º parámetro añadimos el valor del mismo tipo que método hayamos llamado.
Como vereís existe otro método, el flush(), éste es usado para guardar las preferencias en el archivo externo correspondiente, digamos que es el método que guarda las preferencias.


Leyendo datos de las preferencias.

Para leer datos usaremos el mismo objeto, y es muy similar a guardarlas.

float musica = prefes.getFloat("Musica");
boolean valorinicial = prefes.getBoolean("Inicial");
integer nivel = prefes.getInteger("Niveles");

Ejemplo de Metodos:

public String getName(){
String name = prefes.getString("Nombre");
return name;
}

public boolean getInicial(){
Boolean bool = prefes.getBoolean("Inicial");
return bool;
}

Indudablemente es igual para cada tipo de dato, para traernoslo a una variable, únicamente debemos llamar al método de prefes que trae ese tipo de dato, junto con la "clave" que hemos usado para guardarla.

Problemas y Consejos

Hay algunos problemas a tener en cuenta usando preferencias:

1º ) Si en pleno juego, guardaís una preferencia, el juego posiblemente se atontará un segundo o menos, al estar guardando un archivo externo y ejecutando un render no le sentará muy bien.

2º ) El proyecto Desktop se enterará de todos los cambios que hayas hecho al modificar las preferencias sin guardarlas el proyecto android no, es decir:

Desktop

prefes.setBoolean("Boleano", false);
boolean A = True;

//Actualmente para Desktop la preferencia Boleano es false y la variable A es true.

prefes.setBoolean("Boleano" , A);

//Sin usar flush(), desktop ya sabe que la preferencia Boleano es A, por tanto es true.

prefes.flush(), desktop guarda en su archivo de preferencias el valor de A en la clave Boleano.


Android

prefes.setBoolean("Boleano", false);
boolean A = True;

//Actualmente para Android la preferencia Boleano es false y la variable A es true.

prefes.setBoolean("Boleano" , A);

//Sin usar flush(), Android sigue leyendo que la preferencia "Boleano" es false, no le importa que la hayas seteado.

prefes.flush(), A partir de aqui Android ya leerá la variable A en la clave "Boleano"

 
Ésto es importante ya que no puedes guardar los datos en medio del juego, pero tampoco puedes usar las lecturas de preferencia sin guardarlas previamente, por lo tanto necesitarás guardarlas en memoria temporal en atributos y guardarlas cuando el juego acabe, en el método Hide() de una pantalla, cuando hagas GameOver, etc, así el usuario no notará la lentitud de guardar el archivo.

Ésta semana a sido corta pero para la próxima nos meteremos de lleno en Scene2D y necesitaremos estar descansados porque se complicará un poco más la estructura, como siempre se agradecen comentarios, algún video de algún juego que esteís implementando, +1 de Google, etc... 

Muchas gracias a todos y nos vemos la próxima semana.







domingo, 6 de octubre de 2013

Screens y Spawners

Hola de nuevo! Éste artículo lo voy a dedicar a "partir" nuestra classe principal del juego en varias pantallas, para poder repartir el menu principal, la pantalla de puntuaciones, etc. Vamos a ello.  

Screens y Game 

Como su nombre indica, las pantallas nos permiten dividir el juego en diferentes "menus" o partes lógicas y simplificar nuestra lógica. Antes de crear nuestras primeras pantallas, hablaremos de otra classe que nos permitirá navegar por las mismas, ésta clase se llama "Game".

Como hemos podido observar en nuestra classe principal actual, implementamos una
"ApplicationListener" ésta interfaz nos genera los métodos que estamos usando, create, resize, render, etc... pero no nos permite navegar por pantallas, así que en vez de implementar la interfaz directamente, heredaremos de la classe "Game" la cual ya implementa la interfaz "ApplicationListener", para heredarla eliminaremos el "implements ApplicationListener" y añadiremos "extends Game", importando la classe "com.badlogic.gdx.Game" a continuación.

Al realizarlo vereís que no provoca problema alguno, ya que como he escrito antes, Game ya implementa ésta interfaz, no obstante, si escribimos en cualquier lado de algunos de nuestros métodos "this." observaremos que disponemos de varios métodos más entre ellos getScreen(Screen) y setScreen(Screen), efectivamente éstos métodos son los que usaremos para navegar entre pantallas, pero antes debemos crear alguna pantalla ¿no?.


Creando pantallas.

Para crear nuestra primera pantalla:

- Crearemos una clase nueva dentro de nuestro paquete, junto a nuestra classe principal.
- Implementaremos la interfaz "Screen" a nuestra clase, en mi caso :

public class PlayScreen implements Screen{


- Importaremos la classe "import com.badlogic.gdx.Screen;" y añadiremos los métodos de la interfa.

Todo ésto lo podemos hacer rapidamente con el solucionador de problemas de eclipse, en unos segundos tendremos creada nuestra primera pantalla.

package com.Firedark.libgdxspain;

import com.badlogic.gdx.Screen;

public class PlayScreen implements Screen{

    @Override
    public void render(float delta) {
        // TODO Auto-generated method stub
       
    }

    @Override
    public void resize(int width, int height) {
        // TODO Auto-generated method stub
       
    }

    @Override
    public void show() {
        // TODO Auto-generated method stub
       
    }

    @Override
    public void hide() {
        // TODO Auto-generated method stub
       
    }

    @Override
    public void pause() {
        // TODO Auto-generated method stub
       
    }

    @Override
    public void resume() {
        // TODO Auto-generated method stub
       
    }

    @Override
    public void dispose() {
        // TODO Auto-generated method stub
       
    }

}



Como podeís observar en las pantallas tenemos un par de métodos más que los que ya nos resultan familiar.


Método Show() , se ejecuta al "mostrar" la pantalla, sustituye un poco al método Create() de la classe principal.


Método Hide() , se ejecuta al "ocultar" la pantalla.


Bien, una vez creada nuestra pantalla en blanco, vamos a traernos nuestra lógica a ésta pantalla y dejar "vacia" la classe principal, pero tenemos un pequeño problema que resolver, aquí tenemos que distinguir los atributos "Generales del juego" a los atributos "por pantalla", que quiero decir con ésto, lo más normal es que al crear yo el juego le asigne una resolución 800x480 y sea la misma en cada pantalla, el assets Manager, el cual uso para cargar los assets en memoria de manera rápida y desde cualquier lado ¿debería instanciarlo también en cada pantalla? lógicamente no, debemos crear el constructor de nuestra pantalla, añadiendole por parámetro nuestra classe "Game" para así acceder desde cualquier lugar a nuestros assets y nuestros datos generales. Para ello añadiremos a las pantallas nuestro constructor:


public "ClassePantalla"("Classe de Tu Juego" game){
// Y desde aqui podemos acceder a los assets desde la variable local game
// game.assets."musica"
// game.AltodePantalla, etc.
}


Para acceder los atributos de la classe game desde cualquier pantalla aseguraos de que los atributos sean publicos y no privados, en nuestro caso ( Y si habeís seguido mis otros tutoriales ) tendremos que cambiar a public el atributo de assets.

Por último y antes de añadir todo el código de ejemplo, crearemos en la classe principal el atributo de nuestra pantalla y los instanciaremos pasandole por parámetro como hemos dicho nuestra classe, luego "entraremos" en nuestra pantalla ya creada.

package com.Firedark.libgdxspain;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.math.Rectangle;

public class PlayScreen implements Screen{


    private OrthographicCamera camera;
    private SpriteBatch batch;
    private ShapeRenderer shrend;
    private Rectangle recP,recG;
    private boolean debug;
    // Como veis e creado un atributo de nuestra classe principal para toda la pantalla, nos sirve
    // Como variable global dentro de nuestra classe pantalla.
    private LibgdxSpain game;
    //Constructor Creado, por parámetro requiero de la instancia de mi clase principal.
    public PlayScreen(LibgdxSpain game){
        //Ésto (para los mas novatos en java) nos sirve para al crear nuestra classe asignar la variable
        //local del constructor a nuestra variable global dentro de la classe, para poder usarla
        //En todos los métodos.
        this.game = game;
    }
   
    @Override
    public void render(float delta) {
       
        Gdx.gl.glClearColor(1, 1, 1, 1);
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

        batch.setProjectionMatrix(camera.combined);

      
        if(Gdx.input.isTouched()){
       
            recP.x = Gdx.input.getX() - (recP.height/2);
            //Aqui accedo a la variable h de nuestro objeto principal.
            recP.y = game.h - Gdx.input.getY() - (recP.width/2);
           
        }
       
        if(recP.x < 0){
            recP.x = 0;
        }
       
        //Acordaros de direccionar bien todas las variables que dejeis en la classe principal.
        if(recP.x + recP.width > game.w){
            recP.x = game.w - recP.width;
        }
       
       
        if(recP.y < 0){
            recP.y = 0;
        }
       
        if(recP.y + recP.height > game.h){
            recP.y = game.h - recP.height;
        }
       
       
       
        recG.x = recG.x - 0.5f;
       
        if(recG.x < -60){
            recG.x = 800f;
        }
       
       
        if(recP.overlaps(recG)){
            recP.x = 400;
            recP.y = 400;
        }
       
       
        batch.begin();
        //Aqui hago más de lo mismo, busco el assets alojado en game y lo uso cuando quiero.
        batch.draw(game.assets.background,0,0);
        batch.draw(game.assets.pez,recP.x,recP.y);
        batch.draw(game.assets.gamba,recG.x,recG.y);
        batch.end();
       
        if(debug){
           
        shrend.begin(ShapeType.Rectangle);
        shrend.setColor(Color.BLUE);
        shrend.rect(recP.x, recP.y, recP.width, recP.height);
        shrend.end();
         
        shrend.begin(ShapeType.Rectangle);
        shrend.setColor(Color.RED);
        shrend.rect(recG.x, recG.y, recG.width, recG.height);
        shrend.end();
        }
       
       
    }

    @Override
    public void resize(int width, int height) {
   
       
    }

    @Override
    public void show() {
        shrend = new ShapeRenderer();
       
        recP= new Rectangle();
        recP.height = 64;
        recP.width = 64;
        recP.x = 400;
        recP.y = 400;
       
        recG = new Rectangle(800,0,60,60);
       
       
        debug = true;
   
   
        camera = new OrthographicCamera();
        camera.setToOrtho(false,game.w,game.h);

        batch = new SpriteBatch();   
        //Más de lo mismo.
        game.assets.musica.setLooping(true);
        game.assets.musica.play();
       
    }

    @Override
    public void hide() {
       
       
    }

    @Override
    public void pause() {
       
       
    }

    @Override
    public void resume() {

       
    }

    @Override
    public void dispose() {
        batch.dispose();
        shrend.dispose();
       
       
    }

}


Y nuestra nueva classe principal, e eliminado varios métodos que no uso.

package com.Firedark.libgdxspain;

import com.badlogic.gdx.Game;


public class LibgdxSpain extends Game {
   
   //Seguimos Creando aqui el assets Manager fijaros que ahora es Public
    public AssetsManager assets;
    public float h,w;
    //Nuevo atributo de pantalla.
    public PlayScreen pJuego;

    @Override
    public void create() {   
        //Asignamos resolucion, instanciamos y cargamos imagenes
        h = 480;
        w = 800;
        assets = new AssetsManager();
        assets.cargarAssets();
   
        //Instanciamos pantalla y la seteamos a game, ésto nos "llevará" a la pantalla.
        pJuego = new PlayScreen(this);
        this.setScreen(pJuego);
   

    }
   
    @Override
    public void dispose(){
        //Aqui eliminamos imagenes de memoria
        assets.disposeAssets();
        //Las pantallas también se liberan.
        pJuego.dispose();
    }
}




Spawners

Vamos a crear un spawn de objetos, un spawner, el cual nos servirá para añadir objetos "Rectángulos" a nuestro juego de manera sencilla, pudiendo tener por ejemplo 20 enemigos en la misma pantalla y no teniendo que crear los 20 rectángulos y las 20 colisiones, lógica de cada rectángulo por separado.

Para crear un spawner necesitaremos un array de rectángulos, donde almacenaremos nuestros mobs, disparos, etc...

private Array<Rectangle> mobsMalotes;

A continuación crearemos un método en nuestra classe o en alguna classe aparte.

public void spawnMobMalote(){
 //Dentro de éste metodo deberiamos colocar donde se va a spawnear el mob, en caso de un disparo
// del personaje por ejemplo, le podemos pasar por parámetro los valores de ubicacion del personaje
// así al generar el rectángulo lo generamos justo donde está el personaje.

// Aparte podemos darle valores aleatorios a las coordenadas con la utilidad matemática de libgdx
// int x = MathUtils.random(0,100); generará un int entre 0 y 100.

Rectangle rec = new Rectangle(x,y,height,width);
mobsMalotes.add(rec);
}


Una vez cumplimentado el método debemos dibujarlo en pantalla, ya sean disparos, mobs, rocas.
Para ello iremos entre el batch.begin y el batch.end, aqui hay varias maneras de recorrer el array
explicaré por lo menos 2.

Bucle for each.
El Bucle for each nos recorrerá los elementos del array, así que para dibujar los rectángulos con su textura ya haremos ésto:

for(Rectangle rec : mobsMalotes){
            batch.draw(game.assets.textura,rec.x,rec.y,rec.height,rec.width);
           //Podemos añadir algún movimiento lineal rapido a los réctangulos, como en la caso de la
           // gamba del artículo anterior, si añadimos x ejemplo
           rec.y--;
          //Provocaremos que los rectángulos que aparezcan vayan hacia abajo.
          // También podemos añadir colisiones, para actuar en caso de que un rectángulo toque otro.
            if(rec.overlaps(otroRectangulo)){
                //Esto sirve para eliminar el rectángulo que a chocado como ejemplo.
                mobsMalotes.removeValue(rec, true);
               //Un sonido, etc
                game.assets.hit.play();
            }
        }
 
Iterator

Si creamos un Iterator también nos hará la misma función. Es una utilidad del java, importarla de Java.Utils.

//El mismo caso que en el for each.
Iterator<Rectangulo> iter = mobsMalotes.iterator();
            while(iter.hasNext()){
                Rectangulo rec = iter.next();
                batch.draw(game.assets.textura,rec.x,rec.y,rec.height,rec.width);              
                if(rec.getBounds().overlaps(otroRectangulo)){      
                        iter.remove();               
                }

Con ésto ya tenemos dibujo y lógica de cualquier cosa que spawneemos, me falta explicar como hacer un temporizador cíclico para que podaís probar a spawnear cosas cada segundo, controlar los disparos, o simplemente contar tiempo.

TimeUtils. Manejando el tiempo.

TimeUtils.millis()  Devuelve el tiempo de ejecución en milisegundos.
TimeUtils.nanos() Devuelve el tiempo de ejecución en nanosegundos.


Contador de segundos:

Creo un long como atributo y en el método create o show le asigno el tiempo actual de ejecución.

private long time;

public void create(){
time = TimeUtils.millis();
}


Luego en el método render, añado éste código:

public void render(){
 //Si el tiempo en ejecucion, que no a parado se le resta el tiempo que hemos almacenado nos dará el //tiempo en millis que ha pasado, en caso de ser mayor que 1000 es que habrá llegado al segundo
//Al entrar en el condicional, volvemos  a asignar el tiempo actual de ejecucion a la variable time
//provocando lo mismo una y otra vez.
 if(TimeUtils.millis() - time > 1000){
//Esta parte se ejecutará cada segundo.
time= TimeUtils.millis();
}
}

 Finalmente añado mi clase para que veaís un ejemplo hecho, se spawnean cada 2 segundos. rocas en la parte superior y van hacia abajo, si chocan con el pez se eliminan y suena un sonido.

 //Parte de la pantalla creada en el tutorial anterior.
private Rectangle recP,recG;
    private boolean debug,move;
    //Array
    private Array<Rectangle> rocas;
    //TIEMPO
    private long time;
    private LibgdxSpain game;
    public PlayScreen(LibgdxSpain game){
    this.game = game;

    }
   
    @Override
    public void render(float delta) {

        //TEMPORIZADOR
        if(TimeUtils.millis() - time > 2000){
            spawnRoca();
            time = TimeUtils.millis();
        }
       
        Gdx.gl.glClearColor(1, 1, 1, 1);
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

        batch.setProjectionMatrix(camera.combined);

        if(Gdx.input.isTouched()){
           
            float sY = game.h -Gdx.input.getY();
            if(Gdx.input.getX() > recP.x & Gdx.input.getX() < recP.x + recP.width & sY > recP.y & sY < recP.y + recP.height){                       
                move = true;
            }
       
        }
   
        if(!Gdx.input.isTouched()){
            move = false;
        }
       
        if(move){
        recP.x = Gdx.input.getX() - (recP.height/2);   
        recP.y = game.h - Gdx.input.getY() - (recP.width/2);       
        }
       
        if(recP.x < 0){
            recP.x = 0;
        }
       
        if(recP.x + recP.width > game.w){
            recP.x = game.w - recP.width;
        }
       
        if(recP.y < 0){
            recP.y = 0;
        }
       
        if(recP.y + recP.height > game.h){
            recP.y = game.h - recP.height;
        }
           
        recG.x = recG.x - 0.5f;
       
        if(recG.x < -60){
            recG.x = 800f;
        }
       
       
        if(recP.overlaps(recG)){
            recP.x = 400;
            recP.y = 400;
            move = false;
        }
       
       
        batch.begin();
        batch.draw(game.assets.background,0,0);
        batch.draw(game.assets.pez,recP.x,recP.y);
        batch.draw(game.assets.gamba,recG.x,recG.y);
       
        //Iteracion
        Iterator<Rectangle> iter = rocas.iterator();
        while(iter.hasNext()){
            Rectangle rec = iter.next();
            batch.draw(game.assets.roca,rec.x,rec.y,rec.height,rec.width);  
            rec.y--;
            if(rec.overlaps(recP)){    
                    iter.remove();    
                    game.assets.hit.play();
            }
        }
       
       
        batch.end();
       
        if(debug){
           
        shrend.begin(ShapeType.Rectangle);
        shrend.setColor(Color.BLUE);
        shrend.rect(recP.x, recP.y, recP.width, recP.height);
       
        shrend.setColor(Color.RED);
        shrend.rect(recG.x, recG.y, recG.width, recG.height);
        shrend.end();
        }
       
       
    }

    @Override
    public void resize(int width, int height) {
   
       
    }

    @Override
    public void show() {
        game.assets.cargarAssets();
        time = TimeUtils.millis();
        shrend = new ShapeRenderer();
        rocas = new Array<Rectangle>();
        recP= new Rectangle();
        recP.height = 64;
        recP.width = 64;
        recP.x = 400;
        recP.y = 400;
       
        recG = new Rectangle(800,0,60,60);
       
       
        debug = true;
   
   
        camera = new OrthographicCamera();
        camera.setToOrtho(false,game.w,game.h);

        batch = new SpriteBatch();   
        //Más de lo mismo.
        game.assets.musica.setLooping(true);
        game.assets.musica.play();
       
    }

    public void spawnRoca(){
        Rectangle rec = new Rectangle();
        rec.height = 128;
        rec.width = 128;
        float randomX = MathUtils.random(0 ,(game.w - rec.width));
        rec.x = randomX;
        rec.y = game.h + rec.height;
        rocas.add(rec);
        }

Y eso es todo por ésta semana , la siguiente semana explicaré el uso de Preferencias, que son los datos permanentes que se guardan con la aplicación, para almacenar records, volumen, etc.

Gracias a tod@s y cualquier duda preguntar en los comentarios! Suerte ;)

martes, 1 de octubre de 2013

Rectángulos, entradas y colisiones.

Formas Geometricas, Entradas y Colisiones.

Saludos de nuevo, vamos a empezar con la parte interesante de la creación de juegos, la lógica del juego, para ello vamos a crear 2 modos para que al explicar resulte más sencillo.

Vamos a crear un modo Debug en el que dibujaremos en pantalla las formas geometricas, las moveremos, como si de un personaje, mob, pared, o cualquier objeto se tratara, y el modo normal, donde pintaremos las texturas con sus imágenes originales y veremos el juego tal y como debería ser.


ShapeRenderer


El ShapeRenderer es un objeto que se encarga de dibujar formas geometricas, es capaz de dibujar circulos, conos, lineas, puntos, rectangulos,triangulos, etc. Aunque nosotros para juegos 2D nos centraremos en su uso para circulos y rectangulos. 

Para "dibujar" una forma geométrica, deberemos crear el objeto como atributo en nuestra classe y instanciarlo en el método create, no tiene ningún tipo de parámetro y su constructor es únicamente 

new ShapeRenderer();

Para dibujar una forma debemos ir al metodo render y escribir algo parecido a ésto.

//En Forma podeéis escojer varias, rectangulo, circulo, rectangulo relleno (FilledRectangle), punto, linea,etc etc.

        shaperenderer.begin(ShapeType."Forma");
        shaperenderer.setColor(1,1,1,0); // Para el color podeis usar una constante Color.BLACK,etc o
//  o diferentes métodos para instanciar color, como el RGBA que estoy usando, 
// setColor.(Red,Green,Blue,Alpha);
        shaperenderer."Metodo de la forma"; // Hay varios métodos depende de la forma del método //begin, si ponemos por ejemplo ShapeType.Circle, deberemos llamar al método shaperenderer.circle, //y darle los parámetros que necesite, en caso del circulo, coordenadas X e Y y el radio del mismo.
        shaperenderer.end();


Aqui teneís la documentación de ShapeRenderer y la documentación para los colores.

http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/glutils/ShapeRenderer.html

http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/Color.html

Rectangulos

Toda lógica en un juego o casi toda está formada por rectángulos, si creamos un juego de plataformas, tu personaje será un rectangulo y si dispara, disparará rectángulos, cuando éstos  colisionen contra otro "Mob", "Bicho",  enemigo o rectangulo pasará algo, ganarás puntos, perderás una vida o simplemente se acabará el juego, eso depende del programa. Lo mismo puede suceder en un RPG, un juego de arcade, un juego Board etc...

Se puede crear un rectángulo fácilmente desde libgdx, bastará con crearlo como atributo, y instanciarlo, tiene varios contructores:

new Rectangle(); // Si creamos un rectángulo sin parámetros debemos darselos a continuación, ya que //se crea con los parámetros a 0. (rect.x, rect.y, rect.width y rect.height)
new Rectangle(float x, float y, float width, float height); // Esto ya crea el rectángulo parametrizado.
new Rectangle(Rectangle rect); // Crea un rectángulo con otro rectángulo de referencia.

 Un ejemplo rápido de rectángulo:

        rec= new Rectangle();
        rec.height = 64;
        rec.width = 64;
        rec.x = 400;
        rec.y = 400;


http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/Rectangle.html


Entradas: táctil, accelerómetro y teclado.

No voy a explicar todo el paquete "Gdx.input" sólo deciros que es el encargado de gestionar los datos de entrada, no obstante comentaré 3 métodos básicos para empezar a mover cosas.

Gdx.input.isTouched()   - Devuelve True si se ésta pulsando la pantalla.
Gdx.input.getX() y Gdx.input.getY() , nos devuelve la coordenada X e Y de donde pulsemos.

Gdx.input.getAccelerometerX()
Gdx.input.getAccelerometerY()
Gdx.input.getAccelerometerZ()

Éstos tres métodos devuelven de -10 a 10 los valores de los ejes del accelerometro.

 Gdx.input.isKeyPressed(int key);    - Devuelve True si se pulsa la tecla del número especificado en el paramétro, no hace falta saber los números, si escribís la constante de Input.Keys sólo teneís que elegir la letra o teclas que quereís, os dejó el link a la documentación y varios ejemplos.

http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/Input.Keys.html

 Gdx.input.isKeyPressed(Input.Keys.H); //Letra H del teclado.
 Gdx.input.isKeyPressed(Input.Keys.ENTER); //Letra Enter del teclado.

 Colisiones

 A lo sencillo, las colisiones se dan cuando un rectángulo se toca con otro, simplemente deberemos usar el método del objeto Rectangle llamado , rectangulo1.overlaps(rectangulo2) , nos devolverá true en caso de que colisionen los dos rectángulos.


Tutorial Práctico: El pez y la Gamba.


Éste mini tutorial enseña a crear 2 rectángulos, una gamba y un pez, el pez será manejado con el táctil y la gamba ira del borde derecho de la pantalla al izquierdo continuamente, si el pez choca con la gamba, el pez volverá a su posición original.

Paso 1: Descargar Assets ( podeís usar lo que queraís, el pez y la gamba o simples rectángulos sin cargarle imagenes, o cualquier textura que queraís.)

Paso 2. Añadir a la classe el ShapeRenderer, 2 rectángulos ( yo los he llamado recP, y recG, rectángulo Pez y rectángulo Gamba respectivamente).Añadir un valor booleano para activar él modo debug, para alternar si queremos o no ver los rectángulos dibujados.

Paso 3. Instanciar y crear los 2 rectángulos.

Paso 4. Utilizar el Gdx.input para igualar las coordenadas del táctil a las coordenadas del rectángulo del pez arreglando el problema de que no se mueva por las esquinas, sino que el pulso del dedo lleve el centro del pez a el centro del dedo (mas o menos).

Paso 5 (opcional) Evitar que el pez desaparezca por los bordes de la pantalla.

Paso 6. Mover la gamba (Rectangulo) de un lado al otro de la pantalla.

Paso 7.  Dibujar las 2 texturas en la ubicación de los dos rectángulos que toque.

Paso 8, Crear la colisión y en caso de que toquen, llevar al pez a su posición inicial.


Aqui os dejo las dos classes como me quedan:


package com.Firedark.libgdxspain;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.math.Rectangle;

public class LibgdxSpain implements ApplicationListener {
   
    private OrthographicCamera camera;
    private SpriteBatch batch;
    private AssetsManager assets;
    //Crear atributos Paso 2
    private ShapeRenderer shrend;
    private Rectangle recP,recG;
    private boolean debug;
   
    private float h,w;

    @Override
    public void create() {   
        assets = new AssetsManager();
        assets.cargarAssets();
        //Instanciar ShapeRenderer Paso 3
        shrend = new ShapeRenderer();
        //Instanciar Rectangulo Pez Paso 3
        recP= new Rectangle();
        recP.height = 64;
        recP.width = 64;
        recP.x = 400;
        recP.y = 400;
        //Instanciar Rectangulo Gamba Paso 3, fijaos que la envio fuera de la pantalla al empezar.
        recG = new Rectangle(800,0,60,60);
       
        //Podemos alternar entre true y false para ver o no los rectangulos.
        debug = true;
   
        w = 800;
        h = 480;
        camera = new OrthographicCamera();
        camera.setToOrtho(false,w,h);

        batch = new SpriteBatch();   
        assets.musica.setLooping(true);
        assets.musica.play();
    }

    @Override
    public void dispose() {
   
        batch.dispose();
        shrend.dispose();
        assets.disposeAssets();
       
    }

    @Override
    public void render() {       

        Gdx.gl.glClearColor(1, 1, 1, 1);
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

        batch.setProjectionMatrix(camera.combined);

        //Paso 4
        //Si tocamos la pantalla.
        if(Gdx.input.isTouched()){
            //las coordenadas del pez son iguales a la coordenada del "dedo" menos la mitad de lo que mida el rectangulo del pez.
            //asi lo centramos. Lo hacemos en X e Y.
            recP.x = Gdx.input.getX() - (recP.height/2);
            recP.y = h - Gdx.input.getY() - (recP.width/2);
           
        }
        //Paso 5
        // Si el Pez toca el borde izquierdo, se queda en el borde izquierdo.
        if(recP.x < 0){
            recP.x = 0;
        }
       
        //Si el pez toca el borde derecho , "w" es lo largo de la pantalla,
        //Fijaos que añado a la X del rectangulo lo que mide el rectangulo
        //Para no meter todo el pez en el borde, pensar que la coordenada X del rectangulo
        //es la esquina inferior.
        if(recP.x + recP.width > w){
            recP.x = w - recP.width;
        }
       
        //Lo mismo en el eje Y.
        if(recP.y < 0){
            recP.y = 0;
        }
       
        if(recP.y + recP.height > h){
            recP.y = h - recP.height;
        }
       
       
        //Paso 6
        //Movimiento de la Gamba, el bucle render es ciclico, así que aprobecharemos para ir
        //restandole -0.5 a su coordenada x contantemente.
        recG.x = recG.x - 0.5f;
        //Si llega al -60 de la pantalla, es decir se esconde en ella, vuelve a empezar.
        if(recG.x < -60){
            recG.x = 800f;
        }
       
        //Paso 8
        // Si Pez choca contra Gamba, el Pez vuelve a 400 x 400, aqui podriamos hacer lo que quisieramos
        //GameOver, Perder vidas, etc etc
        if(recP.overlaps(recG)){
            recP.x = 400;
            recP.y = 400;
        }
       
       
        batch.begin();
        batch.draw(assets.background,0,0);
        //Paso 7
        //Después del Background, dibujamos Pez y Gamba en las posiciones de los 2 rectangulos
        //esto ya hará que pez y gamba sigan a los rectangulos pertinentes, y se moverán.
        batch.draw(assets.pez,recP.x,recP.y);
        batch.draw(assets.gamba,recG.x,recG.y);
        batch.end();
        //Si activamos el Debug, dibujaremos los 2 rectangulos, pez azul, y gamba rojo.
        if(debug){
            //Azul
        shrend.begin(ShapeType.Line);
        shrend.setColor(Color.BLUE);
        shrend.rect(recP.x, recP.y, recP.width, recP.height);
        shrend.end();
            //Rojo
        shrend.begin(ShapeType.Line);
        shrend.setColor(Color.RED);
        shrend.rect(recG.x, recG.y, recG.width, recG.height);
        shrend.end();
        }
    }

    @Override
    public void resize(int width, int height) {
    }

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }
}



También he tocado la classe AssetsManager para añadir las 2 nuevas Imágenes:

package com.Firedark.libgdxspain;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureRegion;


public class AssetsManager {
   
    public Texture Tbackground,pez,gamba;
    public TextureRegion background;
    public Music musica;
   
    public void cargarAssets(){
        Tbackground = new Texture(Gdx.files.internal("data/Images/oceanbackground.jpg"));
        pez = new Texture(Gdx.files.internal("data/Images/pez.png"));
        gamba = new Texture(Gdx.files.internal("data/Images/gamba.png"));
        background = new TextureRegion(Tbackground,0,0,800,480);
        musica = Gdx.audio.newMusic(Gdx.files.internal("data/Music/musica.wav"));      
    }
   
    public void disposeAssets(){
        Tbackground.dispose();
        musica.dispose();
        pez.dispose();
        gamba.dispose();
    }
   



Bueno, ésto ha sido todo ésta semana, espero que haya sido productivo, se agradecen comentarios, dudas, +1 de Google y todo eso. Espero que empezeís a pensar en vuestra lógica del juego y nos vemos la próxima semana con el tema de las pantallas de juego y algun detallito más. ¡Gracias!