LIBGDX As interfaces para capturar eventos

De MediaWiki
Saltar a: navegación, buscar

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.

  1. public class PantallaXogo implements Screen,InputProcessor {
  2.         ...............
  3.         @Override
  4.         public boolean keyDown(int keycode) {
  5.                 // TODO Auto-generated method stub
  6.                 return false;
  7.         }
  8.  
  9.         @Override
  10.         public boolean keyUp(int keycode) {
  11.                 // TODO Auto-generated method stub
  12.                 return false;
  13.         }
  14.  
  15.         @Override
  16.         public boolean keyTyped(char character) {
  17.                 // TODO Auto-generated method stub
  18.                 return false;
  19.         }
  20.  
  21.         @Override
  22.         public boolean touchDown(int screenX, int screenY, int pointer, int button) {
  23.                 // TODO Auto-generated method stub
  24.                 return false;
  25.         }
  26.  
  27.         @Override
  28.         public boolean touchUp(int screenX, int screenY, int pointer, int button) {
  29.                 // TODO Auto-generated method stub
  30.                 return false;
  31.         }
  32.  
  33.         @Override
  34.         public boolean touchDragged(int screenX, int screenY, int pointer) {
  35.                 // TODO Auto-generated method stub
  36.                 return false;
  37.         }
  38.  
  39.         @Override
  40.         public boolean mouseMoved(int screenX, int screenY) {
  41.                 // TODO Auto-generated method stub
  42.                 return false;
  43.         }
  44.  
  45.         @Override
  46.         public boolean scrolled(int amount) {
  47.                 // TODO Auto-generated method stub
  48.                 return false;
  49.         }
  50.  
  51.         ...............
  52. }


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.

  1.         ......................
  2.         @Override
  3.         public void show() {
  4.                 // TODO Auto-generated method stub
  5.                 Gdx.input.setInputProcessor(this);
  6.         }
  7.  
  8.         @Override
  9.         public void hide() {
  10.                 // TODO Auto-generated method stub
  11.                 Gdx.input.setInputProcessor(null);
  12.         }
  13.  
  14.         @Override
  15.         public void pause() {
  16.                 // TODO Auto-generated method stub
  17.                 Gdx.input.setInputProcessor(null);
  18.                
  19.         }
  20.  
  21.         @Override
  22.         public void resume() {
  23.                 // TODO Auto-generated method stub
  24.                 Gdx.input.setInputProcessor(this);
  25.                
  26.         }
  27.  
  28.         @Override
  29.         public void dispose() {
  30.                 // TODO Auto-generated method stub
  31.                 Gdx.input.setInputProcessor(null);
  32.                 rendererxogo.dispose();
  33.         }


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:

  1.         public enum Keys {
  2.                 ESQUERDA,DEREITA,ARRIBA,ABAIXO
  3.         }

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

  1.         HashMap<Keys, Boolean> keys = new HashMap<ControladorXogo.Keys, Boolean>();
  2.         {
  3.                 keys.put(Keys.ESQUERDA, false);
  4.                 keys.put(Keys.DEREITA, false);
  5.                 keys.put(Keys.ARRIBA, false);  
  6.                 keys.put(Keys.ABAIXO, false);  
  7.         };

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

  1.         /**
  2.          * Modifica o estado do mapa de teclas e pon a true
  3.          * @param tecla: tecla pulsada
  4.          */
  5.         public void pulsarTecla(Keys tecla){
  6.                 keys.put(tecla, true);
  7.         }
  8.         /**
  9.          * Modifica o estado do mapa de teclas e pon a false
  10.          * @param tecla: tecla liberada
  11.          */
  12.         public void liberarTecla(Keys tecla){
  13.                 keys.put(tecla, false);
  14.         }


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).

  1.         @Override
  2.         public boolean keyDown(int keycode) {
  3.                 // TODO Auto-generated method stub
  4.                 // Liberamos as teclas para que se arrastramos o dedo a outro control sen soltar o anterior non xunte o efecto
  5.                 controladorXogo.liberarTecla(ControladorXogo.Keys.ABAIXO);
  6.                 controladorXogo.liberarTecla(ControladorXogo.Keys.ARRIBA);
  7.                 controladorXogo.liberarTecla(ControladorXogo.Keys.ESQUERDA);
  8.                 controladorXogo.liberarTecla(ControladorXogo.Keys.DEREITA);
  9.  
  10.                 switch(keycode){
  11.                 case Input.Keys.UP:
  12.                         controladorXogo.pulsarTecla(ControladorXogo.Keys.ARRIBA);
  13.                         break;
  14.                 case Input.Keys.DOWN:
  15.                         controladorXogo.pulsarTecla(ControladorXogo.Keys.ABAIXO);
  16.                         break;
  17.                 case Input.Keys.LEFT:
  18.                         controladorXogo.pulsarTecla(ControladorXogo.Keys.ESQUERDA);
  19.                         break;
  20.                 case Input.Keys.RIGHT:
  21.                         controladorXogo.pulsarTecla(ControladorXogo.Keys.DEREITA);
  22.                         break;
  23.                 }
  24.  
  25.                        
  26.                 return false;
  27.         }

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.

  1. public class Alien extends Personaxe{
  2.  
  3.         private Vector2 velocidade;
  4.        
  5.         public Alien(Vector2 posicion, Vector2 tamano, float velocidade_max) {
  6.                 super(posicion, tamano, velocidade_max);
  7.                
  8.                 velocidade = new Vector2(0,0);
  9.  
  10.         }
  11.        
  12.         public float getVelocidadeX(){
  13.                 return velocidade.x;
  14.         }
  15.         public float getVelocidadeY(){
  16.                 return velocidade.y;
  17.         }
  18.  
  19.         public void setVelocidadeX(float x){
  20.                 velocidade.x = x;
  21.  
  22.         }
  23.         public void setVelocidadeY(float y){
  24.                 velocidade.y = y;
  25.         }
  26.  
  27.         @Override
  28.         public void update(float delta) {
  29.                 // TODO Auto-generated method stub
  30.  
  31.                 setPosicion(getPosicion().x+velocidade.x*delta, getPosicion().y+velocidade.y*delta);
  32.        
  33.         }
  34.  
  35. }

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.

  1. public class ControladorXogo {
  2.  
  3.         private Mundo meuMundo;
  4.         private Alien alien;
  5.         ....................
  6.         public ControladorXogo(Mundo mundo) {
  7.                 this.meuMundo = mundo;
  8.                 alien = meuMundo.getAlien();
  9.         }
  10.         ....................
  11.  
  12.         private void controlarAlien(float delta){
  13.                
  14.                 // Actualiza Alien
  15.                 alien.update(delta);
  16.         }
  17.  
  18.         /**
  19.          * Vai chamar a todos os métodos para mover e controlar os personaxes Tamén
  20.          * xestionará os eventos producidos polo usuario e que veñen dende a clase
  21.          * PantallaXogo
  22.          *
  23.          * @param delta
  24.          */
  25.         public void update(float delta) {
  26.  
  27.                 controlarCoches(delta);
  28.                 controlarRochas(delta);
  29.                 controlarTroncos(delta);
  30.                
  31.                 controlarNave(delta);
  32.                 controlarAlien(delta);
  33.                
  34.                 procesarEntradas();
  35.  
  36.         }
  37.         private void procesarEntradas(){
  38.                
  39.                 if (keys.get(Keys.DEREITA))
  40.                         alien.setVelocidadeX(alien.velocidade_max);
  41.                 if (keys.get(Keys.ESQUERDA))
  42.                         alien.setVelocidadeX(-alien.velocidade_max);
  43.                 if (!(keys.get(Keys.ESQUERDA)) && (!(keys.get(Keys.DEREITA))))
  44.                         alien.setVelocidadeX(0);
  45.                        
  46.                 if (keys.get(Keys.ARRIBA))
  47.                         alien.setVelocidadeY(alien.velocidade_max);
  48.                 if (keys.get(Keys.ABAIXO))
  49.                         alien.setVelocidadeY(-alien.velocidade_max);
  50.                 if (!(keys.get(Keys.ARRIBA)) && (!(keys.get(Keys.ABAIXO))))
  51.                         alien.setVelocidadeY(0);
  52.  
  53.         }

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.

  1. public class Controis {
  2.  
  3.         public final static Rectangle FONDO_NEGRO = new Rectangle(0,0,Mundo.TAMANO_MUNDO_ANCHO,12);
  4. }

Agora debuxamos dito control na clase RendererXogo:

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

  1.         ..............
  2.         private void debuxarControis(){
  3.                
  4.                 // Fondo negro
  5.                 spritebatch.draw(AssetsXogo.texturePuntoNegro, Controis.FONDO_NEGRO.x,Controis.FONDO_NEGRO.y,Controis.FONDO_NEGRO.width,Controis.FONDO_NEGRO.height);
  6.  
  7.         }
  8.  
  9.     public void render(float delta) {
  10.                 Gdx.gl.glClearColor(0, 0, 0, 1);
  11.                 Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
  12.  
  13.                 spritebatch.begin();
  14.                
  15.                         debuxarFondo();
  16.                
  17.                         debuxarNave();
  18.                        
  19.                         debuxarCoches();
  20.                         debuxarRochas();
  21.                         debuxarTroncos();
  22.                        
  23.                         debuxarAlien();
  24.  
  25.                         debuxarControis();
  26.                 spritebatch.end();
  27.                
  28.                 if (debugger) {
  29.                         debugger();
  30.                 }
  31.     }




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.

  1.         ....................
  2.         private void controlarAlien(float delta){
  3.                
  4.                 // Actualiza Alien
  5.                 alien.update(delta);
  6.                
  7.                 // Impide que se mova fora dos límites da pantalla
  8.                 if (alien.getPosicion().x <=0){
  9.                         alien.setPosicion(0, alien.getPosicion().y);
  10.                 }
  11.                 else {
  12.                         if (alien.getPosicion().x >= Mundo.TAMANO_MUNDO_ANCHO-alien.getTamano().x){
  13.                                 alien.setPosicion(Mundo.TAMANO_MUNDO_ANCHO-alien.getTamano().x, alien.getPosicion().y);
  14.                         }
  15.                        
  16.                 }
  17.                
  18.                 if (alien.getPosicion().y <=Controis.FONDO_NEGRO.height){
  19.                         alien.setPosicion(alien.getPosicion().x,Controis.FONDO_NEGRO.height);
  20.                 }
  21.                 else {
  22.                         if (alien.getPosicion().y >= Mundo.TAMANO_MUNDO_ALTO-alien.getTamano().y){
  23.                                 alien.setPosicion(alien.getPosicion().x, Mundo.TAMANO_MUNDO_ALTO-alien.getTamano().y);
  24.                         }
  25.                        
  26.                 }
  27.  
  28.                
  29.         }




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 -- (2015).