lunes, 9 de diciembre de 2013

Assets Manager II



Saludos de nuevo, sé que últimamente no publico entradas todas las semanas, pero tengo mucha faena acumulada y he tenido que reducir el ritmo, así que en cuanto pueda intentaré escribir alguna entrada interesante como la de a continuación.

En la entrada 3 del blog expliqué como crear un "assets manager" para gestionar nuestras imágenes, sonidos, músicas etc... No obstante, y como era normal en la 3era entrada, (1era entrada de código) no iva a ponerme a explicar algo muy difícil para empezar, así que ahora, después de unas cuantas entradas ya estamos preparados para aprender a gestionar nuestros assets de mejor manera, controlando la carga de los mismos, etc. Vamos a ello!

Classe de libGdx AssetManager.

http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/assets/AssetManager.html

 LibGdx tiene una classe ya preparada para gestionar la carga de nuestros assets, para ello vamos a crear una classe aparte, en nuestro proyecto núcleo y voy a llamarla Manager para dentro de ella, instanciarle un objeto AssetManager que lo importaremos de "com.badlogic.gdx.assets.AssetManager"

import com.badlogic.gdx.assets.AssetManager;
//Creamos nuestra classe
public class Manager  {
//Creamos nuestro atributo AssetManager.
    public AssetManager manager;
   
//Creamos un método constructor y instanciamos el objeto con AssetManager();
    public Manager(){
        manager = new AssetManager();
       }

}

Una vez hecho esto vamos a ver como cargar nuestros assets, creamos un método void sencillo y le añadimos el método del objeto AssetManager load(PathdelArchivo,TipoDeClasse);

    public void loadAssets() {
       //Cargo una textura.
        manager.load("data/Images/pez.png",Texture.class);     
        manager.load("data/Images/calamar.png",Texture.class);
       //Cargo un sonido
        manager.load("data/sounds/boom.mp3",Sound.class);
        manager.load("data/Images/moneda.png",Texture.class);
       //Cargo una musica
        manager.load("data/sounds/musicainicial.mp3",Music.class);
    }
   
Una vez creado el método de carga tendremos que llamarlo cuando nos parezca oportuno. En nuestro caso, en la 2ª parte de ésta entrada, vamos a crear una pantalla de carga tipo "Loading" con una barra y demás.

Antes de liarnos con el tutorial práctico, explico como traernos donde necesitamos todos nuestros assets.

Para traernos y usar cualquier asset en el AssetManager, antes de nada tenemos que instanciar nuestra classe "Manager" propia y tenerla accesible en la classe donde vamos a usarla, así que para llevarla de un lado para otro la instanciaré en mi classe principal "Game" de siempre y al pasarme el game a todas nuestras classes siempre podré usar mis assets.

Aqui va un fragmento de mi classe principal:

public class LibgdxSpain extends Game {
//Aqui creo mis atributos, pantallas, preferencias, etc.
//Y como no creo mi atributo Manager
public Manager assets;
..
..
..
//Luego en mi método create, añado el constructor de mi classe Manager.

assets = new Manager();

// Luego seguiría instanciando mis demás classes, cargando pantallas, etc.

//Fijaos que a mi objeto Manager le llamo "assets" y a mi objeto AssetManager de libgdx que //instancio dentro de Manager le llamo manager en minusculas, para acceder a él deberé escribir
//game.assets.manager
..
..
..

Y ahora, empiezo a programar mi mágnifico Mob "Calamar" nuevo y necesito traer mi textura, así que para ello como siempre meto mi classe game en un atributo del actor para acceder desde cualquier método dentro de mi actor y escribo ésto.

public void draw(SpriteBatch batch){
batch.draw((Texture) game.assets.manager.get("data/Images/calamar.png");
}
//De ésta manera me traigo ese asset, y como el método get dentro de AssetManager tiene varios //valores de retorno(salida) posibles, debo indicar a java que lo que me traigo con get, es una textura.
//Para ello escribo (Texture) game.assets.manager.get(XXXXX);
//Puedo hacer perfectamente lo mismo con cualquier asset, siempre y cuando le diga  java de que
//tipo de objeto estamos hablando. Ejemplos:
//(Sound) game.assets.manager.get("data/sounds/boom.mp3");
//(Music) game.assets.manager.get("data/sounds/MusicaInicial.mp3");
Y finalmente para terminar la teorica comento los 3 métodos últimos que hay que conocer de AssetManager, el método .getProgress() que nos devolverá un float del 0 al 1 con el porcentaje de carga, (0 = 0% y 1 = 100%),  el método .dispose() que como ya sabemos quitará de memoria todas los assets cargados, y finalmente el método .update() que lo utilizaremos para "actualizar" los datos internos del AssetManager, útil para actualizar la barra de progresso, etc.



Tutorial Práctico, Barra de Carga.

Una vez creado nuestro AssetManager vamos a crear nuestro actor que nos actuará como una barra de carga, para ello e creado éstas 2 imágenes rapidito.




Una como contenedor y la otra será la barra que estiraremos dependiendo del progreso de la misma. Aqui va el código, como vereís es aplicar muy fácil lo que hemos aprendido hoy.



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.scenes.scene2d.Actor;
//Creamos el actor.
public class BarraDeCarga extends Actor {
    //No usaré el loader para cargar las imagenes de cargado.
    Texture barra,llenado;
    LibgdxSpain game;
//Me traigo el game
    public BarraDeCarga(LibgdxSpain game){
        this.game = game;
//Lo coloco en el centro, yo lo e puesto en cualquier lado.
        setPosition(game.w/2-200,game.h/2);
//Instancio imagenes a la antigua.
        barra = new Texture(Gdx.files.internal("data/Images/barra.png"));
        llenado = new Texture(Gdx.files.internal("data/Images/llenado.png"));
       
    }
    //Método de dibujo
    public void draw(SpriteBatch batch, float parentAlpha){
       
        //Actualizo en mi método draw el manager.
        game.assets.manager.update();
        //Dibujo la barra, mas o menos centrada en el contenedor, y la "Estiro" en X, como el valor
        //del progress va de 0 a 1, la multiplico por 400 así irá de 0 a 400.
        batch.draw(llenado, getX()+50,getY()+25,game.assets.manager.getProgress()*400,64);
        //Dibujo Contenedor.
        batch.draw(barra, getX(),getY());
       
    }

}
//FIN, le puedo añadir letras, números y todo lo que se me ocurra.
Aquí un video de lo que hace:




Y eso es todo por ahora, espero cuanto antes sacar mi siguiente entrada. Gracias a todos y suerte.

viernes, 15 de noviembre de 2013

Procesador de entradas y de gestos.



Saludos de nuevo! ya hemos aprendido a interaccionar con actores, clicarlos, moverlos, lanzarlos etc pero siempre dentro de un actor, voy a explicar en una pequeña entrada como implementar en nuestro juego un procesador de entradas y un procesador de gestos.






InputProcessor

El "InputProcessor" es una interfaz que tiene varios métodos, de los cuales en la entrada anterior hemos hablado ya en los actores, la interfaz la podemos implementar en cualquier clase, en una Screen, en la logica, y una vez implementada la tenemos que "seleccionar" en el procesador de entradas de libgdx con el método Gdx.input.setInputProcessor(Classe con la interfaz);
Escribo una classe cualquiera creada de nuevo donde implemento la interfaz.

package com.Firedark.libgdxspain;

import com.badlogic.gdx.InputProcessor;

public class MyInputProcessor implements InputProcessor{

    @Override
    public boolean keyDown(int keycode) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean keyUp(int keycode) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean keyTyped(char character) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean touchDown(int screenX, int screenY, int pointer, int button) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean touchUp(int screenX, int screenY, int pointer, int button) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean touchDragged(int screenX, int screenY, int pointer) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean mouseMoved(int screenX, int screenY) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean scrolled(int amount) {
        // TODO Auto-generated method stub
        return false;
    }

}

Una vez creada la clase, se instancia en la clase principal o en la que queraís y la seleccionais con el método Gdx.input.setInputProcessor(claseimplementada); Una vez lo hagaís y toqueís por el ejemplo la pantalla, se llamará al método touchDown de ésta clase, así pasará cuando movaís el raton (solo en desktop) cuando toqueis una tecla del teclado keyDown(), keyUp() etc.

Ésta interfaz la podemos instanciar en cualquier parte, junto a la interfaz Screen, directamente en la clase principal, en un actor de lógica, sólo teneís que acordaros de usar el método Gdx.input.setInputProcessor(claseimplementada);

GestureListener

package com.Firedark.libgdxspain;

import com.badlogic.gdx.input.GestureDetector.GestureListener;
import com.badlogic.gdx.math.Vector2;

public class MyGestureDetector implements GestureListener {

    @Override
    public boolean touchDown(float x, float y, int pointer, int button) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean tap(float x, float y, int count, int button) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean longPress(float x, float y) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean fling(float velocityX, float velocityY, int button) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean pan(float x, float y, float deltaX, float deltaY) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean zoom(float initialDistance, float distance) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2,
            Vector2 pointer1, Vector2 pointer2) {
        // TODO Auto-generated method stub
        return false;
    }

}

Funciona exactamente igual que el InputProcessor, pero ésta interfaz nos detecta los gestos que hagas en la pantalla. Aqui va mi clase con la interfaz. 

Debeís seleccionarla con el método Gdx.input.setInputProcessor(clase), pero no lo podeís hacer directamente, ya que el GestureListener en si no es un InputProcessor, para ello debeís crear una clase GestureDetector y pasarle nuestro GestureListener, se haría así:

Gdx.input.setInputProcessor(new GestureDetector(miobjetogesturelistener));

También la podeís implementar en cualquier clase.

InputMultiplexer

Ahora se nos plantea un problema, si yo selecciono que mi procesador de entradas sea el del Stage de mi scenes2d no puedo tener mi procesador de entradas a la vez, y tampoco podré gestionar gestos y entradas juntas, pero para ello tenemos la clase InputMultiplexer, que nos permitirá tener seleccionados varios procesadores de entradas. Es muy sencilla de usar, escribo un claro ejemplo de su uso y os dejo que investigueís un poco:

        InputMultiplexer multi = new InputMultiplexer();
        multi.addProcessor(game.myinputprocessor);
        multi.addProcessor(new GestureDetector(game.mygesturelistener));
        multi.addProcessor(stage);
        Gdx.input.setInputProcessor(multi);


Con ésto funcionarán los actores, las teclas y los gestos que queramos.

Ésto a sido todo ésta semana y para la semana que viene seguimos. Gracias a tod@s y hasta otra.

jueves, 14 de noviembre de 2013

Actores: Listeners

Saludos a tod@s, ésta semana vamos a dedicarla a los listeneres de los actores, intentaré explicarlo de la manera mas fácil posible para que lo entendamos todos.

 Listeners. 

Un Listener como su traducción indica es un oyente, un "escuchador" y captura "eventos" un evento es una notificación de alguien, en terminos de programación y en nuestro caso, el actor dispara eventos "me han tocado", "me han soltado", "me mueven", etc. Pongo una pequeña imagen de explicación que aunque parezca chorra nos ayudará a aclarar términos.

IMPORTANTE:
 Como vereís en la imagen un actor tiene unos llamados "bounds" límites, los límites de un actor se ajustan con varios de los métodos que hemos usado, setX(), setY(),setWidth(),setHeight(), setBounds(x,y,ancho,alto), etc

Un Actor no puede notificar un evento si no se toca sus límites, podemos crear un actor y colocarlo en cualquier sitio ajustando sus coordenadas con SetPosition(x,y), pero si no le ajustamos su ancho y alto sólo será un punto cualquiera y será imposible clickarlo, independientemente de que dibujemos en su método draw una textura, sprite, etc, vereís la textura, pero no os devolverá nada.

Bien vamos a ver como insertar el Listener en 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.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.actions.MoveByAction;
import com.badlogic.gdx.scenes.scene2d.actions.ParallelAction;
import com.badlogic.gdx.scenes.scene2d.actions.RepeatAction;
import com.badlogic.gdx.scenes.scene2d.actions.RotateByAction;

public class Pez extends Actor {
    LibgdxSpain game;
    TextureRegion RegionPez;
    Texture pez;
    RotateByAction rotate;
    MoveByAction move;
    RepeatAction repeat;
    ParallelAction paralel;
   
    public Pez(LibgdxSpain game){
       
        this.game = game;
   
        setPosition(50,50);
        setHeight(64);
        setWidth(64);
        setOriginX(getWidth()/2);
        setOriginY(getHeight()/2);
   
       
        move = new MoveByAction();
        move.setAmount(700, 300);
        move.setDuration(10f);
        rotate = new RotateByAction();
        rotate.setAmount(400);
        rotate.setDuration(1f);
        //añadir acciones
        repeat = new RepeatAction();
        repeat.setCount(repeat.FOREVER);
        repeat.setAction(rotate);
       
        paralel = new ParallelAction();
        paralel.addAction(move);
        paralel.addAction(repeat);
       
        this.addAction(paralel);

        pez = new Texture(Gdx.files.internal("data/Images/pez.png"));
       
        RegionPez = new TextureRegion(pez);
        //Añadimos un lister, y creamos el listener dentro del método,
        addListener(new InputListener() {
           
           
              public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
                            //Se ejecuta cuando se hace click sobre el actor.
                      return true;
              }
             
              public void touchUp (InputEvent event, float x, float y, int pointer, int button) {
                    //Se ejecuta cuando se levanta el dedo del actor, se ejecuta sólo cuando el touchDown es true.
              }
             
              public void touchDragged (InputEvent event, float x, float y, int pointer) {
                  //Se ejecuta cuando touchdown = true y se arrastra el actor.
            }

       
            public boolean mouseMoved (InputEvent event, float x, float y) {
                //Se ejuta "solo en desktop" ya que es cuando el raton pasa por encima del actor.
                return true;
            }

           
            public void enter (InputEvent event, float x, float y, int pointer, Actor fromActor) {
                //Se ejuta "solo en desktop" ya que es cuando el raton entra al actor.
            }

       
            public void exit (InputEvent event, float x, float y, int pointer, Actor toActor) {
                //Se ejuta "solo en desktop" ya que es cuando el raton sale del actor.
            }
   
           
             
        });
   
    }
   
   
    public void draw(SpriteBatch batch,float parentAlpha){
       

       
        batch.draw(RegionPez, getX(), getY(), getOriginX(), getOriginY(), getWidth(), getHeight(), getScaleX(), getScaleY(), getRotation());
       
       
    }

}


Como veís teneís por parámetro las coordenadas de los puntos donde se pulsa y el "pointer" es para el multitáctil, 0 el primer dedo, 1 el 2º, etc etc.

Hay 2 tipos de listeners dentro de un actor, el puesto en el código anterior y el gestor de gestos:


 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.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.actions.MoveByAction;
import com.badlogic.gdx.scenes.scene2d.actions.ParallelAction;
import com.badlogic.gdx.scenes.scene2d.actions.RepeatAction;
import com.badlogic.gdx.scenes.scene2d.actions.RotateByAction;
import com.badlogic.gdx.scenes.scene2d.utils.ActorGestureListener;

public class Pez extends Actor {
    LibgdxSpain game;
    TextureRegion RegionPez;
    Texture pez;
    RotateByAction rotate;
    MoveByAction move;
    RepeatAction repeat;
    ParallelAction paralel;
  
    public Pez(LibgdxSpain game){
      
        this.game = game;
        setPosition(50,50);
        setHeight(64);
        setWidth(64);
        setOriginX(getWidth()/2);
        setOriginY(getHeight()/2);

        move = new MoveByAction();
        move.setAmount(700, 300);
        move.setDuration(10f);
        rotate = new RotateByAction();
        rotate.setAmount(400);
        rotate.setDuration(1f);
        repeat = new RepeatAction();
        repeat.setCount(repeat.FOREVER);
        repeat.setAction(rotate);
      
        paralel = new ParallelAction();
        paralel.addAction(move);
        paralel.addAction(repeat);
      
        this.addAction(paralel);

        pez = new Texture(Gdx.files.internal("data/Images/pez.png"));
      
        RegionPez = new TextureRegion(pez);
      
        addListener(new ActorGestureListener(){
          
            public void touchDown (InputEvent event, float x, float y, int pointer, int button) {
                //Mismo caso que en InputListener, al pulsar
            }

            public void touchUp (InputEvent event, float x, float y, int pointer, int button) {
                //Mismo caso que en InputListener, al soltar
            }

            public void tap (InputEvent event, float x, float y, int count, int button) {
                //Al tocar y soltar, seria como al hacer click.
            }

            public boolean longPress (Actor actor, float x, float y) {
                //al mantener pulsado un rato sin mover el dedo
          
                return false;
            }

            public void fling (InputEvent event, float velocityX, float velocityY, int button) {
                //seria un gesto como para lanzar un objeto, lo tocamos y lo movemos hacia un lado
                //soltandolo
              
            }

      
            public void pan (InputEvent event, float x, float y, float deltaX, float deltaY) {
               //Es como un arrastrar
            }

            public void zoom (InputEvent event, float initialDistance, float distance) {
                //Como su nombre indica, es como cuando intentamos aumentar el zoom de la pantalla con
                //los dos dedos
            }

            public void pinch (InputEvent event, Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2) {
                //Este es como el Zoom pero mas complicado, con capacidad de rotación, como cuando en
                //el google maps hacemos grande el mapa a la vez que lo giramos.
            }
          
        });
  
    }
  
  
    public void draw(SpriteBatch batch,float parentAlpha){
      

      
        batch.draw(RegionPez, getX(), getY(), getOriginX(), getOriginY(), getWidth(), getHeight(), getScaleX(), getScaleY(), getRotation());
      
      
    }

}


 IMPORTANTE 2.0

Hace 2 tutoriales escribí esto al crear la screen y añadir el stage dentro.

 @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);
    }

Es imposible que si os dejaís esa linea el stage sea capaz de capturar algún gesto,  para que se entienda, el stage tiene incluida la interfaz InputProcessor, la cual se pasa al processador de entradas de libgdx para que pueda "procesarlas" y devolvernos el método que toque.

Ya teneís varias maneras de interactuar con los actores, mañana explicaré como implementar el procesador de entradas y el procesador de gestos sin tener que usar actores. Espero que se haya entendido bien ya que para los que no hayan escuchado el tema de los eventos y listeneres puede ser complicado.

Muchas gracias a todos y hasta mañana.

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 ;)