jueves, 14 de abril de 2016

Tutorial 8: Manejo de Pantallas

Hola buenas.
Es de mi agrado informarle que solo faltan dos tutoriales(incluyendo este), para el gran proyecto del blog. Es correcto haremos un juego completo xD

El presente tutorial tratare de explicar cómo interactuan las diferentes pantallas de nuestros juegos.
Por ejemplo:

  1. El juego inicia y presenta la pantalla del logo(SplashScreen).
  2. Luego se presenta la pantalla principal(MainScreen o MenuScreen).
  3. Dependiendo de la acción del jugador, puede dirigirse a otras pantallas, para nuestro ejemplo el usuario presiona jugar y se carga la pantalla del juego(GameScreen).
  4. Estando en la pantalla del juego pueden suceder que perdamos(GameoverScreen) o pausemos nuestro juego(PauseScreen).
En los numerales anteriores hemos visto más o menos como fluyen las iteraciones entre las pantallas.


El diagrama anterior ejemplifica la iteraciones del ejemplo que narre.
Después de un poco de teoriza vamos a nuestro amado libgdx.
Estrategia: Nuestro ejemplo tendrá dos pantallas, la primera que indicara que presionando sobre la pantalla, nos llevará a la segunda pantalla. Estando en la segunda pantalla un mensaje nos indicara que presionando sobre la pantalla, nos llevara a la pantalla anterior. Una vez estemos de vuelta en la pantalla primera, cambiara el mensaje y dirá que si presionamos en la pantalla el programa cerra.

Preparamos nuestro proyecto:

Importamos nuestro proyecto. Y nos dirigimos a la clase: "Tuto6",y hacemos:

package sv.chuckles.tuto6;

import com.badlogic.gdx.Game;

public class Tuto6 extends Game {

@Override
public void create() {
setScreen(new GameScreen(this));
}
}

Limpiamos nuestra clase y extendemos de la clase Game, la cual nos pedirá, implementar un método llamado create. Libgdx nos proporciona esta clase, para controlar las pantallas, aqui lo importante es la setScreeen, la cual iremos poniendo la pantalla que se ira presentando.
Para este caso estamos creando la pantalla GameScreen.

La clase GameScreen es implementada de la clase Screeen:

package sv.chuckles.tuto6;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class GameScreen implements Screen{
private Tuto6 game;
private BitmapFont bitmapFont;
private SpriteBatch batch;
private String texto;
private boolean isRetorno;
public GameScreen(Tuto6 game){
this.game = game;
batch = new SpriteBatch();
bitmapFont = new BitmapFont();
isRetorno = false;
}

@Override
public void show() {
bitmapFont.setColor(Color.GOLD);
if(!isRetorno)
texto = "Presione sobre la pantalla";
else
texto = "Presione sobre la pantalla, para salir";
}

@Override
public void render(float delta) {
Gdx.gl.glClearColor(0.4f, 0.4f, 0.4f, 0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
bitmapFont.draw(batch, 
"Pantalla: GameScreen\n"+texto, 
Gdx.graphics.getWidth()/2-90, 
Gdx.graphics.getHeight()/2+50);
batch.end();
if(Gdx.input.justTouched()){
if(!isRetorno){
game.setScreen(new SecondScreen(game,this));
isRetorno = true;
}else
Gdx.app.exit();
}
}

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

@Override
public void pause() {
}

@Override
public void resume() {
}

@Override
public void hide() {
if(isRetorno)
dispose();
}

@Override
public void dispose() {
Gdx.app.log("GameScreen", "Entra al dispose");
batch.dispose();
bitmapFont.dispose();
}
}
Explicacion: 
  • En el constructor GameScreen, recibimos de parámetro la clase que hereda de Game, esta clase hay que llevarla por todas las pantallas, puesto es el que controla que pantalla esta ejecutándose.
  • Hemos creado BitmapFont bitmapFont, para pintar mensajes en nuestras pantallas, con esta clase podemos escribir nuestros mensajes con Font, que podemos diseñar o descargar; para efectos de pruebas, vamos a utilizar el default.
  • He creado una variable tipo boolean isRetorno, para controlar si ya fue visitada la segunda pantalla.
  • Después del create, se ejecuta el show, y luego el render. En el método render, escribimos el mensaje: "Pantalla: GameScreen\nPresione sobre la pantalla" y luego observamos que se espera que solo se presione sobre la pantalla. Si cumple con la condición primera hacemos uso otra vez del setScreeen, veremos que en el constructor de la segunda pantalla, se envia otro parametro(this), este parametro es la GameScreen.
La segunda escena es igual que la GameScreen, a difetencia que en el render solo esta: game.setScreen(gameScreen):
@Override
public void render(float delta) {
Gdx.gl.glClearColor(0.1f, 0.1f, 0.1f, 0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
bitmapFont.draw(batch, 
"Pantalla: SecondScreen\nPresione sobre la pantalla,\npara regresar a la pantalla anterior", 
Gdx.graphics.getWidth()/2-90, 
Gdx.graphics.getHeight()/2+50);
batch.end();
if(Gdx.input.justTouched()){
game.setScreen(gameScreen);
}
}

Ya podemos ejecutar nuestro proyecto, tanto en el proyecto de android o el del escritorio.
Ejecutando en sub proyecto Desktop:


Al presionar en la pantalla, nos llevara a la segunda pantalla:

Si volvemos a presionar sobre la pantalla, nos llevara a la primera pantalla, esto es una forma de implementar la acción de Pausa en nuestros juegos.

Listo, ya podemos interactuar con muchas pantallas, según sean nuestras necesidades.
Un Saludo

Si te gusta mi trabajo y quisieras ver mas y mas rápido, puedes donar $1.00 vía donación paypal:




lunes, 11 de abril de 2016

Nuestro Primer Juego para Android

Hola buenas.
Este post lo he escrito con la intensión de compartirles un poco nuestra experiencia en el diseño y desarrollo de nuestro primer juego para android al estilo de Aero Fighters
Lo llamamos Panic Invasion, por votación a personas allegadas.
El juego consta de scroll vertical, y auto disparo. Con el control, se puede poner el dedo sobre cualquier punto de la pantalla y la nave se moverá en el sentido que arrastremos nuestro dedo.
Después de alcanzar el final de la escena, se presentara un enemigo mas grande a vencer(boss), como es típico en estos juegos. Ademas consta de 6 niveles a superar y algunos efectos de sistemas de partículas.

Screenshots:




Aqui esta la app: Panic Invasion

Objetivo:
Nuestro principal objetivo era terminar un juego completo, 100% funcional. Y dejar a un lado las pruebas de conceptos(Hacer tutoriales y ejemplos). 

Un problema grande que nos encontramos fue, que no poseemos algún diseñador para realizar artes.
Tuvimos que buscar artes gratis y descargar casi todo lo que veíamos, este fue nuestro repositorio http://opengameart.org/ 

Otro problema fue el rendimiento del juego en el dispositivo real. Esto pasaba porque cada vez que aparecía un enemigo, este era cargado en ese momento. Lo cual implicaba que el juego fuera al disco y luego cargara las texturas en la memoria del dispositivo. Pidiendo ayuda en los foros http://www.badlogicgames.com/forum/, la solución fue simple: Cargar todos los assets al inicio del juego o escena.

Luego de haber recorrido las tres faces del desarrollo de juego(Pre producción, Producción y Post producción), concluyo, que la etapa mas subestimada es la pre producción(etapa donde se planea todo el juego) y la mas difícil es la post producción(etapa de comercialización de nuestro juego).

Cloclusión:
El desarrollo de videojuegos es difícil y mas difícil es hacer buenos juegos. Aquí lo importante es la perseverancia y querer sacar un juego del cual estar orgullosos.

En pleno año 2016, existen muchas comunidades que ayudan a programadores/diseñadores que recién comienzan sus aventuras, así que animo y adelante.

Juego en google play: Panic Invasion

Sígueme en Twiter: https://twitter.com/libgdxESA

Un saludo








martes, 5 de abril de 2016

Tutorial 7.5: Tomando un Screenshot

Hola buenas.
Les quería compartir como obtener un Screenshot, es muy fácil.
Este es un agregado al tutorial anterior Tutorial 7, agregaremos un bloque de código para tomar nuestra foto en el método "longPress"; este método funciona teniendo presionado sobre la pantalla el puntero del ratón o el dedo en nuestro dispositivo.
Agregamos el siguiente bloque de instrucciones en el método longPress:

Gdx.app.log("longPress", "Screenshot inicia proceso"); byte[] pixels = ScreenUtils.getFrameBufferPixels(0, 0, Gdx.graphics.getBackBufferWidth(), Gdx.graphics.getBackBufferHeight(), true); Pixmap pixmap = new Pixmap(Gdx.graphics.getBackBufferWidth(), Gdx.graphics.getBackBufferHeight(), Pixmap.Format.RGBA8888); BufferUtils.copy(pixels, 0, pixmap.getPixels(), pixels.length); PixmapIO.writePNG(Gdx.files.external("miPantallazo.png"), pixmap); pixmap.dispose(); Gdx.input.vibrate(1000); Gdx.app.log("longPress", "Screenshot tomada");

Bueno no hay mucho que decir, lo importante es que escribe una imagen en formato PNG, ya sea en nuestro home, carpeta personal(c:/users/miPersona), en la raiz de nuestra SD si estamos en dispositivo mobil.

Para que funcione en nuestro sub proyecto de antroid, debemos de ingresar los permisos, para escritura de archivos y permisos de vibración.

Abrimos el archivo: AndroidManifest.xml
Y agregamos:
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.VIBRATE"/>

Listo, ya podemos probar nuestra toma de pantalla, tanto en el proyecto desktop y android

Sígueme en Twiter: https://twitter.com/libgdxESA

Un Saludo

Si te gusta mi trabajo y quisieras ver mas y mas rápido, puedes donar $1.00 vía donación paypal:

lunes, 4 de abril de 2016

Tutorial 7: Cámara y Gestos

Hola buenas.
En el presente tutorial, veremos como manipular un poco la cámara y unos conceptos de captura de gestos.
Es sabido que en los diversos juegos, existe un objeto fundamental, este objeto es la cámara, la cual se encarga de observar en un recuadro llamado vista, que elementos existen en ella.
En nuestro caso la cámara tiene varias propiedades:

  1. Mover y rotar la cámara en nuestras escenas.
  2. Hacer zoom in o zoom out(acercar o alejar los elementos de nuestro escena).
  3. Cambiar las relaciones de aspectos.
Aquí esta la documentación: Camara
También veremos la interfaz GestureListener, para detectar que gestos estamos haciendo en la pantalla.
Para nuestro caso de estudio, utilizaremos la funcion de "pan", la cual lee los estados cuando un dedo en la pantalla es esta siendo arrastrado. Excelente para hacer los desplazamientos entre escenas grandes. En adicion la funcion "fling" es perfecto para hacer el Swipe.

Estrategia:
Utilizaremos una imagen de 2048x1152, y nuestra pantalla tendrá 800x400, es esta pantalla centraremos la imagen, para poder visualizarla solo ese segmento. 

Luego por medio del  arrastre del mouse/dedo, desplazaremos la camera en ese sentido.
Manos a la obra:
Para comenzar, creamos nuestro proyecto, así:
Importamos en eclipse, la imagen que utilizaremos es: dbs_2048x1152.jpg, esta se debe colocar en la carpeta de assets del proyecto de android, y borrar la otra que se encuentra ahí.
Nuestro código quedaría así:
Clase Tuto5:
package sv.chuckles.tuto5;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.input.GestureDetector;
import com.badlogic.gdx.input.GestureDetector.GestureListener;
import com.badlogic.gdx.math.Vector2;

public class Tuto5 extends ApplicationAdapter implements GestureListener {
SpriteBatch batch;
Sprite sprite;
OrthographicCamera camera;
@Override
public void create () {
batch = new SpriteBatch();
sprite = new Sprite(new Texture("dbs_2048x1152.jpg"));
sprite.setPosition(-sprite.getWidth()/2,-sprite.getHeight()/2);
camera = new OrthographicCamera(800, 400);
Gdx.input.setInputProcessor(new GestureDetector(this));
}

@Override
public void render () {
camera.update();
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
   
batch.setProjectionMatrix(camera.combined);
batch.begin();
sprite.draw(batch);
batch.end();
if (Gdx.input.isKeyPressed(Input.Keys.A)) {
camera.zoom += 0.02;
        }
if (Gdx.input.isKeyPressed(Input.Keys.Q)) {
camera.zoom -= 0.02;
        }
}
@Override
public void dispose() {
batch.dispose();
}

@Override
public boolean touchDown(float x, float y, int pointer, int button) {
return false;
}

@Override
public boolean tap(float x, float y, int count, int button) {
return false;
}

@Override
public boolean longPress(float x, float y) {
return false;
}

@Override
public boolean fling(float velocityX, float velocityY, int button) {
return false;
}

@Override
public boolean pan(float x, float y, float deltaX, float deltaY) {
camera.translate(-deltaX,deltaY);
return false;
}

@Override
public boolean panStop(float x, float y, int pointer, int button) {
return false;
}

@Override
public boolean zoom(float initialDistance, float distance) {
return false;
}

@Override
public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2) {
return false;
}
}

Explicación:
  • Linea 15: ahí esta la implementacion de la interfaz "GestureListener", que nos ayuda a capturar los gestos. Cuando se implementa, a la clase se le agregan los métodos sobrescritos: touchDown, tap, longPress, fling, pan, panStop, zoom y pinch.
  • Linea 18: Creamos la variable camera.
  • Linea 26: Instanciamos el objeto camera y pasamos de parametro 800x400 que tendra de vista nuestra ventana.
  • Linea 28: Esta instrucción es muy importante, es aquí donde se le dice a LibGdx que nuestra clase implementada es la que se utilizara para la lectura de los gestos. Si somos observadores vemos que la clase GestureDetector recibe de parámetro "this", que no es nada mas que nuestra clase implementada.
  • Linea 33: Esta instrucción actualiza la vista de la cámara, es decir actualiza las coordenadas para pintarlas en nuestra ventana.
  • Linea 37: Esta instrucción convierte las coordenadas de la cámara a coordenadas naturales para el batch, porque esto, es donde escalamos las pantallas, por ejemplo nuestra escena es de 1024x768, pero nuestra ventana tiene 800x400. Entonces con esto la resolución alta se ajusta a la pequeña.
  • Linea 42 a la 47: Si presionamos la tecla A, hará un zoom in y la tecla Q, hará un zoom out, esto solo funciona en el proyecto de Desktop.
  • Linea 77: la instrucción translate, mueve la cámara, después de la acción del pan.
Ya puedes probar vía Desktop y con tu dispositivo mobil xD

Espero que les ayude y un saludo.


Sígueme en Twiter: https://twitter.com/libgdxESA

Si te gusta mi trabajo y quisieras ver mas y mas rápido, puedes donar $1.00 vía donación paypal:


lunes, 28 de marzo de 2016

Cosas Geek y Mas...

Hola buenas.

Les quería compartir una pagina hermana destinada a noticias, curiosidades, ciencias, móviles, programación, tecnologías y mas xD 
Pueden visitarla aquí: http://www.geeks503.com/
Un Saludo.

martes, 22 de marzo de 2016

Tutorial 6: Animaciones 2D

Hola buenas.
En esta ocasión veremos como animar sprites para nuestros personales, Es perfecto para crear ese dinamismo. En esta ocacion ocuparemos unos sprite descargados de: http://opengameart.org/content/jack-o-lantern-free-sprite 
Antes que todo vamos a preparar las imagenes, ya que son muy grandes. Con Gimp redimensionaremos las imagenes a 100x132 pixeles. Solo utilizaremos tres estados, el estar parado, caminando y corriendo.
Para lograr esto:

Manos a la obra.
Generamos el proyecto:

E importamos en eclipse:

En la carpeta assets de nuestro sub proyecto de android agregamos las carpetas "idle", "walk" y "run", donde estarán nuestras imágenes ya listas. Estas imágenes las van a encontrar en el código fuentes, que pondré al final.

Codigo:
Clase Tuto4;
package sv.chuckles.tuto4;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;

public class Tuto4 extends ApplicationAdapter {
SpriteBatch batch;
float stateTime;
Animation aniIdle, aniWalk, aniRun;
@Override
public void create () {
batch = new SpriteBatch();
//carga de imaganes del descanso
TextureRegion[] keyFramesIdle = {
new TextureRegion(new Texture("idle/idle_1.png")),
new TextureRegion(new Texture("idle/idle_2.png")),
new TextureRegion(new Texture("idle/idle_3.png")),
new TextureRegion(new Texture("idle/idle_4.png")),
new TextureRegion(new Texture("idle/idle_5.png")),
new TextureRegion(new Texture("idle/idle_6.png")),
new TextureRegion(new Texture("idle/idle_7.png")),
new TextureRegion(new Texture("idle/idle_8.png")),
new TextureRegion(new Texture("idle/idle_9.png")),
new TextureRegion(new Texture("idle/idle_10.png"))
}; 
aniIdle = new Animation(0.075f, keyFramesIdle);
//carga de imaganes caminando
TextureRegion[] keyFramesWalk = {
new TextureRegion(new Texture("walk/walk_1.png")),
new TextureRegion(new Texture("walk/walk_2.png")),
new TextureRegion(new Texture("walk/walk_3.png")),
new TextureRegion(new Texture("walk/walk_4.png")),
new TextureRegion(new Texture("walk/walk_5.png")),
new TextureRegion(new Texture("walk/walk_6.png")),
new TextureRegion(new Texture("walk/walk_7.png")),
new TextureRegion(new Texture("walk/walk_8.png")),
new TextureRegion(new Texture("walk/walk_9.png")),
new TextureRegion(new Texture("walk/walk_10.png"))
}; 
aniWalk = new Animation(0.075f, keyFramesWalk);
//carga de imagenes corriendo
TextureRegion[] keyFramesRun = {
new TextureRegion(new Texture("run/run_1.png")),
new TextureRegion(new Texture("run/run_2.png")),
new TextureRegion(new Texture("run/run_3.png")),
new TextureRegion(new Texture("run/run_4.png")),
new TextureRegion(new Texture("run/run_5.png")),
new TextureRegion(new Texture("run/run_6.png")),
new TextureRegion(new Texture("run/run_7.png")),
new TextureRegion(new Texture("run/run_8.png"))
}; 
aniRun = new Animation(0.07f, keyFramesRun);

stateTime = 0;
}

@Override
public void render () {
Gdx.gl.glClearColor(0.5f, 0.5f, 0.5f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stateTime += Gdx.graphics.getDeltaTime();
batch.begin();
batch.draw(aniIdle.getKeyFrame(stateTime, true), 0, 0);
batch.draw(aniWalk.getKeyFrame(stateTime, true), 0, 170);
batch.draw(aniRun.getKeyFrame(stateTime, true), 0, 340);
batch.end();
}
@Override
public void dispose() {
batch.dispose();
}
}

Al ejecutarlo, veremos:

Explicación:
En la declaración de variables, veremos:
  1. float stateTime: Este flotante sera utilizado para acumular el tiempo que transcurre desde el ultimo frame en segundos. para que las animaciones sean pintadas de forma sincronizadas y correctas.
  2. Animation aniIdle, aniWalk, aniRun: Estas variables son las encargadas de almacenar todas nuestras imágenes y que ordenan las animaciones. La variable aniIdle es para el personaje en animación parado, aniWalk para la animación caminando y aniRun para la animación corriendo.
Método create:
  1. Lineas 20 a 31: Creamos un arreglo TextureRegion, donde cargaremos nuestras imágenes cuando el personaje esta parado, las imagines son del 1 al 10 de la carpeta "idle".
  2. Linea 32 - aniIdle = new Animation(0.075f, keyFramesIdle): Instanciamos la clase y le enviamos de parámetro tiempo en segundos que se ejecuta una frame después del otro. Para este caso 0.075 Seg. Y el ultimo parámetro es el arreglo de los imágenes.
  3. Lineas 35 a 47: Igual que el literal anterior. Con la variante que es para la animación de caminar.
  4. Lineas 50 a 60: Lo mismo solo que con la variante de las imágenes de correr.
Metodo render:
  1. stateTime += Gdx.graphics.getDeltaTime(): Es el acumulador ya descrito en la sección 1 de la Explicación.
  2. batch.draw(aniIdle.getKeyFrame(stateTime, true), 0, 0): Pintamos el frame actual, calculado por el stateTime en la posición (0,0), también se indica con el parámetro "true" que es un ciclo infinito, osea se repiten siempre las animaciones.
  3. batch.draw(aniWalk.getKeyFrame(stateTime, true), 0, 170): Igual que el literal anterior, solo que se pinta la animación de caminar en la posición (0,170).
  4. batch.draw(aniRun.getKeyFrame(stateTime, true), 0, 340): Igual que el literal anterior, solo que se pinta la animación de correr en la posición (0,340).
Ejecutamos esto, y veremos nuestras animaciones en acción xD
Ahora bien, que se necesita para pintar animaciones que estén de derecha a izquierda?
Pues hay dos formas, la primera es crear otra animación con las imágenes invertidas en X, pero esto conlleva que cargamos imágenes que ya existen y lo cual no es recomendable hacerlo por rendimiento. Así que la solución mas optima es utilizar nuestro objeto "batch", he indicar que pinte al revés nuestra animación.
Asi: 

batch.draw(aniRun.getKeyFrame(stateTime, true), 600 //posicion en X
,340 //posicion en Y
,-aniRun.getKeyFrame(stateTime).getRegionWidth() //ancho de la imagen
,aniRun.getKeyFrame(stateTime).getRegionHeight() //alto de la imagen
);
Aquí veremos una sobre carga del método "draw", lo importante es que recibe de parámetro extras las dimensiones de las animaciones, entonces ponemos negativo el ancho xD

Ejecutamos:

También lo podemos probar en el emulador o dispositivo:

Código fuente: https://github.com/Solidux/tuto-libgdxelsalvador.blogspot.com/tree/master/tuto4

Espero que todo haya quedado claro xD
Un Saludos

Sigueme en Twiter: https://twitter.com/libgdxESA

Si te gusta mi trabajo y quisieras ver mas y mas rápido, puedes donar $1.00 vía donación paypal:


jueves, 17 de marzo de 2016

Tutorial 5: Detecciones de Colisiones o Choques

Hola Gente.
En esta ocasiona veremos como detectar los choques entre elementos rectangulares y circulares.
Existe también los polígonos, pero por simplicidad veremos estos dos solamente.
Ustedes debes de elegir que tipo utilizar según sean tus necesidades, normalmente se utilizan mas las figuras rectangulares.
Para las detecciones rectangulares utilizaremos la clase "com.badlogic.gdx.math.Rectangle" y para las  circulares utilizaremos la clase "com.badlogic.gdx.math.Circle".

Estrategia:
Crearemos 3 sprites del logo de LibGdx, con las dimensiones 128x128, estos estarán situados en la pantalla en forma de triangulo. Al sprite mas alto sera estático y tendrá asociado un rectángulo igual a sus dimensiones y asociado un circulo que tendrá mas o menos las dimensiones de la imagen circular del logo. Cuando me refiero al termino "asociado", quiero decir que el rectángulo o circulo se van a mover en las mismas posiciones del sprite. Con respecto a los otras sprites, uno sera asociado con un rectángulo y el otro a un circulo; con la finalidad que puedan chocar con el mas alto.

Otra estrategia sera colocar un tinte diferente para diferenciar cada sprite, también moveremos el sprite de la izquierda con las teclas ASWD y el de la derecha con las teclas cursoras.

Comencemos: Lo primero es crear nuestro proyecto:

Importamos nuestro proyecto en eclipse, como ya lo hemos hecho. Un dato importante es que no incluiremos el sub proyecto para android, puesto moveremos nuestros sprite con el teclado.
Clase Tuto3:
package sv.chuckles.tuto3;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
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.Circle;
import com.badlogic.gdx.math.Rectangle;

public class Tuto3 extends ApplicationAdapter {
SpriteBatch batch;
Sprite sprite1,sprite2,sprite3;
Rectangle rec1,rec2;
Circle cir1,cir3;
ShapeRenderer renderer = null;
@Override
public void create () {
batch = new SpriteBatch();
sprite1 = new Sprite(new Texture("badlogic.jpg"));
sprite2 = new Sprite(new Texture("badlogic.jpg"));
sprite3 = new Sprite(new Texture("badlogic.jpg"));
sprite1.setPosition(Gdx.graphics.getWidth()/2-sprite1.getWidth()/2, Gdx.graphics.getHeight()-sprite1.getHeight()-50);
rec1 = new Rectangle(sprite1.getX(), sprite1.getY(), sprite1.getWidth(), sprite1.getHeight());
cir1 = new Circle(sprite1.getX()+sprite1.getWidth()/2, sprite1.getY()+sprite1.getWidth()/2, 50f);
sprite2.setColor(Color.BLUE);
rec2 = new Rectangle(0, 0, sprite2.getWidth(), sprite2.getHeight());
sprite3.setPosition(Gdx.graphics.getWidth()-sprite3.getWidth(),0);
sprite3.setColor(Color.MAGENTA);
cir3 = new Circle(0, 0, 50f);
//Instancia para dibujar la figuras matematicas
//comentarias esta linea si no se quiere ver los rectangulos y circulos dibujados
renderer = new ShapeRenderer();
}

@Override
public void render () {
Gdx.gl.glClearColor(0.5f, 0.5f, 0.5f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
detectarMovimiento(sprite2,false);
detectarMovimiento(sprite3,true);
detectarColisiones();

//pintado de las imagenes en la pantalla
batch.begin();
sprite1.draw(batch);
sprite2.draw(batch);
sprite3.draw(batch);
batch.end();
if(renderer != null){
renderer.begin(ShapeType.Line);
renderer.setColor(Color.GOLD);
renderer.rect(rec1.getX(), rec1.getY(), rec1.getWidth(), rec1.getHeight());
renderer.circle(cir1.x, cir1.y, cir1.radius);
renderer.setColor(Color.WHITE);
renderer.circle(cir3.x, cir3.y, cir3.radius);
renderer.rect(rec2.getX(), rec2.getY(), rec2.getWidth(), rec2.getHeight());
renderer.end();
}
}
private void detectarColisiones() {
//detectar si hay colision entre los rectangulos
if(rec1.overlaps(rec2)){
sprite2.setColor(Color.YELLOW);
}else{
sprite2.setColor(Color.BLUE);
}
if(cir1.overlaps(cir3)){
sprite3.setColor(Color.YELLOW);
}else{
sprite3.setColor(Color.MAGENTA);
}
}

private void detectarMovimiento(Sprite sprite,boolean isKeyCursorPress){
float x = sprite.getX();
float y = sprite.getY();
if(isKeyCursorPress){
if(Gdx.input.isKeyPressed(Keys.LEFT))
x -= 5;
else if(Gdx.input.isKeyPressed(Keys.RIGHT))
x += 5;
if(Gdx.input.isKeyPressed(Keys.UP))
y += 5;
else if(Gdx.input.isKeyPressed(Keys.DOWN))
y -= 5;
}else{
if(Gdx.input.isKeyPressed(Keys.A))
x -= 5;
else if(Gdx.input.isKeyPressed(Keys.D))
x += 5;
if(Gdx.input.isKeyPressed(Keys.W))
y += 5;
else if(Gdx.input.isKeyPressed(Keys.S))
y -= 5;
}
if(x < 0) x=0;
if(y < 0) y=0;
if(x>Gdx.graphics.getWidth()) x=Gdx.graphics.getWidth();
if(y>Gdx.graphics.getHeight()) y=Gdx.graphics.getHeight();
sprite.setPosition(x, y);
//tambien tenemos que mover el rectangulo o el circulo
if(isKeyCursorPress)//si es verdad, movemos el circulo
cir3.setPosition(x+sprite.getWidth()/2, y+sprite.getHeight()/2);
else
rec2.setPosition(x, y);
}
@Override
public void dispose() {
if(renderer != null)
renderer.dispose();
batch.dispose();
}
}

Explicacion:

Declaraciones de objetos: No hay nada del otro mundo. Solo existe este objeto "ShapeRenderer renderer" que es de suma importancia, ya que en el es posible dibujar gráficos primitivos como lineas, rectángulos, círculos, etc. Entonces para ¿qué nos servirá esto?, pues los objetos rectángulos y círculos son lógicos, no son representaciones gráficas y nos auxiliaremos de esta clase.

Método create:
El sprite1 era destinado al estático, que tendrá asociado el rectangular rec1 y el circulo cir1, aquí instanciamos el objeto rec1, que el constructor recibe de parámetros la posición del rectángulo y las dimensiones del ancho y alto, para nuestro caso tendríamos que poner los mismas coordenadas de sprite1: rec1 = new Rectangle(sprite1.getX(), sprite1.getY(), sprite1.getWidth(), sprite1.getHeight());
Para el caso del circulo: cir1 = new Circle(sprite1.getX()+sprite1.getWidth()/2, sprite1.getY()+sprite1.getWidth()/2, 50f); donde se coloca la posicion del centro y su radio.
Creo que aquí todo esta fácil de entender, a esta altura del partido ya debemos de estar muy familiarizados con el código.

Método render:
Aquí esta toda la lógica, encontraremos el método: "detectarMovimiento", el cual detecta que tecla fue tocada y mueva la imagen y su rectángulo o circulo asociado.
Donde nos centraremos es en el método "detectarColisiones", este metodo observamos como se pregunta que el rec1 esta sobre el rec2, si esto es cierto el tinte cambia del sprite2:

Tambien se pregunta que el cir1 esta sobre el cir2, si es cierto también cambia de tinte amarillo:

Podemos ver como funciona las colisiones, mas sencillo imposible xD
Si no quieren ver los rectángulos o círculos dibujado pues comentaríamos la linea 43
Este "ShapeRenderer" es nuestro DEBUG gráfico.


Para el siguiente tutorial, veremos como hacer animaciones
Un Saludos
Sigueme en Twiter: https://twitter.com/libgdxESA

Si te gusta mi trabajo y quisieras ver mas y mas rápido, puedes donar $1.00 vía donación paypal: