viernes, 17 de abril de 2009

Recibiendo Input

Otra de las cosas fundamentales en la programacion de juegos, es recibir input del teclado, es decir, saber que teclas se apretan, y cuales se sueltan, para poder asi mover los sprites a voluntad.
Una vez que comenzamos a recibir input tenemos que tener algo muy importante en cuenta, es mas comodo y por decirlo asi un estandar, y mejor la performance, dividir el juego en logica y dibujar. El dibujo se ejecuta las veces que decimos que se actualize la pantalla, y la logica lo hace mas rapido aun, asi aunque el juego sea muy lento o ande mal el input llegara bien y podremos animar los dibujos.
En nuestro ejemplo anterior, se movia automaticamente, ahora vamos a hacer, que lo movamos nosotros, usando el teclado, en este caso, las flechas, pero puede ser cualquier boton.
Primero que nada las clases que debemos importar, esas son KeyAdapter y KeyEvent, de awt.
Esas clases combinadas nos permiten leer las teclas que se han apretado, o soltado.
Agregamos los imports


import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;


En el constructor tenemos que hacer que la ventana sea focus-able, es decir que pueda estar en primer plano y asi recibir el input, esto es muy importante ya que sin esto no funciona, y es solo una linea de codigo, lo otro que vamos a agregar en el constructor es un KeyListener, esto "escucha" constantemente nuevas entradas (inputs).

this.setFocusable(true);
this.addKeyListener(new Adapter());


Otra cosa que podemos agregar es DoubleBuffering, esta opcion recomiendo ponerla siempre, no cuesta nada y ayuda mucho cuando el juego va creciendo

this.setDoubleBuffered(true);


Basicamente, dibuja primero en memoria, y luego todo junto en pantalla de una sola vez, es mucho mejor asi.
Otra cosa que tenemos que actualizar son los dx y dy, en el anterior valian la velocidad, es decir empezaban valiendo 2, pero nosotros queremos que se mueva por las teclas, asi que los vamos a poner igual a 0, y lo cambiamos cuando apretamos las teclas, que en este caso seran las flechas.

dx = dy = 0;


Ahora, dentro de la clase Board, hacemos otra clase, Adapter, que hereda de KeyAdapter.

private class Adapter extends KeyAdapter{
@Override
public void keyPressed(KeyEvent e){
int key = e.getKeyCode();

if(key == KeyEvent.VK_UP)
dy = SPEED * -1;
if(key == KeyEvent.VK_DOWN)
dy = SPEED;
if(key == KeyEvent.VK_RIGHT)
dx = SPEED;
if(key == KeyEvent.VK_LEFT)
dx = SPEED * -1;
}

@Override
public void keyReleased(KeyEvent e){
int key = e.getKeyCode();

if(key == KeyEvent.VK_UP)
dy = 0;
if(key == KeyEvent.VK_DOWN)
dy = 0;
if(key == KeyEvent.VK_RIGHT)
dx = 0;
if(key == KeyEvent.VK_LEFT)
dx = 0;
}
}


Puden ver los @Override, indican simplemente que la funcion de abajo esta siendo sobre-escrita de la clase que heredamos. Las funciones en si son bastante faciles de entender, keyPressed es cuando se presiona una tecla, y keyReleased cuando se suelta, ambas requieren como argumento un KeyEvent, ahi es donde podemos ver que tecla se apreto, ese argumento contiene la tecla, luego simplemente comparamos cual fue y cambiamos dx y dy.
Ahora, editamos la funcion actionPerformed

public void actionPerformed(ActionEvent e){ // Se ejecuta cada 5ms
if((x>0 && dx<0)>0))
x += dx;
if((y>0 && dy<0)>0))
y += dy;
repaint(); // "re-pintamos" el panel
}


Lo que hace eso es pura logica, la anterior no funcionaba, aca decimos, si la posicion x del sprite es mayor que 0 y lo quiero mover hacia la izuiqerda, o la posicion x es menor que el limite de la pantalla y lo quiero mover a la derecha, lo muevo x)
Lo importante para resaltar sin embargo aca, es la clase Adapter y como la agregamos en el constructor.
Una vez que hallamos hecho todos los cambios, ya podemos mover el sprite con las flechas de nuestro teclado.
Aca les dejo Board.java

package animation;

import java.awt.Image;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
import javax.swing.ImageIcon;
import java.awt.Color;

// Timer Imports
import java.awt.Toolkit;
import javax.swing.Timer;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent; // Para poder usar actionPerformed, necesitamos este tipo

// Para leer teclas
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

/**
*
* @author fede
*/
public class Board extends JPanel implements ActionListener {
private Image image;
private Timer timer;
private int x, y, dx, dy;
private static int SPEED = 2;

public Board(){
ImageIcon ii = new ImageIcon(this.getClass().getResource("image.png"));
image = ii.getImage();
this.setBackground(Color.white);
this.setFocusable(true);
this.addKeyListener(new Adapter());
this.setDoubleBuffered(true); // Dibujo en memoria antes que en pantalla
x = 150;
y = 10;
dx = dy = 0;

// Timer
timer = new Timer(5, this); // cada 5ms llama actionPerformed
timer.start();
}

public void paint(Graphics g){
super.paint(g);

Graphics2D g2d = (Graphics2D)g; // Convertimos a g de Graphics a Graphics2D
g2d.drawImage(image, x, y, this);

// Timer
Toolkit.getDefaultToolkit().sync(); // fuerza sincronizacion, basicamente

g.dispose();
}

public void actionPerformed(ActionEvent e){ // Se ejecuta cada 5ms
if((x>0 && dx<0)>0))
x += dx;
if((y>0 && dy<0)>0))
y += dy;
repaint(); // "re-pintamos" el panel
}

private class Adapter extends KeyAdapter{
@Override
public void keyPressed(KeyEvent e){
int key = e.getKeyCode();

if(key == KeyEvent.VK_UP)
dy = SPEED * -1;
if(key == KeyEvent.VK_DOWN)
dy = SPEED;
if(key == KeyEvent.VK_RIGHT)
dx = SPEED;
if(key == KeyEvent.VK_LEFT)
dx = SPEED * -1;
}

@Override
public void keyReleased(KeyEvent e){
int key = e.getKeyCode();

if(key == KeyEvent.VK_UP)
dy = 0;
if(key == KeyEvent.VK_DOWN)
dy = 0;
if(key == KeyEvent.VK_RIGHT)
dx = 0;
if(key == KeyEvent.VK_LEFT)
dx = 0;
}
}
}


La imagen en este caso, sera exactamente la misma que la anterior, deberian probarlo para ver la diferencia x)



Bueno, moverlo es lo de menos, lo mas importante de esta parte es lo de leer las teclas, ya lo demas es simple logica, espero se halla entendido bien esa parte.

2 comentarios: