LIBGDX As interfaces para capturar eventos

De Manuais Informática - IES San Clemente.
Revisión del 18:27 2 feb 2015 de Angelfg (discusión | contribs.)
(difs.) ← Revisión anterior | Revisión actual (difs.) | Revisión siguiente → (difs.)
Ir a la navegación Ir a la búsqueda

UNIDADE 2: Interface de xestión de eventos

As interfaces

Información na wiki: https://github.com/libgdx/libgdx/wiki/Event-handling

O motor libgdx ten dúas interfaces para xestionar todos os eventos:

  • InputProcessor: Xestiona os eventos de pulsación de teclas, pulsación sobre a pantalla (móbil), e arrastre do dedo pola pantalla (móbil)...
  • GestureListener: Xestiona outro tipo de eventos coma son os de dobre pulsación (evento tap), o clásico movemento con dous dedos para facer un zoom da pantalla (evento zoom)....Esta interface está explicada na Unidade 3: Xestión de Eventos: GestureListener.


Para engadir unha interface temos que utilizar a palabra implements na definición da clase igual que fixemos coa interface Screen. Ó facelo vos dará un erro sobre o nome da clase. Se situades o rato enriba do erro aparecerá unha opción de Add unimplimented methods que debemos escoller.

Código da clase PantallaXogo
Obxectivo: engadir unha interface de xestión de eventos.

public class PantallaXogo implements Screen,InputProcessor {
        ...............
	@Override
	public boolean keyDown(int keycode) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean keyUp(int keycode) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean keyTyped(char character) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean touchDown(int screenX, int screenY, int pointer, int button) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean touchUp(int screenX, int screenY, int pointer, int button) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean touchDragged(int screenX, int screenY, int pointer) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean mouseMoved(int screenX, int screenY) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean scrolled(int amount) {
		// TODO Auto-generated method stub
		return false;
	}

        ...............
}


Os eventos que controlamos con esta interface son:

  • keyDown(): Tecla premida.
  • keyUp(): Tecla liberada.
  • keyTyped(): Tecla premida e xera un carácter unidade.
  • touchDown(): O dedo prema a pantalla (móbil) ou o botón do rato é premido.
  • touchUp(): O dedo deixa de premer a pantalla ou o botón do rato deixa de ser premido.
  • touchDragged(): Dedo ou rato é arrastrado pola pantalla. No caso do rato o botón debe estar presionado.
  • mouseMoved(): O rato se move pola pantalla e non se pulsa ningún botón.
  • scrolled(): Cando se preme a roda de scroll do rato.

Como vemos todos os método devolven un boolean. Este é usado no caso de que teñamos diferentes interfaces de xestión de eventos no mesmo xogo e non queremos que o evento se traslade á seguinte interface (poñeríase return true). Normalmente o deixaremos por defecto.

Unha vez temos a interface temos que informar á nosa clase que os eventos teñen que ir a dita interface.

Isto o logramos chamando ó método Gdx.input.setInputProcessor(obxecto) indicando como obxecto o obxecto da clase que ten implementada a interface. No noso caso this:

  • Gdx.input.setInputProcessor(this)

Onde temos que poñer dita orde ?

  • O poñeremos nos métodos show e resume.

Cando acabamos de xestionar os eventos chamaremos ó mesmo método pero enviando null como obxecto:

  • Gdx.input.setInputProcessor(null)

Onde temos que poñer dita orde ?

  • O poñeremos nos métodos pause, hide e dispose (pode ser que non sexa necesario poñelo en hide se programamos que cando cambiemos de pantalla chamemos ó método dispose).


Código da clase PantallaXogo
Obxectivo: Informar á clase onde se atopa a xestión de eventos.

        ......................
	@Override
	public void show() {
		// TODO Auto-generated method stub
		Gdx.input.setInputProcessor(this);
	}

	@Override
	public void hide() {
		// TODO Auto-generated method stub
		Gdx.input.setInputProcessor(null);
	}

	@Override
	public void pause() {
		// TODO Auto-generated method stub
		Gdx.input.setInputProcessor(null);
		
	}

	@Override
	public void resume() {
		// TODO Auto-generated method stub
		Gdx.input.setInputProcessor(this);
		
	}

	@Override
	public void dispose() {
		// TODO Auto-generated method stub
		Gdx.input.setInputProcessor(null);
		rendererxogo.dispose();
	}


Agora xa temos todo preparado para xestionar os eventos. Empecemos coa parte máis sinxela (a versión PC) e faremos que cando prememos as teclas do cursor movamos algo.

Como estamos programando seguindo o MVC, teremos que informar á clase controladora que se pulsou unha tecla e actuar en consecuencia.

Como o facemos ?

Existen moitas alternativas. Eu vos vou expoñer a que aprendín.

Definimos na clase ControladorXogo un tipo de datos Enum cos diferentes controis que podemos ter sobre o noso xogo. Neste caso teremos as catro teclas dos cursores:

	public enum Keys {
		ESQUERDA,DEREITA,ARRIBA,ABAIXO
	}

Despois definimos un hashmap e o inicializamos a false (lembrar importar a clase coa combinación de teclas Control+Shift+O).

	HashMap<Keys, Boolean> keys = new HashMap<ControladorXogo.Keys, Boolean>();
	{
		keys.put(Keys.ESQUERDA, false);
		keys.put(Keys.DEREITA, false);
		keys.put(Keys.ARRIBA, false);	
		keys.put(Keys.ABAIXO, false);	
	};

E por último, para poder acceder a esta variable dende a clase PantallaXogo, faremos os método pulsarTecla e liberarTecla dentro desta clase ControladorXogo:

	/**
	 * Modifica o estado do mapa de teclas e pon a true 
	 * @param tecla: tecla pulsada
	 */
	public void pulsarTecla(Keys tecla){
		keys.put(tecla, true);
	}
	/**
	 * Modifica o estado do mapa de teclas e pon a false
	 * @param tecla: tecla liberada
	 */
	public void liberarTecla(Keys tecla){
		keys.put(tecla, false);
	}


Agora só queda chamar a estes métodos dende a clase PantallaXogo...

Código da clase PantallaXogo
Obxectivo: Informar á clase controladora da tecla pulsada (lembrar importar o paquete Input coa combinación de teclas Contro+Shift+O).

	@Override
	public boolean keyDown(int keycode) {
		// TODO Auto-generated method stub
		// Liberamos as teclas para que se arrastramos o dedo a outro control sen soltar o anterior non xunte o efecto
		controladorXogo.liberarTecla(ControladorXogo.Keys.ABAIXO);
		controladorXogo.liberarTecla(ControladorXogo.Keys.ARRIBA);
		controladorXogo.liberarTecla(ControladorXogo.Keys.ESQUERDA);
		controladorXogo.liberarTecla(ControladorXogo.Keys.DEREITA);

		switch(keycode){
		case Input.Keys.UP:
			controladorXogo.pulsarTecla(ControladorXogo.Keys.ARRIBA);
			break;
		case Input.Keys.DOWN:
			controladorXogo.pulsarTecla(ControladorXogo.Keys.ABAIXO);
			break;
		case Input.Keys.LEFT:
			controladorXogo.pulsarTecla(ControladorXogo.Keys.ESQUERDA);
			break;
		case Input.Keys.RIGHT:
			controladorXogo.pulsarTecla(ControladorXogo.Keys.DEREITA);
			break;
		}

			
		return false;
	}

Como vedes non ten moita complicación. Simplemente comprobamos que tecla se preme e informamos á clase controladora.


TAREFA 2.7 A FACER: Esta parte está asociada á realización dunha tarefa.



Exercicio proposto: Agora é necesario mover o alien. Isto o facemos na clase ControladorXogo modificando a súa velocidade en función da tecla pulsada. Vos atrevedes a facelo ?

Pistas:

  • O alien ten unha mesma velocidade (non varía, non ten aceleración) pero pode moverse nos dous eixes (x/y). Teredes que crear na clase Alien os métodos necesarios para modificar dita velocidade en cada un dous eixes e obter dita velocidade.
  • O método update terá que ter en conta a velocidade nos dous eixes. Neste xogo non permitimos que se mova en diagonal xa que cando prememos unha tecla desactivamos as anteriores.
  • Lembrar que na clase ControladorXogo debemos modificar a velocidade cando se preme a tecla correspondente e para de moverse cando soltamos a tecla.
  • Lembrar chamar ó método update do alien dende a clase ControladorXogo.


Posible solución:

Código da clase Alien
Obxectivo: Definimos os métodos para xestionar a velocidade nos eixes x/y e modificamos o método update.

public class Alien extends Personaxe{

	private Vector2 velocidade;
	
	public Alien(Vector2 posicion, Vector2 tamano, float velocidade_max) {
		super(posicion, tamano, velocidade_max);
		
		velocidade = new Vector2(0,0);

	}
	
	public float getVelocidadeX(){
		return velocidade.x;
	}
	public float getVelocidadeY(){
		return velocidade.y;
	}

	public void setVelocidadeX(float x){
		velocidade.x = x;

	}
	public void setVelocidadeY(float y){
		velocidade.y = y;
	}

	@Override
	public void update(float delta) {
		// TODO Auto-generated method stub

		setPosicion(getPosicion().x+velocidade.x*delta, getPosicion().y+velocidade.y*delta);
	
	}

}

Como vemos usamos un Vector2 para gardar as dúas velocidades. Cando prememos unha tecla faremos que dita velocidade valga a velocidade máxima (a que lle mandamos no constructor cando instanciamos o obxecto alien)

Código da clase ControladorXogo
Obxectivo: Modificamos a velocidade do alien en función da tecla pulsada.

public class ControladorXogo {

	private Mundo meuMundo;
	private Alien alien;
        ....................
	public ControladorXogo(Mundo mundo) {
		this.meuMundo = mundo;
		alien = meuMundo.getAlien();
	}
        ....................

	private void controlarAlien(float delta){
		
		// Actualiza Alien
		alien.update(delta);
	}

	/**
	 * Vai chamar a todos os métodos para mover e controlar os personaxes Tamén
	 * xestionará os eventos producidos polo usuario e que veñen dende a clase
	 * PantallaXogo
	 * 
	 * @param delta
	 */
	public void update(float delta) {

		controlarCoches(delta);
		controlarRochas(delta);
		controlarTroncos(delta);
		
		controlarNave(delta);
		controlarAlien(delta);
		
		procesarEntradas();

	}
	private void procesarEntradas(){
		
		if (keys.get(Keys.DEREITA))
			alien.setVelocidadeX(alien.velocidade_max);
		if (keys.get(Keys.ESQUERDA))
			alien.setVelocidadeX(-alien.velocidade_max);
		if (!(keys.get(Keys.ESQUERDA)) && (!(keys.get(Keys.DEREITA))))
			alien.setVelocidadeX(0);
			
		if (keys.get(Keys.ARRIBA))
			alien.setVelocidadeY(alien.velocidade_max);
		if (keys.get(Keys.ABAIXO))
			alien.setVelocidadeY(-alien.velocidade_max);
		if (!(keys.get(Keys.ARRIBA)) && (!(keys.get(Keys.ABAIXO))))
			alien.setVelocidadeY(0);

	}

Nota: Modificar a orde de chamada do método debuxarAlien na clase RendererXogo para que sexa a última. Desta forma o alien vaise debuxar por enriba de todos os demais elementos.



Agora imos facer unha pequena modificación no xogo para que a parte baixa da pantalla conte cunha banda negra onde van ir as vidas salvadas e o crono do xogo.

Isto xa deberiades ser capaces de facelo. Só temos que debuxar a textura baseada no gráfico LIBGDX_puntonegro.jpg que temos feito da tarefa 2.3. Como imos ter que gardar o tamaño desta banda negra para poder debuxalo dende a clase RendererXogo e facer que o Alien non poida baixar dende a clase ControladorXogo, imos definir o tamaño nunha clase de nome Controis que a definiremos no paquete principal.

Código da clase Controis
Obxectivo: Gardamos o tamaño dos controis utilizados no xogo. Neste caso o tamaño da banda negra inferior.

public class Controis {

	public final static Rectangle FONDO_NEGRO = new Rectangle(0,0,Mundo.TAMANO_MUNDO_ANCHO,12);
}

Agora debuxamos dito control na clase RendererXogo:

Código da clase RendererXogo
Obxectivo: Debuxar a banda inferior negra.

        ..............
	private void debuxarControis(){
		
		// Fondo negro
		spritebatch.draw(AssetsXogo.texturePuntoNegro, Controis.FONDO_NEGRO.x,Controis.FONDO_NEGRO.y,Controis.FONDO_NEGRO.width,Controis.FONDO_NEGRO.height);

	}

    public void render(float delta) {
		Gdx.gl.glClearColor(0, 0, 0, 1);
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

		spritebatch.begin();
		
			debuxarFondo();
		
			debuxarNave();
			
			debuxarCoches();
			debuxarRochas();
			debuxarTroncos();
			
			debuxarAlien();

			debuxarControis();
		spritebatch.end();
		
		if (debugger) {
			debugger();
		}
    }




Exercicio proposto: Facer que o alien non saia da pantalla, é dicir, que se mova nos límites do noso mundo e que non poda ir máis abaixo da banda inferior negra.


Posible solución:
Código da clase ControladorXogo
Obxectivo: Facer que o Alien se mova dentro dos límites da pantalla.

        ....................
	private void controlarAlien(float delta){
		
		// Actualiza Alien
		alien.update(delta);
		
		// Impide que se mova fora dos límites da pantalla
		if (alien.getPosicion().x <=0){
			alien.setPosicion(0, alien.getPosicion().y);
		}
		else {
			if (alien.getPosicion().x >= Mundo.TAMANO_MUNDO_ANCHO-alien.getTamano().x){
				alien.setPosicion(Mundo.TAMANO_MUNDO_ANCHO-alien.getTamano().x, alien.getPosicion().y);
			}
			
		}
		
		if (alien.getPosicion().y <=Controis.FONDO_NEGRO.height){
			alien.setPosicion(alien.getPosicion().x,Controis.FONDO_NEGRO.height);
		}
		else {
			if (alien.getPosicion().y >= Mundo.TAMANO_MUNDO_ALTO-alien.getTamano().y){
				alien.setPosicion(alien.getPosicion().x, Mundo.TAMANO_MUNDO_ALTO-alien.getTamano().y);
			}
			
		}

		
	}




Como temos definido o noso xogo (MVC) agora é moi sinxelo engadir novas formas de control (acelerómetro, gráfico, touchpad,...) xa que o único que temos que facer é controlar na clase que ten a interface cando se preme o control e chamar ó método presionarTecla coa 'tecla' pulsada.




TAREFA 2.8.A A FACER: Esta parte está asociada á realización dunha tarefa.



Tarefas avanzadas



TAREFA AVANZADA OPTATIVA: Esta parte está asociada á realización dunha tarefa avanzada: Acelerómetro/Compás.




TAREFA AVANZADA OPTATIVA: Esta parte está asociada á realización dunha tarefa avanzada: Interface GestureListener.




TAREFA AVANZADA OPTATIVA: Esta parte está asociada á realización dunha tarefa avanzada: TochPad (joystick).





-- Ángel D. Fernández González -- (2014).