0% encontró este documento útil (0 votos)
59 vistas135 páginas

Lib GDX

Descargar como pdf o txt
Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1/ 135

Creación de Videojuegos

con Java
¿Qué vamos a hacer?
¿Qué necesitamos?

Conocimientos de Java

Android SDK

Android Studio
– Recuerda que es multiplataforma
– Necesitamos un emulador

Generador de proyectos de libGDX:
– https://libgdx.com/assets/downloads/legacy_setup/gdx-setup_l
atest.jar

NOTA: xorg-xrandr package debe ser instalado en Linux
Alternativas sobre otros IDEs
ECLIPSE

Se puede descargar desde (nos interesa la versión
java): https://www.eclipse.org/ide/
– Para importar un proyecto GDX: File -> Import ->
Gradle -> Existing Gradle Project
– Cambiar las propiedades a Gradler 6,7,1
– El nivel de java para compilar es 8
– Plugins de android:

https://sites.google.com/site/pruebajoseog/instalaci/3-
instalacion-del-plug-in-android-para-eclipse-adt
Propiedades
Eclipse – Plugin Android

Help – new software
– https://dl-ssl.google.com/android/eclipse/
Eclipse - SDK
Android SDK

Es necesario que sepas dónde tienes instalado el
SDK de Android:
Generador de proyectos

Para ejecutarlo: java -jar ./gdx-setup_latest.jar
Abrimos el proyecto

¿Qué tenemos?
– Un proyecto que con
Android Studio como IDE
vamos a poder modificar.
– Vamos a programar una
vez, pero tendremos tantas
versiones de la aplicación
(móvil, escritorio, html..)
como indicásemos
Y esto cómo se ejecuta?

Vamos a tener diferentes proyectos:
– Android. Proyecto para móviles android, lo podremos
ejecutar en el emulador
– Desktop. Lo utilizaremos para probar la aplicación sin
necesidad de un emulador.
– Html. Vista en Html.
– Core – Ésta es la importante. Será donde vamos a
codificar el juego.
Android Studio
Proyecto de Escritorio
Empezamos a codificar

No vamos a crear una aplicación desde cero

Vamos a construir por encima de la aplicación
libGDX

Todo el control lo llevará libGDX.

Nosotros programaremos una serie de métodos que
sabemos que en ciertas circunstancias serán
llamados por libGDX.
ApplicationListener

Esta clase será llamada por Aplication
Ciclo de vida de la aplicación
Empezamos a codificar

En el proyecto CORE, todo lo haremos sobre él, abrimos la clase
MainGame.

Limpiaremos el código de ejemplo y vamos a reescribirlo.
– Usaremos Texturas: El objeto donde libGDX almacena las imágenes.
– Utilizaremos Lotes de Texturas (SpriteBatch): Es más eficiente cargar un
conjunto de texturas juntas que 1 a 1.
– En el método create() inicializaremos la textura que representará al personaje y
el SpriteBatch)
– En el método render() cargaremos las texturas

Limpiamos la pantalla

Comenzamos la carga de texturas
– Cargamos la textura de nuestra imagen.

Finalizamos la carga de texturas
– En el método dispose() liberaremos los recursos. ESTO ES MUY
IMPORTANTE PARA NO PENALIZAR EL DISPOSITIVO MÓVIL.
Proyecto Core

Abrimos la clase y
limpiamos el código
(atributos y contenido
de los métodos).
Creamos los atributos para las
texturas
private Texture img; // Imagen del reno
private SpriteBatch batch;//Texturas en lotes


Se utiliza la Clase Texture para las imágenes

Se crea un lote de Textuas: SpriteBatch. Es más
eficiente cargar un lote de texturas que cargar las
texturas 1 a 1.
create()
public void create () {
img = new Texture("reno_pers.png");
batch = new SpriteBatch();
}


Inicializamos las texturas: reno_pers.png es una
imagen de la carpeta assets del proyecto android.

Inicializamos el lote de texturas.
dispose()
public void dispose () {
reno.dispose();
batch.dispose();
}


Los recursos utilizados se deben liberar, si no se
liberan penalizarán el funcionamiento del móvil.
render()
public void render () {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); /
batch.begin();
batch.draw(reno, 0, 0);
batch.end();
}

Este método será utilizado para dibujar.
– Limpiaremos la pantalla (glClear)
– Iniciamos la carga del lote

Cargamos la textura de nuestra imagen.
– Finalizamos la carga del lote y libGDX lo mostrará de forma automática.
Ejecutamos el juego
LibGDX utiliza como posición inicial del eje de
coordenadas (0,0) la esquina inferior izquierda.
Podemos saber la altura y anchura con:
Anchura: Gdx.graphics.getWidth() ;
Altura: Gdx.graphics.getHeight());
Atributos para el ancho y la altura
de la pantalla
public void create () { ●
Creamos dos atributos
[...]
width = Gdx.graphics.getWidth(); de tipo entero y en el
height =
Gdx.graphics.getHeight();
método create los
widthImg = img.getWidth();
inicializamos.
heightImg = img.getHeight(); ●
Vamos a hacer lo
}
mismo con el tamaño
de la textura del reno.
Situar el reno en la esquina
superior derecha.
public void render () {
[...]
batch.draw(img, width-widthReno, height-heightReno);
}


Calculamos la posición a partir de
– Tamaño de la pantalla
– Tamaño de la imagen.
Pintar el fondo
public void render () {
Gdx.gl.glClearColor(0,255,0,1);
[...]
}


Estableceremos el color de fondo
– Gdx.gl.glClearColor(R,G,B,A).
Mostrar varios renos en la
pantalla
public void render () {
[...]
batch.draw(reno, width-widthReno, height-heightReno);
batch.draw(reno, 0, 0);
batch.draw(reno, 0, height-heightReno);
[...]
}


Podemos poner tantos draws como queramos.
Trabajar con Regiones

Es recomendable utilizar imágenes cuyo tamaño
sean potencias de 2.

A veces va a ser necesario recortar una imagen, en
este caso me va a venir bien utilizar regiones y
decirle a libGDX que la recorte.
– Damos de alta la nueva textura: pincho, la inicializamos
y hacemos el dispose() correspondiente.

Creamos la Región
Crear la región
private TextureRegion regionPinchos;
create(){
regionPinchos = new TextureRegion(pinchos,0,64,256,64 );
}
Render(){
batch.draw(regionPinchos, 250, 0);
}


Las imágenes tienen su eje de coordenadas arriba
(no abajo).

Recortaremos la imagen al inicializar la imagen.
Detectar la entrada

Pulsar una tecla

Usar el ratón

En android → los dedos y teclas físicas
Detectar la entrada

Tenemos muchas formas de detectar la interacción
con el usuario utilizando Gdx.input.XXXX
Detectar si se toca la pantalla
if (Gdx.input.isTouched()){
//La pantalla está siendo tocada.
System.out.println("Se está tocando la
pantalla");
}
Ojo Render se ejecuta muchas veces por
segundos, por lo que se imprimirá el mensaje
varias veces.
Detectar si se ACABA DE tocar
la pantalla
if (Gdx.input.justTouched()){
//Se acaba de tocar la pantalla
System.out.println("Pantalla tocada");
}
Solo mostrará el mensaje cada vez que la
toquemos, una única vez.
Detectar si se toca una tecla
if(Gdx.input.isKeyPressed(Input.Keys.A)){
System.out.println("Se ha pulsado la A");
}
Las teclas se encuentran en la clase: Input.Keys
Al igual que ocurría antes, si solo quiero que aparezca
una vez el mensaje usare: isKeyJustPressed
Ejercicio
Modifica tu método Render para que al pulsar la
tecla SPACE la imagen se mueva a la derecha y si
se pulsa la pantalla, se mueva a la izquierda.
Problemas de las detecciones
anteriores

El código está en el método render

Render se ejecuta varias veces por segundo

Se gastan recursos para hacer estas
comprobaciones.

Hay otra alternativa mejor para esto → Procesares
(asíncrono)
Eventos de Entrada

Crearemos el código de los eventos

Gdx llamará al código de los eventos cuando se
produzcan.
– El código se encuentra dentro de los “Procesadores”
– Se ejecutarán en hilos independientes
– No molestará al flujo principal.
– Necesitamos implementar la interfaz InputProcessor.
Procesadores

Contienen el código que se debe ejecutar cuando se produce
un evento.

Se implementan en clases que implementan la interfaz
InputProcessor. Esta interfaz tiene varios métodos:
– XxDown → Cuando empezamos a pulsar una tecla, pantalla…
– XxUP → Cuando soltamos la tecla o dejamos de pulsar la
pantalla.
– MouseMoved → Si se mueve el ratón
– TouchDragged → Cuando estamos arrastrando el dedo.
– KeyTyped → Cuando hemos pulsado una tecla
Procesadores

Si no vamos a implementar todos los métodos, nos resultará
más útil que nuestra clase extienda de InputAdapter (que
implementa toda la interfaz InputProcessor) y solo
sobrescribimos los métodos que nos interesan
– public class Procesador implements InputProcessor
– public class Procesador extends InputAdapter

Los métodos devuelve booleanos, para indicarle a Gdx si
hemos hecho algo o no.
– Ojo, no tiene sentido que el xxxDown devuelva false, y tengamos
código en el xxxUp. El xxUp no se ejecutará.
Procesadores
package es.tiernoparla;

import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.InputProcessor;

public class Procesador extends InputAdapter {

@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
System.out.println("Has tocado en la posición "+screenX+", "+screenY);
return true;
}

}
Procesadores y código principal

En el código principal, en el método create, le
dirimos a Gdx que utilice nuestro procesador de
eventos:
public void create () {
Procesador p = new Procesador();
Gdx.input.setInputProcessor(p);
Ejercicio
Modifica tu videojuego para que cuando se pulse
una tecla, ésta sea visualizada en la consola.
Nota: Se debe usar el un procesador de entradas.
Conceptos elementales
Scene 2D

Una vez visto los conceptos elementales, empezamos.

Scene 2D soporta:
– Dibujar cosas
– Escalados
– Rotaciones
– Colisiones
– Procesadores de Entrada
– Acciones de los elementos

Crearemos escenas y pondremos actores

Scene2d.ui → Interfaces de usuario (botones, viñetas de texto…)
– Solo indicaremos las texturas

ECS Ashley → Scene2D avanzado, se usan componentes
– ECS = Entity Component System.
Scene2D

actions
listeners
ACTOR
draw
STAGE act
ACTOR
ACTOR
GROUP
ACTOR
Pantalla de Bienvenida

Queremos tener una pantalla de bienvenida, y no directamente el juego
(que lanza la ejecución del render).

Para tener varias pantallas Gdx nos permite extender desde Game, en
lugar de ApplicationAdapter. Deberemos cambiar de la clase principal
el ApplicationAdapter por Game.

Game incluye el elemento screen, y podremos jugar con una pantalla u
otra.
– Crearemos una nueva clase Java que implemente Screen.
– Tendremos que implementar sus métodos: show(), render(), resize()…
– Ahora render tienen un parámetro “Delta”, que nos va a venir bien para que las
cosas “se muevan” saber cuánto tiempo (valor de delta) pasó desde que se
llamó a la última vez al método render().
Pantallas
import com.badlogic.gdx.Screen; package com.mygdx.game;

public class BaseScreen implements Screen {


import com.badlogic.gdx.Game;
@Override
public void show() {
import com.badlogic.gdx.Gdx;
} import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
@Override import
public void render(float delta) { com.badlogic.gdx.graphics.g2d.SpriteBatch;
}

//ApplicationAdapter applicationAdapter por


@Override game
public void resize(int width, int height) {
public class MyGdxGame extends Game {
[...]
Pantallas

Suele ser una buena práctica relacionar las pantallas con la clase
principal, ya que nos resultará de interés (por ejemplo: navegar de
una pantalla a otra):
public class BaseScreen implements Screen {

protected MainGame game;

public BaseScreen(MainGame game){


this.game = game;
}
Pantalla

Si tenemos 4 pantallas, tendríamos que implementar la lógica anterior 4 veces…
Podemos optimizarlo si la convertimos en clase abstracta y las pantallas extender de
ella:
public abstract class BaseScreen implements Screen {

private MainGame game;

public BaseScreen(MainGame game){


this.game = game;
}
public class BienvenidaScreen extends BaseScreen{
}
Crear la pantalla principal

Crearemos una pantalla principal del juego
“MainGame” que va a extender de BaseScreen (y
por lo tanto ya estará conectada con MyGdxGame)
– Podremos enlazar con preferencias

Cambiar personaje

Quitar la música, etc.
– Poder tener varias pantallas.
MainGameScreen

Scene2D se caracteriza por tener un Stage donde
estarán los actores.

Así que lo primero es crear el Stage.
– Creamos el atributo Stage
– Lo inicializamos en el show
– Y lo liberamos en el hide (cuando se deje de ver la
screen).
Stage
package com.mygdx.game;

import com.badlogic.gdx.scenes.scene2d.Stage;

public class MainGameScreen extends BaseScreen {

private Stage stage;


public MainGameScreen(MyGdxGame game) {
super(game);
}

public void show() {


stage = new Stage();
}

public void hide() {


stage.dispose();
}
}
Stage

Ahora agregaremos el resto de actores (pinchos,
personaje…)

Tenemos que decirle al Stage que se coordine (dibuje,
actualice) en el render.
– Draw: Pintar los actores y escenario
– Act: Actualizar los actores (por ejemplo si unos pinchos se
mueven hacia el personaje).

Finalmente hay que decirle a la clase del juego que cargue la
screen MainGameScreen.
– Create().
package com.mygdx.game;

Stage
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.scenes.scene2d.Stage;

package com.mygdx.game; public class MainGameScreen extends BaseScreen {

private Stage stage;


import com.badlogic.gdx.Game;
public MainGameScreen(MyGdxGame game) {
import com.badlogic.gdx.Gdx; super(game);
import com.badlogic.gdx.graphics.GL20; }

import com.badlogic.gdx.graphics.Texture; @Override


public void show() {
import com.badlogic.gdx.graphics.g2d.SpriteBatch; stage = new Stage();
}

//ApplicationAdapter applicationAdapter por game @Override


public void hide() {
public class MyGdxGame extends Game {
stage.dispose();
}

@Override @Override

public void create () { public void render(float delta) {


// Color azul claro del cielo
Gdx.gl.glClearColor(0.4f, 0.5f , 0.8f, 1.f);

//Pantalla de bienvenida //limpiar la pantalla

setScreen(new MainGameScreen(this)); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

} stage.act(); //Actualiar los actores


stage.draw(); //Dibujar los actores

} }

}
Crear actores
Vamos a crear un actor que representará a nuestro
protagonista.
– Creamos una paquete “actors”
– Creamos una clase ActorJugador, que va a representar a nuestro
personaje. Esta clase tiene que extender de Actor.
– Los actores se añaden al stage (stage.addActor())

Actor proporciona dos métodos elementales
– act() Para que se actualice
– draw() Para pintarlo
– Los actores son independientes los unos de los otros y tienen su
propio comportamiento.
Crear actores

Instanciar un actor
– Private ActorJugador jugador;
– En show
jugador = new ActorJugador();
stage.addActor(jugador);
La clase Actor nos dará un montón de opciones para
trabajar con el actor:

setXXX
– SetPosition(); //Lo localiza en unas coordenadas
Esto crea el actor, pero no hará nada, el Actor está
vacío. Debemos darle apariencia y comportamiento.
Crear Actores
private ActorJugador jugador;
package com.mygdx.game.actors;

public MainGameScreen(MyGdxGame game) {

super(game);

}
import com.badlogic.gdx.scenes.scene2d.Actor;

@Override

public void show() {

stage = new Stage(); public class ActorJugador extends Actor {

jugador = new ActorJugador();

stage.addActor(jugador);

}
jugador.setPosition(20, 100);

@Override

public void hide() {

stage.dispose();

@Override

public void render(float delta) {

// Color azul claro del cielo RGBA

Gdx.gl.glClearColor(0.4f, 0.5f , 0.8f, 1.f);

//limpiar la pantalla

Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

stage.act(); //Actualiar los actores

stage.draw(); //Dibujar los actores

}
Pintar al Actor

Hay varias formas (luego se introducen formas mejores).
– Creamos un campo de tipo Textura en la clase actor
– Creamos un constructor de ActorJugador que cargue la textura:
public ActorJugador() {
jugador = new Texture("ninja.png");
}
– Lo pintamos (getX y getY nos dan las coordenadas)
public void draw(Batch batch, float parentAlpha) {
batch.draw(jugador, getX(), getY());
}
– Esto tiene inconvenientes: No estamos haciendo dispose (problemas en la
mejoria) y ¿qué pasa si quiero cambiar de personaje?
Pintar Actor

En lugar de crear la textura e instanciarla en el constructor, la vamos a pasar
por parámetro al constructor.
– Si quiero cambiar la textura, solo cambio el valor del parámetro.

Actualizo el método show():
private Texture texturaJugador;
public void show() {
stage = new Stage();
texturaJugador = new Texture("ninja.png");
jugador = new ActorJugador(texturaJugador);
stage.addActor(jugador);
jugador.setPosition(20, 100);
}
Y en el método hide ya puedo hacer el dispose de la textura.
Pintar jugador
public class MainGameScreen extends BaseScreen { package com.mygdx.game.actors;

private Stage stage; import com.badlogic.gdx.graphics.Texture;


import com.badlogic.gdx.graphics.g2d.Batch;
private ActorJugador jugador; import com.badlogic.gdx.scenes.scene2d.Actor;
private Texture texturaJugador;
public class ActorJugador extends Actor {
public MainGameScreen(MyGdxGame game) {
super(game); public Texture jugador;
}
public ActorJugador(Texture jugador) {
@Override this.jugador = jugador;
public void show() { }
stage = new Stage();
@Override
texturaJugador = new Texture("ninja.png");
jugador = new ActorJugador(texturaJugador); public void act(float delta) {
stage.addActor(jugador);
}
jugador.setPosition(20, 100);
} @Override
public void draw(Batch batch, float parentAlpha) {
@Override batch.draw(jugador, getX(), getY());
public void hide() { }
stage.dispose();
texturaJugador.dispose(); }
}
Pintar jugador

Hemos arreglado los problemas anteriores, pero si ya
tengo un dispose en BaseScreen…
– Vamos a usarlo, usaremos el dispose para quitar de la memoria
la textura.

Ojo, si lo quito del hide… y se llama varias veces al show,
se perdería la referencia a la textura y estaré dejando sucia
la memoria. Mejor lo hacemos al cargar la principio.
– Modificamos el constructor y en él damos de alta la textura.

De todas formas… se puede hacer mejor (próximamente)
Pintar al Jugador
package com.mygdx.game;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.mygdx.game.actors.ActorJugador;

public class MainGameScreen extends BaseScreen {

private Stage stage;

private ActorJugador jugador;


private Texture texturaJugador;

public MainGameScreen(MyGdxGame game) {


super(game);
texturaJugador = new Texture("ninja.png");
}

@Override
public void show() {
stage = new Stage();

jugador = new ActorJugador(texturaJugador);


stage.addActor(jugador);

jugador.setPosition(20, 100);
}

@Override
public void hide() {
stage.dispose();
}

@Override
public void render(float delta) {
// Color azul claro del cielo RGBA
Gdx.gl.glClearColor(0.4f, 0.5f , 0.8f, 1.f);

//limpiar la pantalla
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

stage.act(); //Actualiar los actores


stage.draw(); //Dibujar los actores

@Override
public void dispose() {
texturaJugador.dispose();
}

}
Otros actores

Vamos a incluir el actor piedra
– Seguimos los mismos pasos que con ActorJugador
– En jugar de Textura tendremos TextureRegion, para conocer
los límites de la textura

Se quiere que la piedra avance a la izquierda.
– Modificaremos el método act() del ActorPiedra.
– Usaremos el parámetro delta para simularlo.

Vamos a jugar con el tiempo. Estableceremos el tiempo que se
necesita para que los pinchos lleguen usando el delta.
– setX(getX() - 250 * delta) // Si queremos que recorra 250 px
Movimiento

Debe hacerse en 2 segundos

500 px

500px / 2 segundos = 250 px/segundo

Y delta me da el tiempo entre ejecuciones del método act(delta)

setX(getX() - 250 * delta)


Movimiento
package com.mygdx.game.actors;
package com.mygdx.game;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.mygdx.game.actors.ActorJugador;
import com.mygdx.game.actors.ActorPiedra; import com.badlogic.gdx.graphics.g2d.TextureRegion;
public class MainGameScreen extends BaseScreen { import com.badlogic.gdx.scenes.scene2d.Actor;
private Stage stage;

private ActorJugador jugador;


private ActorPiedra piedra; public class ActorPiedra extends Actor {
private Texture texturaJugador;
private Texture texturaPiedra;
private TextureRegion regionPiedra;

public MainGameScreen(MyGdxGame game) { public TextureRegion piedra;


super(game);
texturaJugador = new Texture("ninja.png");
texturaPiedra = new Texture("Stone.png");

regionPiedra = new TextureRegion(texturaPiedra,0,0,128,64);


public ActorPiedra (TextureRegion piedra) {
}
this.piedra = piedra;
@Override
public void show() {
stage = new Stage();
}
jugador = new ActorJugador(texturaJugador);
piedra = new ActorPiedra(regionPiedra);
stage.addActor(jugador); @Override
stage.addActor(piedra);

jugador.setPosition(20, 100);
piedra.setPosition(400, 100);
} public void act(float delta) {
@Override setX(getX() - 250 * delta);
public void hide() {
stage.dispose();
}

@Override }
public void render(float delta) {
// Color azul claro del cielo RGBA
Gdx.gl.glClearColor(0.4f, 0.5f , 0.8f, 1.f);

//limpiar la pantalla @Override


Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
public void draw(Batch batch, float parentAlpha) {
stage.act(); //Actualiar los actores
stage.draw(); //Dibujar los actores
batch.draw(piedra, getX(), getY());
}
}
@Override
public void dispose() {
texturaJugador.dispose();
}

}
}
Problemas

La clase MainGameScreen tiene solo dos actores y
empieza a tener mucho código repetido e
incómodo.
– Esto lo tendremos que mejorar

El personaje no reacciona a la piedra.
– Necesitamos introducir colisiones
Colisiones
Stage me permite saber los límites
– Vamos a indicarle a Stage que nos pinte los bordes de los objetos: stage.setDebugAll(true).
public void show() {
stage = new Stage();
stage.setDebugAll(true);
– Para que funcione, hay que darle a los actores unas dimensiones (la cogemos de la
TextureRegion). Hay que hacerlo en los dos actores:
public ActorPiedra (TextureRegion piedra) {
this.piedra = piedra;
setSize(piedra.getRegionWidth(), piedra.getRegionHeight());
}
public ActorJugador(Texture jugador) {
this.jugador = jugador;
setSize(jugador.getWidth(), jugador.getHeight());
}
Colisiones

Veremos un borde “verde” durante la ejecución,
sus límites
Colisiones

jugador.getX() jugador.getX()
+jugador.getWidth()
Si hay una colisión:
jugador.getX()+jugador.getWidth() > roca.getX()
Colisiones

En el método render (MainGameScreen)
meteremos una llamada a un nuevo método:
comprobarColisiones():
– Primero comprobamos stage.act();
– Comprobamos las colisions
– Finalmente lo pintamos: stage.draw();
Colisiones

Agregamos el atributo “alive” (booleano) al
jugador. Para saber si está vivo.

Creamos el comprobarColisiones:
private void comprobarColisiones() {
if (jugador.isAlive() && jugador.getX()+jugador.getWidth()>
piedra.getX())
System.out.println("Colisión: Jugador aplastado");
jugador.setAlive(false);
}
package com.mygdx.game;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;

Colisiones
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.mygdx.game.actors.ActorJugador;
import com.mygdx.game.actors.ActorPiedra;

public class MainGameScreen extends BaseScreen {

private Stage stage;

private ActorJugador jugador;


package com.mygdx.game.actors; private ActorPiedra piedra;
private Texture texturaJugador;
private Texture texturaPiedra;
import com.badlogic.gdx.graphics.Texture; private TextureRegion regionPiedra;

import com.badlogic.gdx.graphics.g2d.Batch; public MainGameScreen(MyGdxGame game) {


super(game);
import com.badlogic.gdx.scenes.scene2d.Actor; texturaJugador = new Texture("ninja.png");
texturaPiedra = new Texture("Stone.png");

public class ActorJugador extends Actor { regionPiedra = new TextureRegion(texturaPiedra,0,0,128,64);


}

@Override
private Texture jugador; public void show() {
private boolean alive; stage = new Stage();
stage.setDebugAll(true);

jugador = new ActorJugador(texturaJugador);


piedra = new ActorPiedra(regionPiedra);
stage.addActor(jugador);
public ActorJugador(Texture jugador) { stage.addActor(piedra);
this.jugador = jugador;
jugador.setPosition(20, 100);
this.alive = true; piedra.setPosition(500, 100);
}

setSize(jugador.getWidth(), jugador.getHeight()); @Override


public void hide() {
} stage.dispose();
}

@Override
@Override public void render(float delta) {
public void act(float delta) { // Color azul claro del cielo RGBA
Gdx.gl.glClearColor(0.4f, 0.5f , 0.8f, 1.f);

//limpiar la pantalla
} Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

stage.act(); //Actualiar los actores


@Override
//Primero comprobamos y luego dibujamos
public void draw(Batch batch, float parentAlpha) { comprobarColisiones();

batch.draw(jugador, getX(), getY()); stage.draw(); //Dibujar los actores

}
}

private void comprobarColisiones() {


public boolean isAlive() { if (jugador.isAlive() && jugador.getX()+jugador.getWidth()> piedra.getX()) {
System.out.println("Colisión: Jugador aplastado");
return alive; jugador.setAlive(false);
} }
}

@Override
public void setAlive(boolean alive) { public void dispose() {
texturaJugador.dispose();
this.alive = alive;
}
}
}

}
Colisiones - Box2D

No es la mejor forma de hacer colisiones, pero para juegos
sencillos en 2D es una técnica aceptable.

Es mejor usar Box2D
– Solo sirve en físicas 2D, es una limitación.
– Vamos a crear una nueva pantalla vacía: Box2DScreen que
extienda de BaseScreen, con los métodos

show

dispose

Render (limpiamos la pantalla)
– Vamos a apuntar MyGdxGame (create) a Box2DScreen
Box2D
package com.mygdx.game;
package com.mygdx.game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.Game;
public class Box2DScreen extends BaseScreen {

public Box2DScreen(MyGdxGame game) { //ApplicationAdapter applicationAdapter por game


super(game);
} public class MyGdxGame extends Game {

@Override
public void show() { @Override
}
public void create () {
@Override setScreen(new Box2DScreen(this));
public void dispose() {
}
}

@Override
}
public void render(float delta) {
//limpiar la pantalla
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

}
}
Box2D

Vamos a crear un mundo World
– private World world;

Ahora lo instanciamos en el show():
– Necesitamos: world = new World(new Vector2(0, -10),
true);

Gravedad (-9,81 en el eje y, redondeamos a -10)
– Podemos poner la que queramos y cómo nos queramos.

doSleep= Si no hay nada que simular, que no simule nada.
– Hay que hacer dispose de world.
public class Box2DScreen extends BaseScreen {

World public Box2DScreen(MyGdxGame game) {


super(game);
}

private World world;

@Override
public void show() {
world = new World(new Vector2(0, -10), true);
}

@Override
public void dispose() {
world.dispose();

@Override
public void render(float delta) {
//limpiar la pantalla
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

}
}
World

Ya tenemos el mundo, ahora hay que simularlo (en el render):
– world.step(delta, 6, 2)

6 y 2 porque lo dice la documentación (no está muy claro)
– Cuanto mayores son estos valores, más CPU consume.
@Override
public void render(float delta) {
//limpiar la pantalla
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
world.step(delta, 6, 2);
}
World

Nuestro mundo está vacío… no tiene nada

Hay que dibujar nuestro mundo, para ello usamos
Box2DDebugRenderer. Creamos el atributo en
nuestra Box2DScreen, lo inicializamos en el show y
hacemos su dispose.
private Box2DDebugRenderer renderer;
renderer = new Box2DDebugRenderer();
renderer.dispose();
Dibujar el mundo

Siempre primero actualizar las físicas

Después renderizar

World

Cámaras (que generan matrices de proyección). Crearemos
un nuevo atributo con la cámara.

Las cámaras Orthographic son ideales para 2d.

Las instanciaremos con las dimensiones de la pantalla:
camera = new OrthographicCamera(Gdx.graphics.getWidth(),
Gdx.graphics.getHeight());
package com.mygdx.game;

Box2D
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.World;

public class Box2DScreen extends BaseScreen {

public Box2DScreen(MyGdxGame game) {


super(game);
}

private World world;


private Box2DDebugRenderer renderer;

private OrthographicCamera camera;

@Override
public void show() {
world = new World(new Vector2(0, -10), true);
renderer = new Box2DDebugRenderer();
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
}

@Override
public void dispose() {
world.dispose();
renderer.dispose();

@Override
public void render(float delta) {
//limpiar la pantalla
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

world.step(delta, 6, 2);

//La cámara tiene que actualizarse.


camera.update();
renderer.render(world, camera.combined);

}
}
Box2D
Vamos a crear personales. Para ello necesitamos:
– Body: Representan entidades de nuestro mundo
(posición, velocidad…). Representan formas
– Fixture: Nos permite definir la forma que va a tener un
body.
private Body jugadorBody;
private Fixture jugadorFixture;
Body

Para crear un Body necesitamos utilizar el método
createBody del mundo (world). Y éste nos va a
pedir un BodyDef.
– Le damos posición
– Tipo

Static Body – Cuerpos que no se van a mover

Dynamic Body – cuerpos que se van a mover.
Nota: hay que hacer dispose del body.
public class Box2DScreen extends BaseScreen {

Body
public Box2DScreen(MyGdxGame game) {
super(game);
}

private World world;


private Box2DDebugRenderer renderer;

private OrthographicCamera camera;

private Body jugadorBody;


private Fixture jugadorFixture;

@Override
public void show() {
world = new World(new Vector2(0, -10), true);
renderer = new Box2DDebugRenderer();
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());

BodyDef jugadorDef = createJugadorBodyDef();


world.createBody(jugadorDef);
}

private BodyDef createJugadorBodyDef() {


BodyDef def = new BodyDef();
def.position.set(0, 10);
def.type = BodyDef.BodyType.DynamicBody;
return def;
}

@Override
public void dispose() {
world.destroyBody(jugadorBody);
world.dispose();
renderer.dispose();

@Override
public void render(float delta) {
//limpiar la pantalla
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

world.step(delta, 6, 2);

//La cámara tiene que actualizarse.


camera.update();
renderer.render(world, camera.combined);

}
}
Fixture

Necesitamos la Fixture para darle una forma.

Le indicamos la forma que va a tener
– Por ejemplo poligonal.

Le damos un tamaño, ojo que viene en metros.
– setAsBox(1,1);

Una vez usada, tenemos que hacer un dispose

Se va a ver algo muy pequeño, ya que tenemos una
fixture pequeña (1,1) (1 m²) para una pantalla tan grande.
Fixture
@Override
public void show() {
world = new World(new Vector2(0, -10), true);
renderer = new Box2DDebugRenderer();
camera = new OrthographicCamera(Gdx.graphics.getWidth(),
Gdx.graphics.getHeight());

BodyDef jugadorDef = createJugadorBodyDef();


jugadorBody = world.createBody(jugadorDef);

PolygonShape jugadorShape = new PolygonShape();


jugadorShape.setAsBox(1, 1);
jugadorFixture = jugadorBody.createFixture(jugadorShape,1);
jugadorShape.dispose();
}
OrthographicCamera

Antes

360px

640 px x
16∗4
x= =7,11
9

16:9 4m

1m

OrthographicCamera
OrthographicCamera

Cambiamos la definición
– camera = new
OrthographicCamera(Gdx.graphics.getWidth(),
Gdx.graphics.getHeight());
a
– Vamos a usar 32/18 (16:9)
camera = new OrthographicCamera(32, 18);
¿Qué ocurre al ejecutar?
package com.mygdx.game;

import com.badlogic.gdx.Gdx;

OrthographicCamera
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;

public class Box2DScreen extends BaseScreen {

public Box2DScreen(MyGdxGame game) {


super(game);
}

private World world;


private Box2DDebugRenderer renderer;

private OrthographicCamera camera;

private Body jugadorBody;


private Fixture jugadorFixture;

@Override
public void show() {
world = new World(new Vector2(0, -10), true);
renderer = new Box2DDebugRenderer();
camera = new OrthographicCamera(32, 18);

BodyDef jugadorDef = createJugadorBodyDef();


jugadorBody = world.createBody(jugadorDef);

PolygonShape jugadorShape = new PolygonShape();


jugadorShape.setAsBox(1, 1);
jugadorFixture = jugadorBody.createFixture(jugadorShape,1);
jugadorShape.dispose();
}

private BodyDef createJugadorBodyDef() {


BodyDef def = new BodyDef();
def.position.set(0, 10);
def.type = BodyDef.BodyType.DynamicBody;
return def;
}

@Override
public void dispose() {
jugadorBody.destroyFixture(jugadorFixture);
world.destroyBody(jugadorBody);
world.dispose();
renderer.dispose();

@Override
public void render(float delta) {
//limpiar la pantalla
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

world.step(delta, 6, 2);

//La cámara tiene que actualizarse.


camera.update();
renderer.render(world, camera.combined);

}
}
Crear Suelo

BodyDef.position.set(x, y):

La posición que le
damos define el centro
de gravedad
Crear Suelo

PoligonShape.setAsBoxt(x, y):

2y x

2x

jugadorShape.setAsBox(1, 1); jugadorShape.setAsBox(0.5f, 0.5f);


Crear Suelo

Condiciones del suelo
– sueloShape.setAsBox(500, 1); // 1km x 2m
– BodyDef.position.set(0,-1):
1km(2x)

2m
Position: (2y)
X =0
Y = -1

El borde superior del suelo, estará en y =0,


donde estarán el resto de elementos
Suelo

y=0
Crear el Suelo

Se implementa igual que el jugador
– Show:
sueloBody = world.createBody(createSueloBodyDef());
PolygonShape sueloShape = new PolygonShape();
sueloShape.setAsBox(500, 1); // 1km x 2m
sueloFixture = sueloBody.createFixture(sueloShape,1);
sueloShape.dispose();

– Ahora será estática su definición (no se va a mover)


private BodyDef createSueloBodyDef() {
BodyDef def = new BodyDef();
def.position.set(0, -1);
def.type = BodyDef.BodyType.StaticBody;
return def;
}
Crear el suelo

Hemos creado un cuerpo y una Fixture y si
ejecutamos, cuando llega al suelo se para.
package com.mygdx.game;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;

Crear Suelo
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;

public class Box2DScreen extends BaseScreen {

public Box2DScreen(MyGdxGame game) {


super(game);
}

private World world;


private Box2DDebugRenderer renderer;

private OrthographicCamera camera;

private Body jugadorBody;


private Fixture jugadorFixture;
private Fixture sueloFixture;

private Body sueloBody;

@Override
public void show() {
world = new World(new Vector2(0, -10), true);
renderer = new Box2DDebugRenderer();
camera = new OrthographicCamera(32, 18);

BodyDef jugadorDef = createJugadorBodyDef();


jugadorBody = world.createBody(jugadorDef);

//Creamos el suelo
sueloBody = world.createBody(createSueloBodyDef());

PolygonShape jugadorShape = new PolygonShape();


jugadorShape.setAsBox(0.5f, 0.5f); // se multiplica por 2 -> 1m x 1m
jugadorFixture = jugadorBody.createFixture(jugadorShape,1);
jugadorShape.dispose();

PolygonShape sueloShape = new PolygonShape();


sueloShape.setAsBox(500, 1); // 1km x 2m
sueloFixture = sueloBody.createFixture(sueloShape,1);
sueloShape.dispose();
}

private BodyDef createJugadorBodyDef() {


BodyDef def = new BodyDef();
def.position.set(0, 10);
def.type = BodyDef.BodyType.DynamicBody;
return def;
}

private BodyDef createSueloBodyDef() {


BodyDef def = new BodyDef();
def.position.set(0, -1);
def.type = BodyDef.BodyType.StaticBody;
return def;
}

@Override
public void dispose() {
jugadorBody.destroyFixture(jugadorFixture);
sueloBody.destroyFixture(sueloFixture);

world.destroyBody(jugadorBody);
world.destroyBody(sueloBody);
world.dispose();
renderer.dispose();

@Override
public void render(float delta) {
//limpiar la pantalla
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

world.step(delta, 6, 2);

//La cámara tiene que actualizarse.


camera.update();
renderer.render(world, camera.combined);

}
}
Crear Suelo

Ya se para la caída y podríamos
obtener la colisión que se produce
entre el objeto y el suelo.

Hay que retocar la cámara, está muy
lejos.
– Para ello vamos a darle las dimensiones
que tendría que tener (zoom)
camera = new OrthographicCamera(7.11f, 4);
– Y vamos a mover la cámara un poco hacia
arriba para que el suelo baje (la movemos
una posición 1m hacia arriba).
camera.translate(0,1);
package com.mygdx.game;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;

Crear Suelo
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;

public class Box2DScreen extends BaseScreen {

public Box2DScreen(MyGdxGame game) {


super(game);
}

private World world;


private Box2DDebugRenderer renderer;

private OrthographicCamera camera;

private Body jugadorBody;


private Fixture jugadorFixture;
private Fixture sueloFixture;

private Body sueloBody;

@Override
public void show() {
world = new World(new Vector2(0, -10), true);
renderer = new Box2DDebugRenderer();
camera = new OrthographicCamera(7.11f, 4);
camera.translate(0,1);

BodyDef jugadorDef = createJugadorBodyDef();


jugadorBody = world.createBody(jugadorDef);

//Creamos el suelo
sueloBody = world.createBody(createSueloBodyDef());

PolygonShape jugadorShape = new PolygonShape();


jugadorShape.setAsBox(0.5f, 0.5f); // se multiplica por 2 -> 1m x 1m
jugadorFixture = jugadorBody.createFixture(jugadorShape,1);
jugadorShape.dispose();

PolygonShape sueloShape = new PolygonShape();


sueloShape.setAsBox(500, 1); // 1km x 2m
sueloFixture = sueloBody.createFixture(sueloShape,1);
sueloShape.dispose();
}

private BodyDef createJugadorBodyDef() {


BodyDef def = new BodyDef();
def.position.set(0, 10);
def.type = BodyDef.BodyType.DynamicBody;
return def;
}

private BodyDef createSueloBodyDef() {


BodyDef def = new BodyDef();
def.position.set(0, -1);
def.type = BodyDef.BodyType.StaticBody;
return def;
}

@Override
public void dispose() {
jugadorBody.destroyFixture(jugadorFixture);
sueloBody.destroyFixture(sueloFixture);

world.destroyBody(jugadorBody);
world.destroyBody(sueloBody);
world.dispose();
renderer.dispose();

@Override
public void render(float delta) {
//limpiar la pantalla
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

world.step(delta, 6, 2);

//La cámara tiene que actualizarse.


camera.update();
renderer.render(world, camera.combined);

}
}
Crear Fixtures – Si no son cajas

Vamos a crear una fixture para las rocas, y vamos a hacer que sean
triangulares.

A diferencia de los métodos anteriores de BodyDef, vamos a pasarle un
parámetro para poder construirlo con tantas rocas como queramos.

El tamaño: 1x 1, por lo que posicionaremos como (x, 0.5f)
private BodyDef createRocaBodyDef(float x) {
BodyDef def = new BodyDef();
def.position.set(x, 0.5f);
def.type = BodyDef.BodyType.StaticBody;
return def;
}
Crear Fixtures – Si no son cajas

Esta fixture será más compleja, en lugar de crearla
en el show vamos a crear un método que la cree.

¿Y como lo construyo? Tengo que usar vértices

Y=0.5

1m
Y=0

Y=-0.5

X-0.5 X X+0.5
1m
Crear Fixtures – Si no son cajas

Vamos a darle las posiciones de los vértices de
forma contraria a las agujas del reloj.
C
Y=0.5
A = (-0.5, -0.5)
B = (0.5, -0.5)
C = (0,0.5)
Y=0

A B
Y=-0.5

X-0.5 X X+0.5
Crear Fixtures – Si no son cajas
private Fixture crearRocaFixture(Body rocaBody) {
Vector2[] vertices = new Vector2[3];
vertices[0] = new Vector2(-0.5f, -0.5f);
vertices[1] = new Vector2(0.5f, -0.5f);
vertices[2] = new Vector2(0f, 0.5f);

PolygonShape shape = new PolygonShape();


shape.set(vertices);
Fixture fix = rocaBody.createFixture(shape,1);
return fix;
}
package com.mygdx.game;

Crear Fixtures –
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;

Si no son cajas
public class Box2DScreen extends BaseScreen {

public Box2DScreen(MyGdxGame game) {


super(game);
}

private World world;


private Box2DDebugRenderer renderer;

private OrthographicCamera camera;

private Body jugadorBody;


private Fixture jugadorFixture;
private Fixture sueloFixture;

private Body sueloBody;

private Body rocaBody;


private Fixture rocaFixture;

@Override
public void show() {
world = new World(new Vector2(0, -10), true);
renderer = new Box2DDebugRenderer();
camera = new OrthographicCamera(7.11f, 4);
camera.translate(0,1);

BodyDef jugadorDef = createJugadorBodyDef();


jugadorBody = world.createBody(jugadorDef);

//Creamos el suelo
sueloBody = world.createBody(createSueloBodyDef());

rocaBody = world.createBody(createRocaBodyDef(1));

PolygonShape jugadorShape = new PolygonShape();


jugadorShape.setAsBox(0.5f, 0.5f); // se multiplica por 2 -> 1m x 1m
jugadorFixture = jugadorBody.createFixture(jugadorShape,1);
jugadorShape.dispose();

PolygonShape sueloShape = new PolygonShape();


sueloShape.setAsBox(500, 1); // 1km x 2m
sueloFixture = sueloBody.createFixture(sueloShape,1);
sueloShape.dispose();

rocaFixture = crearRocaFixture(rocaBody);
}

private BodyDef createJugadorBodyDef() {


BodyDef def = new BodyDef();
def.position.set(0, 10);
def.type = BodyDef.BodyType.DynamicBody;
return def;
}

private BodyDef createSueloBodyDef() {


BodyDef def = new BodyDef();
def.position.set(0, -1);
def.type = BodyDef.BodyType.StaticBody;
return def;
}

private BodyDef createRocaBodyDef(float x) {


BodyDef def = new BodyDef();
def.position.set(x, 0.5f);
def.type = BodyDef.BodyType.StaticBody;
return def;
}

private Fixture crearRocaFixture(Body rocaBody) {


Vector2[] vertices = new Vector2[3];
vertices[0] = new Vector2(-0.5f, -0.5f);
vertices[1] = new Vector2(0.5f, -0.5f);
vertices[2] = new Vector2(0f, 0.5f);

PolygonShape shape = new PolygonShape();


shape.set(vertices);
Fixture fix = rocaBody.createFixture(shape,1);
return fix;
}

@Override
public void dispose() {
jugadorBody.destroyFixture(jugadorFixture);
sueloBody.destroyFixture(sueloFixture);
rocaBody.destroyFixture(rocaFixture);

world.destroyBody(jugadorBody);
world.destroyBody(sueloBody);
world.destroyBody(rocaBody);
world.dispose();
renderer.dispose();

@Override
public void render(float delta) {
//limpiar la pantalla
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

world.step(delta, 6, 2);

//La cámara tiene que actualizarse.


camera.update();
renderer.render(world, camera.combined);

}
}
Crear Fixtures –
Si no son cajas

¿Qué ocurre si cambio la definición del
createRocaBodyDef(1) a 0.5f?
Saltos

Tipos
– Fuerzas reales: variación de la velocidad sobre un cuerpo de
forma progresiva.
– Impulsos: cambios de la velocidad de golpe.
Los métodos van a permitir utilizar variables de tipo float o
Vectores. Ojo, si estamos en el render (que se ejecuta entre
40-60 veces) e instanciamos vectores, el recolector de
basura de java va a echar humo.
– Tendremos 2 parámetros normalmente:

Force: Qué fuerza vamos a aplicar

Point: En qué punto la vamos a aplicar (en el centro, en un borde, etc)

Wake: Para que active objetos que están en reposo, y que se dejan en
esta circunstancia para que no consuman CPU.
Salto

Impulso Lineal:
– Que no se mueva horizontalmente
– Que se mueva verticalmente
– Que despierte al actor
– Y que su body, nos de la posición.
20
private void saltar() {
Vector2 position = jugadorBody.getPosition();
jugadorBody.applyLinearImpulse(0, 20, position.x, position.y, true);

}
Salto

Para probarlo vamos a ejecutar el método saltar
cuando el usuario pulse la barra espaciadora.
– Lo vamos a meter en el render para ejecutarlo, pero ésta
no es una buena solución.
@Override
public void render(float delta) {
//limpiar la pantalla
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

if (Gdx.input.isKeyJustPressed(Keys.SPACE)) {
saltar();
}

world.step(delta, 6, 2);

//La cámara tiene que actualizarse.


camera.update();
renderer.render(world, camera.combined);
package com.mygdx.game;

import java.awt.RenderingHints.Key;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;

Salto
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;

public class Box2DScreen extends BaseScreen {

public Box2DScreen(MyGdxGame game) {


super(game);
}

private World world;


private Box2DDebugRenderer renderer;

private OrthographicCamera camera;

private Body jugadorBody;


private Fixture jugadorFixture;
private Fixture sueloFixture;

private Body sueloBody;

private Body rocaBody;


private Fixture rocaFixture;

@Override
public void show() {
world = new World(new Vector2(0, -10), true);
renderer = new Box2DDebugRenderer();
camera = new OrthographicCamera(7.11f, 4);
camera.translate(2,1);

BodyDef jugadorDef = createJugadorBodyDef();


jugadorBody = world.createBody(jugadorDef);

//Creamos el suelo
sueloBody = world.createBody(createSueloBodyDef());

rocaBody = world.createBody(createRocaBodyDef(1));

PolygonShape jugadorShape = new PolygonShape();


jugadorShape.setAsBox(0.5f, 0.5f); // se multiplica por 2 -> 1m x 1m
jugadorFixture = jugadorBody.createFixture(jugadorShape,1);
jugadorShape.dispose();

PolygonShape sueloShape = new PolygonShape();


sueloShape.setAsBox(500, 1); // 1km x 2m
sueloFixture = sueloBody.createFixture(sueloShape,1);
sueloShape.dispose();

rocaFixture = crearRocaFixture(rocaBody);
}

private BodyDef createJugadorBodyDef() {


BodyDef def = new BodyDef();
def.position.set(0, 10);
def.type = BodyDef.BodyType.DynamicBody;
return def;
}

private BodyDef createSueloBodyDef() {


BodyDef def = new BodyDef();
def.position.set(0, -1);
def.type = BodyDef.BodyType.StaticBody;
return def;
}

private BodyDef createRocaBodyDef(float x) {


BodyDef def = new BodyDef();
def.position.set(x, 0.5f);
def.type = BodyDef.BodyType.StaticBody;
return def;
}

private Fixture crearRocaFixture(Body rocaBody) {


Vector2[] vertices = new Vector2[3];
vertices[0] = new Vector2(-0.5f, -0.5f);
vertices[1] = new Vector2(0.5f, -0.5f);
vertices[2] = new Vector2(0f, 0.5f);

PolygonShape shape = new PolygonShape();


shape.set(vertices);
Fixture fix = rocaBody.createFixture(shape,1);
return fix;
}

@Override
public void dispose() {
jugadorBody.destroyFixture(jugadorFixture);
sueloBody.destroyFixture(sueloFixture);
rocaBody.destroyFixture(rocaFixture);

world.destroyBody(jugadorBody);
world.destroyBody(sueloBody);
world.destroyBody(rocaBody);
world.dispose();
renderer.dispose();

@Override
public void render(float delta) {
//limpiar la pantalla
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

if (Gdx.input.isKeyJustPressed(Keys.SPACE)) {
saltar();
}

world.step(delta, 6, 2);

//La cámara tiene que actualizarse.


camera.update();
renderer.render(world, camera.combined);

private void saltar() {


Vector2 position = jugadorBody.getPosition();
jugadorBody.applyLinearImpulse(0, 20, position.x, position.y, true);

}
}
Controlar colisiones

En Box2d tenemos lo que se conoce como
“contactos”. Cuando dos fixtures chocan se produce
un contacto.
– En world podemos establecer ContactListener, para que
escuche cuando se produzcan esos contactos.

BeginContact – detectar cuando se produce una colisión

EndContact - detectar cuando se deja de producir una colisión.

XxxSolve – Se ejecutan cuando las físicas detectan un contacto.
Este nos nos interesa salvo para acciones avanzadas.
Contactos

El código no quedará muy limpio, ya que la
colisión del jugador y del suelo es la misma que la
del suelo con el jugador. Hay que contemplarlo
todo.
– Dentro del contacto no podemos llamar a las acciones,
por ejemplo que salte el jugador, el renderizador no está
disponible.
– Hay que decirle a Box2D que cuando pueda, que salte.
¿Cómo lo hacemos? con un atributo “haColisionado”
Contactos
//Colisiones
world.setContactListener(new ContactListener() {

@Override
public void beginContact(Contact contact) {
Fixture fixtureA = contact.getFixtureA(), fixtureB = contact.getFixtureB();
if(fixtureA == jugadorFixture && fixtureB == sueloFixture) {
haColisionado = true;
}

if(fixtureB == jugadorFixture && fixtureA == sueloFixture) {


haColisionado = true;
}
}
[…]
@Override
public void render(float delta) {
//limpiar la pantalla
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

if (Gdx.input.justTouched() || haColisionado) {
haColisionado = false;
saltar();
}
Contactos

Esta forma de hacer los contactos y meter atributos
para decirle a renderizador que haga cosas, es muy
muy poco limpio.
– Se debe hacer con entidades que recojan estas
posibilidades.
– Lo haremos más adelante.
package com.mygdx.game;

import java.awt.RenderingHints.Key;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;

Colisiones
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.ContactImpulse;
import com.badlogic.gdx.physics.box2d.ContactListener;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.Manifold;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;

public class Box2DScreen extends BaseScreen {

public Box2DScreen(MyGdxGame game) {


super(game);
}

private World world;


private Box2DDebugRenderer renderer;

private OrthographicCamera camera;

private Body jugadorBody;


private Fixture jugadorFixture;
private Fixture sueloFixture;

private Body sueloBody;

private Body rocaBody;


private Fixture rocaFixture;

private boolean haColisionado;

@Override
public void show() {
world = new World(new Vector2(0, -10), true);
renderer = new Box2DDebugRenderer();
camera = new OrthographicCamera(7.11f, 4);
camera.translate(0,1);

//Colisiones
world.setContactListener(new ContactListener() {

@Override
public void beginContact(Contact contact) {
Fixture fixtureA = contact.getFixtureA(), fixtureB = contact.getFixtureB();
if(fixtureA == jugadorFixture && fixtureB == sueloFixture) {
haColisionado = true;
}

if(fixtureB == jugadorFixture && fixtureA == sueloFixture) {


haColisionado = true;
}
}

@Override
public void preSolve(Contact contact, Manifold oldManifold) {
// TODO Auto-generated method stub

@Override
public void postSolve(Contact contact, ContactImpulse impulse) {
// TODO Auto-generated method stub

@Override
public void endContact(Contact contact) {
// TODO Auto-generated method stub

});

BodyDef jugadorDef = createJugadorBodyDef();


jugadorBody = world.createBody(jugadorDef);

//Creamos el suelo
sueloBody = world.createBody(createSueloBodyDef());

rocaBody = world.createBody(createRocaBodyDef(4f));

PolygonShape jugadorShape = new PolygonShape();


jugadorShape.setAsBox(0.5f, 0.5f); // se multiplica por 2 -> 1m x 1m
jugadorFixture = jugadorBody.createFixture(jugadorShape,1);
jugadorShape.dispose();

PolygonShape sueloShape = new PolygonShape();


sueloShape.setAsBox(500, 1); // 1km x 2m
sueloFixture = sueloBody.createFixture(sueloShape,1);
sueloShape.dispose();

rocaFixture = crearRocaFixture(rocaBody);
}

private BodyDef createJugadorBodyDef() {


BodyDef def = new BodyDef();
def.position.set(0, 10);
def.type = BodyDef.BodyType.DynamicBody;
return def;
}

private BodyDef createSueloBodyDef() {


BodyDef def = new BodyDef();
def.position.set(0, -1);
def.type = BodyDef.BodyType.StaticBody;
return def;
}

private BodyDef createRocaBodyDef(float x) {


BodyDef def = new BodyDef();
def.position.set(x, 0.5f);
def.type = BodyDef.BodyType.StaticBody;
return def;
}

private Fixture crearRocaFixture(Body rocaBody) {


Vector2[] vertices = new Vector2[3];
vertices[0] = new Vector2(-0.5f, -0.5f);
vertices[1] = new Vector2(0.5f, -0.5f);
vertices[2] = new Vector2(0f, 0.5f);

PolygonShape shape = new PolygonShape();


shape.set(vertices);
Fixture fix = rocaBody.createFixture(shape,1);
return fix;
}

@Override
public void dispose() {
jugadorBody.destroyFixture(jugadorFixture);
sueloBody.destroyFixture(sueloFixture);
rocaBody.destroyFixture(rocaFixture);

world.destroyBody(jugadorBody);
world.destroyBody(sueloBody);
world.destroyBody(rocaBody);
world.dispose();
renderer.dispose();

@Override
public void render(float delta) {
//limpiar la pantalla
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

if (Gdx.input.justTouched() || haColisionado) {
haColisionado = false;
saltar();
}

world.step(delta, 6, 2);

//La cámara tiene que actualizarse.


camera.update();
renderer.render(world, camera.combined);

private void saltar() {


Vector2 position = jugadorBody.getPosition();
jugadorBody.applyLinearImpulse(0, 5, position.x, position.y, true);

}
}
Sistema de saltos

Cuando el jugador toque la pantalla, el usuario
debe saltar.

Luego, si cuando el jugador choca con el suelo y la
pantalla sigue pulsada, el jugador tiene que volver
a saltar. private boolean debeSaltar, jugadorSaltando;

@Override
public void render(float delta) {
//limpiar la pantalla
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

if (debeSaltar) {
debeSaltar = false;
saltar();
}

if(Gdx.input.justTouched() && !jugadorSaltando) {


debeSaltar =true;
}

world.step(delta, 6, 2);

//La cámara tiene que actualizarse.


camera.update();
renderer.render(world, camera.combined);

}
Sistema de saltos

Cuando se inicia una colisión entre el suelo y el
jugador → NO está saltando. Y cuando acaba, es
porque está saltando.
//Colisiones
world.setContactListener(new ContactListener() {

@Override
public void beginContact(Contact contact) {
Fixture fixtureA = contact.getFixtureA(), fixtureB = contact.getFixtureB();
if(fixtureA == jugadorFixture && fixtureB == sueloFixture) {
jugadorSaltando = false;
}

if(fixtureB == jugadorFixture && fixtureA == sueloFixture) {


jugadorSaltando = false;
}
}

@Override
public void endContact(Contact contact) {
Fixture fixtureA = contact.getFixtureA(), fixtureB = contact.getFixtureB();
if(fixtureA == jugadorFixture && fixtureB == sueloFixture) {
jugadorSaltando = true;
}

if(fixtureB == jugadorFixture && fixtureA == sueloFixture) {


jugadorSaltando = true;
}

}
package com.mygdx.game;

import java.awt.RenderingHints.Key;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.ContactImpulse;
import com.badlogic.gdx.physics.box2d.ContactListener;
import com.badlogic.gdx.physics.box2d.Fixture;

Sistema de Saltos
import com.badlogic.gdx.physics.box2d.Manifold;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;

public class Box2DScreen extends BaseScreen {

public Box2DScreen(MyGdxGame game) {


super(game);
}

private World world;


private Box2DDebugRenderer renderer;

private OrthographicCamera camera;

private Body jugadorBody;


private Fixture jugadorFixture;
private Fixture sueloFixture;

private Body sueloBody;

private Body rocaBody;


private Fixture rocaFixture;

private boolean debeSaltar, jugadorSaltando = false;

@Override
public void show() {
world = new World(new Vector2(0, -10), true);
renderer = new Box2DDebugRenderer();
camera = new OrthographicCamera(7.11f, 4);
camera.translate(0,1);

Ahora el jugador ya no
//Colisiones
world.setContactListener(new ContactListener() {

@Override

● public void beginContact(Contact contact) {


Fixture fixtureA = contact.getFixtureA(), fixtureB = contact.getFixtureB();
if(fixtureA == jugadorFixture && fixtureB == sueloFixture) {

}
jugadorSaltando = false;

if(fixtureB == jugadorFixture && fixtureA == sueloFixture) {


jugadorSaltando = false;

vuelve a saltar hasta


}
}

@Override
public void endContact(Contact contact) {
Fixture fixtureA = contact.getFixtureA(), fixtureB = contact.getFixtureB();
if(fixtureA == jugadorFixture && fixtureB == sueloFixture) {
jugadorSaltando = true;
}

if(fixtureB == jugadorFixture && fixtureA == sueloFixture) {


jugadorSaltando = true;

que no toca el suelo.


}

@Override
public void preSolve(Contact contact, Manifold oldManifold) {
// TODO Auto-generated method stub

@Override
public void postSolve(Contact contact, ContactImpulse impulse) {
// TODO Auto-generated method stub

No se permiten saltos
});

– BodyDef jugadorDef = createJugadorBodyDef();


jugadorBody = world.createBody(jugadorDef);

//Creamos el suelo
sueloBody = world.createBody(createSueloBodyDef());

rocaBody = world.createBody(createRocaBodyDef(4f));

en el aire.
PolygonShape jugadorShape = new PolygonShape();
jugadorShape.setAsBox(0.5f, 0.5f); // se multiplica por 2 -> 1m x 1m
jugadorFixture = jugadorBody.createFixture(jugadorShape,1);
jugadorShape.dispose();

PolygonShape sueloShape = new PolygonShape();


sueloShape.setAsBox(500, 1); // 1km x 2m
sueloFixture = sueloBody.createFixture(sueloShape,1);
sueloShape.dispose();

rocaFixture = crearRocaFixture(rocaBody);
}

private BodyDef createJugadorBodyDef() {


BodyDef def = new BodyDef();
def.position.set(0, 10);
def.type = BodyDef.BodyType.DynamicBody;
return def;
}

private BodyDef createSueloBodyDef() {


BodyDef def = new BodyDef();
def.position.set(0, -1);
def.type = BodyDef.BodyType.StaticBody;
return def;
}

private BodyDef createRocaBodyDef(float x) {


BodyDef def = new BodyDef();
def.position.set(x, 0.5f);
def.type = BodyDef.BodyType.StaticBody;
return def;
}

private Fixture crearRocaFixture(Body rocaBody) {


Vector2[] vertices = new Vector2[3];
vertices[0] = new Vector2(-0.5f, -0.5f);
vertices[1] = new Vector2(0.5f, -0.5f);
vertices[2] = new Vector2(0f, 0.5f);

PolygonShape shape = new PolygonShape();


shape.set(vertices);
Fixture fix = rocaBody.createFixture(shape,1);
return fix;
}

@Override
public void dispose() {
jugadorBody.destroyFixture(jugadorFixture);
sueloBody.destroyFixture(sueloFixture);
rocaBody.destroyFixture(rocaFixture);

world.destroyBody(jugadorBody);
world.destroyBody(sueloBody);
world.destroyBody(rocaBody);
world.dispose();
renderer.dispose();

@Override
public void render(float delta) {
//limpiar la pantalla
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

if (debeSaltar) {
debeSaltar = false;
saltar();
}

if(Gdx.input.justTouched() && !jugadorSaltando) {


debeSaltar =true;
}

world.step(delta, 6, 2);

//La cámara tiene que actualizarse.


camera.update();
renderer.render(world, camera.combined);

}
void saltar() {
Vector2 position = jugadorBody.getPosition();
jugadorBody.applyLinearImpulse(0, 5, position.x, position.y, true);

}
}
Sistema de saltos

¿Y si quiero que mientras esté pulsada la pantalla
el jugador siga saltando después de tocar el suelo?
world.setContactListener(new ContactListener() {

@Override
public void beginContact(Contact contact) {
Fixture fixtureA = contact.getFixtureA(), fixtureB = contact.getFixtureB();
if(fixtureA == jugadorFixture && fixtureB == sueloFixture) {
jugadorSaltando = false;
if (Gdx.input.isTouched()) {
debeSaltar = true;
}
}

if(fixtureB == jugadorFixture && fixtureA == sueloFixture) {


jugadorSaltando = false;
if (Gdx.input.isTouched()) {
debeSaltar = true;
}
}
}
package com.mygdx.game;

import java.awt.RenderingHints.Key;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;

Sistema de Saltos
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.ContactImpulse;
import com.badlogic.gdx.physics.box2d.ContactListener;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.Manifold;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;

public class Box2DScreen extends BaseScreen {

public Box2DScreen(MyGdxGame game) {


super(game);
}

private World world;


private Box2DDebugRenderer renderer;

private OrthographicCamera camera;

private Body jugadorBody;


private Fixture jugadorFixture;
private Fixture sueloFixture;

private Body sueloBody;

private Body rocaBody;


private Fixture rocaFixture;

private boolean debeSaltar, jugadorSaltando = false;

@Override
public void show() {

La forma de trabajar
world = new World(new Vector2(0, -10), true);
renderer = new Box2DDebugRenderer();
camera = new OrthographicCamera(7.11f, 4);
camera.translate(0,1);

● //Colisiones
world.setContactListener(new ContactListener() {

@Override
public void beginContact(Contact contact) {
Fixture fixtureA = contact.getFixtureA(), fixtureB = contact.getFixtureB();
if(fixtureA == jugadorFixture && fixtureB == sueloFixture) {

sería así, pero el código


jugadorSaltando = false;
if (Gdx.input.isTouched()) {
debeSaltar = true;
}
}

if(fixtureB == jugadorFixture && fixtureA == sueloFixture) {


jugadorSaltando = false;
if (Gdx.input.isTouched()) {
debeSaltar = true;
}
}

cada vez queda peor


}

@Override
public void endContact(Contact contact) {
Fixture fixtureA = contact.getFixtureA(), fixtureB = contact.getFixtureB();
if(fixtureA == jugadorFixture && fixtureB == sueloFixture) {
jugadorSaltando = true;
}

if(fixtureB == jugadorFixture && fixtureA == sueloFixture) {


jugadorSaltando = true;
}

@Override
public void preSolve(Contact contact, Manifold oldManifold) {

Muchas variables
// TODO Auto-generated method stub


}

@Override
public void postSolve(Contact contact, ContactImpulse impulse) {
// TODO Auto-generated method stub

});

BodyDef jugadorDef = createJugadorBodyDef();

Código duplicado, etc.


jugadorBody = world.createBody(jugadorDef);

– //Creamos el suelo
sueloBody = world.createBody(createSueloBodyDef());

rocaBody = world.createBody(createRocaBodyDef(4f));

PolygonShape jugadorShape = new PolygonShape();


jugadorShape.setAsBox(0.5f, 0.5f); // se multiplica por 2 -> 1m x 1m
jugadorFixture = jugadorBody.createFixture(jugadorShape,1);
jugadorShape.dispose();

PolygonShape sueloShape = new PolygonShape();

Aunque el código es
sueloShape.setAsBox(500, 1); // 1km x 2m
sueloFixture = sueloBody.createFixture(sueloShape,1);
sueloShape.dispose();

● }
rocaFixture = crearRocaFixture(rocaBody);

private BodyDef createJugadorBodyDef() {


BodyDef def = new BodyDef();
def.position.set(0, 10);
def.type = BodyDef.BodyType.DynamicBody;
return def;

manifiestamente
}

private BodyDef createSueloBodyDef() {


BodyDef def = new BodyDef();
def.position.set(0, -1);
def.type = BodyDef.BodyType.StaticBody;
return def;
}

private BodyDef createRocaBodyDef(float x) {


BodyDef def = new BodyDef();

mejorable, la forma de
def.position.set(x, 0.5f);
def.type = BodyDef.BodyType.StaticBody;
return def;
}

private Fixture crearRocaFixture(Body rocaBody) {


Vector2[] vertices = new Vector2[3];
vertices[0] = new Vector2(-0.5f, -0.5f);
vertices[1] = new Vector2(0.5f, -0.5f);
vertices[2] = new Vector2(0f, 0.5f);

PolygonShape shape = new PolygonShape();

trabajar es ésta.
shape.set(vertices);
Fixture fix = rocaBody.createFixture(shape,1);
return fix;
}

@Override
public void dispose() {
jugadorBody.destroyFixture(jugadorFixture);
sueloBody.destroyFixture(sueloFixture);
rocaBody.destroyFixture(rocaFixture);

world.destroyBody(jugadorBody);
world.destroyBody(sueloBody);
world.destroyBody(rocaBody);
world.dispose();
renderer.dispose();

@Override
public void render(float delta) {
//limpiar la pantalla
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

if (debeSaltar) {
debeSaltar = false;
saltar();
}

if(Gdx.input.justTouched() && !jugadorSaltando) {


debeSaltar =true;
}

world.step(delta, 6, 2);

//La cámara tiene que actualizarse.


camera.update();
renderer.render(world, camera.combined);

private void saltar() {


Vector2 position = jugadorBody.getPosition();
jugadorBody.applyLinearImpulse(0, 5, position.x, position.y, true);}}
Mover el personaje
@Override

Ya podemos saltar, pero el


public void render(float delta) {
//limpiar la pantalla
● Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

personaje no se mueve.
if (debeSaltar) {
debeSaltar = false;
saltar();
}

if(Gdx.input.justTouched() && !jugadorSaltando) {



Debemos saltar los pinchos }
debeSaltar =true;

según se aproxime a los


//Obtenemos la velocidad actual Y del jugador para mantenerla.
float velocidadY = jugadorBody.getLinearVelocity().y;
jugadorBody.setLinearVelocity(4, velocidadY);

pinchos.
world.step(delta, 6, 2);

//La cámara tiene que actualizarse.


camera.update();
renderer.render(world, camera.combined);


Para hacerlo, vamos a usar el }}

método setLinearVelocity()
del método body. A nosotros
nos interesa la velocidad del
eje x
Mover el personaje

Tenemos un problema y es que el jugador sale
disparado. Hay que tratar esa colisión de alguna
manera.
Mover el personaje

Vamos a guardar en una variable si el jugador está
vivo o no.

Se moverá mientras esté vivo.
if(jugadorVivo) {
//Obtenemos la velocidad actual Y del jugador para mantenerla.
float velocidadY = jugadorBody.getLinearVelocity().y;
jugadorBody.setLinearVelocity(4, velocidadY);
}


Estableceremos otro contacto/colisión entre el
jugador y la roca.
Mover el personaje

UserData podemos asociar cualquier objeto a
cualquier objeto de Box2d.

Usando los UserData podemos crear esta nueva
colisión (jugador y rocas) de una forma más limpia
que la anterior.
– Asociamos una cadena de caracteres a cada Fixture,
para saber de que tipo/categoría es cada cosa.
jugadorFixture.setUserData("player");
sueloFixture.setUserData("floor");
rocaFixture.setUserData("rock");
UserData
if(fixtureA == jugadorFixture && fixtureB ==
sueloFixture) {
jugadorSaltando = false;
if (Gdx.input.isTouched()) {
public void beginContact(Contact contact) {
debeSaltar = true; Fixture fixtureA = contact.getFixtureA(), fixtureB = contact.getFixtureB();
}
} if((fixtureA.getUserData().equals("player") && fixtureB.getUserData().equals("floor")) ||

(fixtureB.getUserData().equals("player") && fixtureA.getUserData().equals("floor"))) {

if (Gdx.input.isTouched()) {
if(fixtureB == jugadorFixture && fixtureA == debeSaltar = true;
sueloFixture) { }
jugadorSaltando = false; jugadorSaltando = false;

if (Gdx.input.isTouched()) { }

}
debeSaltar = true;
}
}
User Data - Movimiento

Aprovechando esta técnica, programaremos
también la colisión entre el personaje y la piedra.
if((fixtureA.getUserData().equals("player") && fixtureB.getUserData().equals("rock")) ||
(fixtureB.getUserData().equals("player") && fixtureA.getUserData().equals("rock"))) {
jugadorVivo = false;
}
package com.mygdx.game;

Movimiento
import java.awt.RenderingHints.Key;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.ContactImpulse;
import com.badlogic.gdx.physics.box2d.ContactListener;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.Manifold;

del personaje
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;

public class Box2DScreen extends BaseScreen {

public Box2DScreen(MyGdxGame game) {


super(game);
}

private World world;


private Box2DDebugRenderer renderer;

private OrthographicCamera camera;

private Body jugadorBody;


private Fixture jugadorFixture;
private Fixture sueloFixture;

private Body sueloBody;

private Body rocaBody;


private Fixture rocaFixture;

private boolean debeSaltar, jugadorSaltando = false, jugadorVivo = true;

Ahora al producirse
@Override
public void show() {
world = new World(new Vector2(0, -10), true);
renderer = new Box2DDebugRenderer();

● camera = new OrthographicCamera(16, 9);


camera.translate(0,1);

//Colisiones
world.setContactListener(new ContactListener() {

@Override
public void beginContact(Contact contact) {

una colisión no se
Fixture fixtureA = contact.getFixtureA(), fixtureB = contact.getFixtureB();

if((fixtureA.getUserData().equals("player") && fixtureB.getUserData().equals("floor")) ||


(fixtureB.getUserData().equals("player") && fixtureA.getUserData().equals("floor"))) {
if (Gdx.input.isTouched()) {
debeSaltar = true;
}
jugadorSaltando = false;
}

if((fixtureA.getUserData().equals("player") && fixtureB.getUserData().equals("rock")) ||


(fixtureB.getUserData().equals("player") && fixtureA.getUserData().equals("rock"))) {

actualiza la velocidad y
jugadorVivo = false;
}
}

@Override
public void endContact(Contact contact) {
Fixture fixtureA = contact.getFixtureA(), fixtureB = contact.getFixtureB();
if(fixtureA == jugadorFixture && fixtureB == sueloFixture) {
jugadorSaltando = true;
}

if(fixtureB == jugadorFixture && fixtureA == sueloFixture) {


jugadorSaltando = true;

se para.
}
}

@Override
public void preSolve(Contact contact, Manifold oldManifold) {}

@Override
public void postSolve(Contact contact, ContactImpulse impulse) {}

});

BodyDef jugadorDef = createJugadorBodyDef();


jugadorBody = world.createBody(jugadorDef);

//Creamos el suelo
sueloBody = world.createBody(createSueloBodyDef());
rocaBody = world.createBody(createRocaBodyDef(6f));

PolygonShape jugadorShape = new PolygonShape();


jugadorShape.setAsBox(0.5f, 0.5f); // se multiplica por 2 -> 1m x 1m
jugadorFixture = jugadorBody.createFixture(jugadorShape,1);
jugadorShape.dispose();

PolygonShape sueloShape = new PolygonShape();


sueloShape.setAsBox(500, 1); // 1km x 2m
sueloFixture = sueloBody.createFixture(sueloShape,1);
sueloShape.dispose();

rocaFixture = crearRocaFixture(rocaBody);

jugadorFixture.setUserData("player");
sueloFixture.setUserData("floor");
rocaFixture.setUserData("rock");
}

private BodyDef createJugadorBodyDef() {


BodyDef def = new BodyDef();
def.position.set(0, 0.5f);
def.type = BodyDef.BodyType.DynamicBody;
return def;
}

private BodyDef createSueloBodyDef() {


BodyDef def = new BodyDef();
def.position.set(0, -1);
def.type = BodyDef.BodyType.StaticBody;
return def;
}

private BodyDef createRocaBodyDef(float x) {


BodyDef def = new BodyDef();
def.position.set(x, 0.5f);
def.type = BodyDef.BodyType.StaticBody;
return def;
}

private Fixture crearRocaFixture(Body rocaBody) {


Vector2[] vertices = new Vector2[3];
vertices[0] = new Vector2(-0.5f, -0.5f);
vertices[1] = new Vector2(0.5f, -0.5f);
vertices[2] = new Vector2(0f, 0.5f);

PolygonShape shape = new PolygonShape();


shape.set(vertices);
Fixture fix = rocaBody.createFixture(shape,1);
return fix;
}

@Override
public void dispose() {
jugadorBody.destroyFixture(jugadorFixture);
sueloBody.destroyFixture(sueloFixture);
rocaBody.destroyFixture(rocaFixture);

world.destroyBody(jugadorBody);
world.destroyBody(sueloBody);
world.destroyBody(rocaBody);
world.dispose();
renderer.dispose();

@Override
public void render(float delta) {
//limpiar la pantalla
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

if (debeSaltar) {
debeSaltar = false;
saltar();
}

if(Gdx.input.justTouched() && !jugadorSaltando) {


debeSaltar =true;
}

if(jugadorVivo) {
//Obtenemos la velocidad actual Y del jugador para mantenerla.
float velocidadY = jugadorBody.getLinearVelocity().y;
jugadorBody.setLinearVelocity(4, velocidadY);
}

world.step(delta, 6, 2);

//La cámara tiene que actualizarse.


camera.update();
renderer.render(world, camera.combined);

private void saltar() {


Vector2 position = jugadorBody.getPosition();
jugadorBody.applyLinearImpulse(0, 5, position.x, position.y, true);
Integración

Vamos a empezar a organizar todo e integrar
Box2d con Scene2D
– Se cogerá lo mejor de cada uno.
– Crearemos el stage con Scene2D

Éste se encarga de dibujar
– Utilizaremos una simulación de Box2D

Éste se encarga de todas las posiciones

Stage World
«Scene2d» «Box2d»
Integración
package com.mygdx.game;


Creamos la clase import
import
com.badlogic.gdx.Gdx;
com.badlogic.gdx.graphics.GL20;

GameScreen import
import
import
com.badlogic.gdx.math.Vector2;
com.badlogic.gdx.physics.box2d.World;
com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.utils.viewport.FitViewport;
– Extenderá de BaseScreen public class GameScreen extends BaseScreen{

private Stage stage;


– Tendrá los siguientes private World world;

atributos: public GameScreen(MyGdxGame game) {


super(game);
stage = new Stage (new FitViewport(640, 360));

Stage }
world = new World (new Vector2(0,-10), true);


World @Override
public void render(float delta) {
Gdx.gl.glClearColor(0.4f, 0.5f , 0.8f, 1.f);

Cambiamos MainGame Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

stage.act(); //Actualiar los actores

para que use GameScreen }


stage.draw(); //Dibujar los actores

@Override
public void dispose() {
stage.dispose();
world.dispose();
}
}
package com.mygdx.game;

Integración import com.badlogic.gdx.Gdx;


import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.utils.viewport.FitViewport;
package com.mygdx.game;
public class GameScreen extends BaseScreen{
import com.badlogic.gdx.Game;
private Stage stage;
private World world;
//ApplicationAdapter applicationAdapter por game
public class MyGdxGame extends Game { public GameScreen(MyGdxGame game) {
super(game);
stage = new Stage (new FitViewport(640, 360));
@Override world = new World (new Vector2(0,-10), true);
public void create () { }

@Override
setScreen(new GameScreen(this));
public void render(float delta) {
} Gdx.gl.glClearColor(0.4f, 0.5f , 0.8f, 1.f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

}
stage.act(); //Actualiar los actores
stage.draw(); //Dibujar los actores

@Override
public void dispose() {
stage.dispose();
world.dispose();
}
}
Integración

Solo se ve una pantalla azul, no tenemos actores.

Creamos un paquete entities

Dentro crearemos PlayerEntity que tendrá los siguientes
atributos public class PlayerEntity extends Actor{

private Texture texture;


– Texture private
private
World world;
Body body;
private Fixture fixture;
– World private boolean alive = true, jumping = false;

public PlayerEntity(World world, Texture texture, Vector2 position) {


– Body
this.world = world;
– Fixture this.texture = texture;

BodyDef def = new BodyDef();


– alive (true) def.position.set(position);
def.type = BodyDef.BodyType.DynamicBody;
this.body = world.createBody(def);
– jumping(false) PolygonShape box = new PolygonShape();
box.setAsBox(0.5f, 0.5f); // 1m x 1m
fixture = body.createFixture(box,3);
box.dispose();

}
Problemas de la integración

Ahora hay que dibujarlo pero
– Box2D opera en metros (4m)
– Scene2D opera en pixeles. (360px)
– Hay que hacer conversiones entre px y Metros:

Ratio = 360/4 = 90

4m para
Box2D

360px
para
Scene2D
Problemas de la integración

Creamos una clase con el ratio de conversión 1m =
90 px

Convertiré los metros que me diga Box2D por la
constante para pasarlo a pixeles (lo que usa Scene2d)
package com.mygdx.game;
package com.mygdx.game.entities;
public class Constants { [...]
import static com.mygdx.game.Constants.*;
public static final float PIXELS_IN_METER = 90f; public class PlayerEntity extends Actor{

} private Texture texture;


private World world;
private Body body;
private Fixture fixture;
private boolean alive = true, jumping = false;

public PlayerEntity(World world, Texture texture, Vector2 position) {

this.world = world;
this.texture = texture;

BodyDef def = new BodyDef();


def.position.set(position);
def.type = BodyDef.BodyType.DynamicBody;
this.body = world.createBody(def);

PolygonShape box = new PolygonShape();


box.setAsBox(0.5f, 0.5f); // 1m x 1m
fixture = body.createFixture(box,3);
box.dispose();

//Tamaño para Scene2E


setSize(PIXELS_IN_METER, PIXELS_IN_METER);

@Override
public void draw(Batch batch, float parentAlpha) {
setPosition(body.getPosition().x * PIXELS_IN_METER, body.getPosition().y * PIXELS_IN_METER);
batch.draw(texture, getX(), getY(), getWidth(), getHeight());
}

}
Integración
public class MyGdxGame extends Game {

Pintamos el jugador, ¿Como
cargamos la textura? private AssetManager manager;
– Usamos el AssetManager: @Override
Centraliza lo recursos en un public void create () {
repositorio único. manager = new AssetManager();
manager.load("ninja.png", Texture.class);

Lo crearé en MyGdxGame, ya
manager.load("Stone.png", Texture.class);
que todas las pantallas tendrán
manager.finishLoading();
acceso a él.

Cargamos los recursos con setScreen(new GameScreen(this));
load (“nombre”, Class) }

Cuando terminemos de cargar
todos los recursos, usaremos el public AssetManager getManager() {
método “finishLoading()” return manager;
}
}
package com.mygdx.game;

import com.badlogic.gdx.Gdx;

Integración
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.scenes.scene2d.Stage;
package com.mygdx.game; import com.badlogic.gdx.utils.viewport.FitViewport;
import com.mygdx.game.entities.PlayerEntity;

import com.badlogic.gdx.Game; public class GameScreen extends BaseScreen{

import com.badlogic.gdx.assets.AssetManager; private Stage stage;


private World world;
import com.badlogic.gdx.graphics.Texture; private PlayerEntity player;

public GameScreen(MyGdxGame game) {


//ApplicationAdapter applicationAdapter por game super(game);
stage = new Stage (new FitViewport(640, 360));
public class MyGdxGame extends Game {
world = new World (new Vector2(0,-10), true);

}
private AssetManager manager;
@Override
public void show() {
@Override Texture playerTexture = game.getManager().get("ninja.png");
Vector2 posInicial = new Vector2(1, 2);
public void create () { player = new PlayerEntity(world, playerTexture, posInicial);
manager = new AssetManager(); stage.addActor(player);
}
manager.load("ninja.png", Texture.class);
@Override
manager.load("Stone.png", Texture.class);
public void hide() {
manager.finishLoading(); player.detach();
player.remove();//elimina el actor de stage.
}

setScreen(new GameScreen(this));
@Override
} public void render(float delta) {
Gdx.gl.glClearColor(0.4f, 0.5f , 0.8f, 1.f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
public AssetManager getManager() {
stage.act(); //Actualiar los actores
return manager; stage.draw(); //Dibujar los actores

}
}
}
@Override
public void dispose() {
stage.dispose();
world.dispose();
}
}
Otro problema

Scene2d y Box2d tienen origenes en posiciones
distintas. Hay que tenerlo en cuenta en el draw.
Otro problema

Tenemos que quitarle 0.5f (la mitad X, la mitad Y
que usa Box2D para ajustarlo a Scene2d)

public void draw(Batch batch, float parentAlpha) {


// hay que quitarles -0.5f para que se coloque en la parte inferior izquierda.
setPosition((body.getPosition().x - 0.5f) * PIXELS_IN_METER,
(body.getPosition().y - 0.5f) * PIXELS_IN_METER) ;
batch.draw(texture, getX(), getY(), getWidth(), getHeight());
}

También podría gustarte