Lección 02 – Controles

Aventuras en Megadrive: Controles

icono

Controles

Vamos a aprender como detectar los controles de la consola.

Lo primero es llamar a JOY_init() al principio del main(). Esta función resetea los controles y mira qué controles están conectados. Podemos tener un mando, dos o más (multitap). No obstante para detectar mandos estándar *creo* que no es necesario, a ver si alguien me confirma este punto.

Dos formas de manejar los controles. Una síncrona (se ejecutará cada frame) y otra asíncrona (se ejecutará sólo cuando se pulse un botón).

Cual elegir dependerá de cómo se controle nuestro juego. Incluso ambas.

 

FORMA SÍNCRONA

Es una simple función que es llamada en cada ciclo del bucle principal.

//declaración al principio del main.c
static void handleInput(); 
...
...
...
//Bucle principal 
while(TRUE) { 
...
 //recoge la entrada de los mandos 
 handleInput(); 
 ...
}

Aparte ponemos nuestra función, simplemente comprobamos qué estamos pulsando:

static void handleInput()
{
  //variable donde se guarda la entrada del mando
  u16 value = JOY_readJoypad(JOY_1);

  //si pulsamos izquierda...
  if (value & BUTTON_LEFT)
      //mover personaje a la izquierda

  //si pulsamos derecha...
  if (value & BUTTON_RIGHT)
      //mover personaje a la derecha

  //etc
}

Ten en cuenta que esto se ejecuta cada frame. Por tanto si metemos mucho código en esta función, podría afectar al juego, ya que dejar al pulsado un botón, lo detectamos 50 o 60 veces por segundo (depende de si el juego el PAL o NTSC) y se ejecuta el código correspondiente.

También puede afectar al funcionamiento del juego, si pulsamos un botón, por ejemplo para disparar, se efectúa el disparo 50 / 60 veces por segundo. ¿Cómo evitarlo? ¿Cómo limitarlo?

Para evitarlo o limitar las veces que detectamos una tecla por segundo, podemos usar un simple contador:

//declaracion de variables globales
s16 cont_pulsacion;
...
//dentro del main()
cont_pulsacion= 0;
...
//Bucle principal
while(TRUE) {
...
//recoge entrada de los mandos sólo si cont_pulsacion vale 0
//en caso contrario,hace el ELSE y va aumentando su valor...
if(!cont_pulsacion) handleInput();
else cont_pulsacion++;
//...hasta que llega a un valor y resetea el flag de pulsacion
//en este caso 30 (detectamos 2 veces por segundo)
if(cont_pulsacion>30) cont_pulsacion = 0;
...
}

//cont_pulsacion>0 si hemos pulsado izquierda o derecha
static void handleInput() {
u16 value = JOY_readJoypad(JOY_1);
if (value & BUTTON_LEFT){  cont_pulsacion++;   //mover personaje a la izquierda  }
if (value & BUTTON_RIGHT){  cont_pulsacion++;    //mover personaje a la derecha  }
//etc
}

 

 

FORMA ASÍNCRONA

La función asíncrona detecta cuando hay un cambio en el estado de algún botón.
Se define al inicio del programa.

static void joyEvent(u16 joy, u16 changed, u16 state);

Después, justo antes del bucle principal del main ponemos:

JOY_setEventHandler(joyEvent);

Esto activa un callback: Cuando se detecte un cambio en el estado de algún botón se ejecutará automáticamente joyEvent().

No necesitamos poner nada en el bucle principal del main. Será el SGDK el que ejecutará la función cuando corresponda. En esa función debemos comprobar qué botón se ha pulsado y actuar en consecuencia.

Un ejemplo:

static void joyEvent(u16 joy, u16 changed, u16 state)
{
  // botón START
  if (state & BUTTON_START)
  {
     //hemos pulsado el botón START
  }
  else if (changed & BUTTON_START)
       {
          //hemos soltado el botón START
       }

  if (state & (BUTTON_A | BUTTON_B | BUTTON_C))
  {
      //hemos pulsado A, B o C (cualquiera de ellos)
  }
}

 

Esta forma de detectar pulsaciones funcionan porque detectamos cambios. Pero si dejamos pulsado el botón constantemente, no se detectará más que la primera pulsación ( o bien al soltar el botón). Hasta que volvamos a pulsar ( o bien a soltar) el botón, no volveremos a detectar nada , ya que tener apretado un botón no es un cambio, y por tanto no se detecta.  Para ello hemos de utilizar la manera síncrona.

Por ejemplo, imaginemos el juego de Sonic, con este código podríamos usar ‘A’ para hacer un salto. Sonic hará el salto y hasta que no soltemos el botón ‘A’ no podremos hacer otro. Perfecto.

Pero… ¿y el auto-disparo en un matamarcianos? No funcionaría.

 

Es habitual utilizar la forma síncrona para detectar el PAD y
la forma asíncrona para detectar los botones  A, B ,C , START, etc

 

 

CONSTANTES

Estas son las constantes que nos interesará chequear:

JOY_1 / JOY_2: Pad Jugadores 1 y 2.
BUTTON_START, BUTTON_A, BUTTON_B, BUTTON_C: Botones mando de 3 botones.
BUTTON_X, BUTTON_Y, BUTTON_Z, BUTTON_MODE:  Botones mando de 6 botones.
BUTTON_UP, BUTTON_DOWN, BUTTON_LEFT, BUTTON_RIGHT: El D-PAD.
if (state & XXXX):   Hemos pulsado el botón XXXX
if (changed & XXXX): Hemos soltado el botón XXXX

Hay más cosas que contar pero en principio no las voy a usar, os remito a la Wiki del SGDK. Pero si comentaré JOY_waitPressBtn(), que servirá para pasar las pantallas de la Intro y las pantallas entre fases.

JOY_waitPressBtn(): Espera a que se pulse un botón cualquiera (A,B ó C)

 

OTROS CONTROLES

Hay otros tipos de controles diferentes a los mandos de siempre. El Menacer, el ratón, un trackball, etc. No voy a entrar en ese terreno pues no les voy a dar uso.

 

EJEMPLO PRÁCTICO

En el SGDK hay una carpeta llamada Samples. Copy+Paste a tu carpeta de proyectos.
En esta carpeta hay ejemplos y tutoriales. Abre el proyecto JOYTEST, curiosea un poco.

 

GITHUB

El código de esta lección, y de todas las demás, lo podrás encontrar en mi github:

https://github.com/danibusvlc/aventuras-en-megadrive