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!