Programación de videojuegos con LibGdx Javier Villegas Gomez
Índice -Arquitectura de un videojuego -Funcionamiento de un videojuego -LibGdX -Ejemplo: Hello world -Personaje principal - Movimiento -Ejercicio 1 -Obstáculos -Colisiones -Ejercicio 2 -Animaciones -Ejercicio 3 -Enemigo, jerarquía de clases -Optimización del código
Arquitectura de un videojuego Los videojuegos de acción suelen tener muchos elementos en movimiento a la vez: Personajes Enemigos Balas (disparos) El fondo y demás objetos Cómo lo programamos?...
Funcionamiento de un videojuego GAME LOOP (Bucle principal) En él se lee la entrada del usuario, se procesa actualizando las estructuras de datos internas del juego y después se muestra el resultado al usuario (imágenes, sonidos, etc.) muchas veces por segundo.
Funcionamiento general 1º - Inicialización 2º - Game Loop (Bucle principal, en el que se actualizan y dibujan todos los cambios) 3º - Liberar memoria
Como funciona LibGdx
Estructura de LibGDX Se divide en 4 módulos: -Marco de aplicacion: maneja el bucle principal y el ciclo de vida -Componente de graficos: gestiona imagenes y objetos gráficos en pantalla -Componente de audio: acceso a sonido y música de la aplicación -Componente de E/S (files): lee y escribe en los ficheros de datos (imágenes, configuracion, sonido...) -Componente de Entrada (input): gestiona la entrada por teclado, pantalla tactil, acelerometro...
Hello World, muy visual Texture MenuTexture; SpriteBatch batch; public MenuPrincipal(HelloWorld helloworld) { this.helloworld = helloworld; MenuTexture = new Texture("data/PantallaMenu.png"); MenuTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear); batch = new SpriteBatch(); public void render(float delta) { Gdx.gl.glClearColor(0, 0, 0, 1); Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); batch.begin(); batch.draw(menutexture, 0, 0, MenuTexture.getWidth(), MenuTexture.getHeight()); batch.end();
Creamos nuestro personaje public class Turing { Vector2 posicion; //Un vector2 es un vector de 2 elementos float anchura, altura; Rectangle bordes; float velocidad; float SPEED; public Turing(Vector2 posicion, float anchura, float altura){ this.posicion = posicion; this.anchura = anchura; this.altura = altura; bordes = new Rectangle(posicion.x, posicion.y, anchura, altura); this.speed = 100;
Y le damos movimiento Dentro de la clase Turing: public void update() { if(gdx.input.iskeypressed(keys.d)){ posicion.x = posicion.x + Gdx.graphics.getDeltaTime() * SPEED; bordes.x = posicion.x; bordes.y = posicion.y;
Ejercicio 1 - Completar movimiento a la izquierda - Corregir el salto Pista: usar variables de estado para controlar la subida y la bajada Ejemplo: boolean saltando, enelsuelo...
Creamos un obstáculo: Plataforma Tan simple como crear una clase que represente la altura, anchura y posicion de la plataforma. public class Plataforma { public Vector2 posicion; public float anchura; public float altura; public Rectangle bordes; public Plataforma(Vector2 posicion, float anchura, float altura){ this.posicion = posicion; this.anchura = anchura; this.altura = altura; bordes = new Rectangle(posicion.x, posicion.y, anchura, altura); Y Turing debe recibir las plataformas, para saber cuando colisiona con ellas turing = new Turing(new Vector2(10,10), texturaturing.getwidth()/3, texturaturing.getheight()/3, plataforma);
Colisión Nos ayudamos de la función overlaps boolean Colisiona(Rectangle a, Rectangle b){ if(a.overlaps(b)){ // nos devuelve verdadero si dos rectángulos se solapan return true; return false; Problema: Como sabemos de que lado proviene la colisión? Por arriba o por la derecha?
Colisión Solución: Prevenir la colisión - Si el siguiente movimiento a la derecha provoca colisión, bloqueamos el movimiento a la derecha, y así en las demás direcciones
Ejercicio 2 - Las colisiones fallan por los lados, en cuanto choca empieza a bajar, y por abajo no colisiona, atraviesa el obstáculo, que falla?
Animaciones Necesitamos: Sprite de imágenes: TextureRegion[] turingdchaframes; //Vector de TextureRegion TextureRegion[] turingizqframes; // Aqui almacenaremos cada imagen del sprite de la animación TextureRegion frameactual; // se encargara de dibujar el frame correspondiente float statetime; // Segundos desde que comienza la animación Animation turingdcha; // El objeto animacion Animation turingizq;
Animaciones Lo inicializamos todo: TextureRegion[][] tmp = TextureRegion.split(texturaturingdcha,texturaturingdcha.getWidth()/20, texturaturingdcha.getheight()/1); turingdchaframes = new TextureRegion[20*1]; int indice = 0; for (int i = 0; i < 1; i++) { for (int j = 0; j < 20; j++) {// aqui rellenamos el vector con cada imagen turingdchaframes[indice++] = tmp[i][j]; turingdcha = new Animation(0.05f, turingdchaframes); // y creamos la animacion con el array de sprites. turingizq = new Animation(0.05f, turingizqframes);
Animaciones Y los utilizamos: turing.update(); statetime = turing.getstatetime(); // statetime = (statetime + Gdx.graphics.getDeltaTime()); if(turing.getdireccion() == 1){ frameactual = turingdcha.getkeyframe(statetime, true); else{ frameactual = turingizq.getkeyframe(statetime, true); batch.begin();... batch.draw(frameactual, turing.getposicion().x, turing.getposicion().y, turing.getanchura(), turing.getaltura()); batch.end();
Ejercicio 3 -La animación sólo funciona hacia la derecha... Que le falta?
Enemigo, Jerarquía de clases La clase Enemigo es muy similar a la clase Turing o Personaje, en ese caso... por qué no aprovechar algunas características?
Enemigo, Jerarquía de clases De esta manera, las subclases nos quedan mucho más simplificadas: public class Enemigo extends EntidadMovible{ int movimiento; // El movimiento del enemigo public Enemigo(Vector2 posicion, float anchura, float altura, int direccion) { super(posicion, anchura, altura, direccion); this.movimiento = 50; public class Plataforma extends Entidad{ public Plataforma(Vector2 posicion, float anchura, float altura) { super(posicion, anchura, altura);
Optimizando el código Para estructurar aun mas el codigo, separamos tanto la E/S como la detección de colisiones del código general. CollisionHandler se encargara de las colisiones, para ello recibirá todos los elementos necesarios: Personaje, plataformas, enemigos... en lugar de hacerlo desde el propio personaje
Optimizando el código InputHandler se encargara de la Entrada, para ello utilizamos la clase de LibGdx InputProcessor que nos permitirá controlar las teclas pulsadas
Optimizando el codigo public class InputHandler implements InputProcessor { Turing turing; public InputHandler(Turing turing) { this.turing = turing; //Aqui van las funciones para el manejo de cada tecla Y simplemente en GameScreen: Gdx.input.setInputProcessor(new InputHandler(turing));
Gracias por vuestra atencion! Este programa ha sido realizado con financiación del Proyecto de Mejora Docente "Consolidación de los conocimientos de programación mediante el desarrollo de videojuegos: una experiencia en el año de Turing" (código AAA_13_019) de la Convocatoria de Actuaciones Avaladas para la Mejora Docente, Formación del Profesorado y Difusión de Resultados de la Unidad de Innovación Docente de la Universidad de Cádiz, Curso 2012/2013, cuyos fondos proceden de la Consejería de Economía, Innovación, Ciencia y Empleo de la Junta de Andalucía.