Lib GDX
Lib GDX
Lib GDX
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;
@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;
import com.badlogic.gdx.scenes.scene2d.Stage;
Stage
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.scenes.scene2d.Stage;
@Override @Override
} }
}
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;
super(game);
}
import com.badlogic.gdx.scenes.scene2d.Actor;
@Override
stage.addActor(jugador);
}
jugador.setPosition(20, 100);
@Override
stage.dispose();
@Override
//limpiar la pantalla
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
}
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;
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;
@Override
public void show() {
stage = new Stage();
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);
@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
500 px
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;
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);
}
}
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;
@Override
private Texture jugador; public void show() {
private boolean alive; stage = new Stage();
stage.setDebugAll(true);
@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);
}
}
@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 {
@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 {
@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;
@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);
}
}
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);
}
@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.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);
}
}
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());
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;
@Override
public void show() {
world = new World(new Vector2(0, -10), true);
renderer = new Box2DDebugRenderer();
camera = new OrthographicCamera(32, 18);
@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);
}
}
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
2m
Position: (2y)
X =0
Y = -1
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();
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;
@Override
public void show() {
world = new World(new Vector2(0, -10), true);
renderer = new Box2DDebugRenderer();
camera = new OrthographicCamera(32, 18);
//Creamos el suelo
sueloBody = world.createBody(createSueloBodyDef());
@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);
}
}
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;
@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);
//Creamos el suelo
sueloBody = world.createBody(createSueloBodyDef());
@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);
}
}
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);
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 {
@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);
//Creamos el suelo
sueloBody = world.createBody(createSueloBodyDef());
rocaBody = world.createBody(createRocaBodyDef(1));
rocaFixture = crearRocaFixture(rocaBody);
}
@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);
}
}
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);
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;
@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);
//Creamos el suelo
sueloBody = world.createBody(createSueloBodyDef());
rocaBody = world.createBody(createRocaBodyDef(1));
rocaFixture = crearRocaFixture(rocaBody);
}
@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);
}
}
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 (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;
@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;
}
@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
});
//Creamos el suelo
sueloBody = world.createBody(createSueloBodyDef());
rocaBody = world.createBody(createRocaBodyDef(4f));
rocaFixture = crearRocaFixture(rocaBody);
}
@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);
}
}
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();
}
world.step(delta, 6, 2);
}
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;
}
@Override
public void endContact(Contact contact) {
Fixture fixtureA = contact.getFixtureA(), fixtureB = contact.getFixtureB();
if(fixtureA == jugadorFixture && fixtureB == 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;
@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
}
jugadorSaltando = false;
@Override
public void endContact(Contact contact) {
Fixture fixtureA = contact.getFixtureA(), fixtureB = contact.getFixtureB();
if(fixtureA == jugadorFixture && fixtureB == sueloFixture) {
jugadorSaltando = 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
No se permiten saltos
});
//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();
rocaFixture = crearRocaFixture(rocaBody);
}
@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();
}
world.step(delta, 6, 2);
}
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;
}
}
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;
@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) {
@Override
public void endContact(Contact contact) {
Fixture fixtureA = contact.getFixtureA(), fixtureB = contact.getFixtureB();
if(fixtureA == jugadorFixture && fixtureB == 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
});
– //Creamos el suelo
sueloBody = world.createBody(createSueloBodyDef());
rocaBody = world.createBody(createRocaBodyDef(4f));
Aunque el código es
sueloShape.setAsBox(500, 1); // 1km x 2m
sueloFixture = sueloBody.createFixture(sueloShape,1);
sueloShape.dispose();
● }
rocaFixture = crearRocaFixture(rocaBody);
manifiestamente
}
mejorable, la forma de
def.position.set(x, 0.5f);
def.type = BodyDef.BodyType.StaticBody;
return def;
}
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();
}
world.step(delta, 6, 2);
personaje no se mueve.
if (debeSaltar) {
debeSaltar = false;
saltar();
}
pinchos.
world.step(delta, 6, 2);
●
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")) ||
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;
Ahora al producirse
@Override
public void show() {
world = new World(new Vector2(0, -10), true);
renderer = new Box2DDebugRenderer();
//Colisiones
world.setContactListener(new ContactListener() {
@Override
public void beginContact(Contact contact) {
una colisión no se
Fixture fixtureA = contact.getFixtureA(), fixtureB = contact.getFixtureB();
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;
}
se para.
}
}
@Override
public void preSolve(Contact contact, Manifold oldManifold) {}
@Override
public void postSolve(Contact contact, ContactImpulse impulse) {}
});
//Creamos el suelo
sueloBody = world.createBody(createSueloBodyDef());
rocaBody = world.createBody(createRocaBodyDef(6f));
rocaFixture = crearRocaFixture(rocaBody);
jugadorFixture.setUserData("player");
sueloFixture.setUserData("floor");
rocaFixture.setUserData("rock");
}
@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(jugadorVivo) {
//Obtenemos la velocidad actual Y del jugador para mantenerla.
float velocidadY = jugadorBody.getLinearVelocity().y;
jugadorBody.setLinearVelocity(4, velocidadY);
}
world.step(delta, 6, 2);
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{
●
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);
@Override
public void dispose() {
stage.dispose();
world.dispose();
}
}
package com.mygdx.game;
@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{
}
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{
this.world = world;
this.texture = texture;
@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;
}
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)