12. Utilizando el ratón

La mayoría de los lenguajes "de propósito general", como C, C++ o Pascal, no suelen incluir "por sí mismos" órdenes para manejar el ratón. Aun así, en el mundo de la informática se tiende a crear entornos que sean fáciles de manejar para el usuario, y el ratón es una gran ayuda para manejar los entornos gráficos actuales, como Windows, MacOS o las X-Window de Linux y los demás sistemas Unix. Por eso, es muy fácil encontrar bibliotecas de funciones ya creadas, que nos permitan utilizar el ratón desde estos lenguajes.

En estas bibliotecas tendremos posibilidades como: mostrar el ratón (habitualmente se llamará "showMouse"), ocultar el ratón (hideMouse), leer en qué posición se encuentra (getMouseX y getMouseY), mover el ratón a cierto punto (setMouseX y setMouseY) o ver si se ha pulsado un cierto botón del ratón (getMouseButton). Otras posibilidades más avanzadas, como comprobar si se ha hecho un doble clic, o cambiar la forma del puntero, o leer la posición de la rueda (si nuestro ratón tiene una y el driver lo reconoce), o limitar el movimiento a ciertas zonas de la pantalla, pueden no estar disponibles en algunas bibliotecas, y por supuesto los nombres exactos cambiarán de una biblioteca a otra, ya que no hay un estándar claro 

En el caso de Allegro, algunas de las posibilidades que tenemos son:

  • int mouse_x : Nos devuelve la coordenada x (horizontal) del punto en el que se encuentra el ratón. Va desde 0 (parte izquierda de la pantalla) hasta el valor máximo que permita nuestro modo de pantalla (por ejemplo hasta 639 si estamos en un modo de 640x480 puntos).
  • int mouse_y : Nos devuelve la coordenada y (vertical) del punto en el que se encuentra el ratón. Va desde 0 (parte superior de la pantalla) hasta el valor máximo que permita nuestro modo de pantalla (por ejemplo hasta 479 si estamos en un modo de 640x480 puntos).
  • int mouse_z : Nos da la posición de la rueda del ratón (insisto: sólo si nuestro ratón tiene una y el driver lo reconoce).
  • int mouse_b : Indica si se ha pulsado algún botón del ratón. Cada botón corresponde a un bit, de modo que usaríamos "if (mouse_b & 1)" para comprobar si se ha pulsado el primer ratón y "if (mouse_b & 2)" para ver el segundo.
  • void position_mouse (int x, int y): Desplaza el ratón a una cierta posición.
  • void set_mouse_range (int x1, int y1, int x2, int y2): Limita el movimiento del ratón a una zona de la pantalla, entre las coordenadas x1 y x2 (horizontal), y2¡1 e y2 (vertical).
  • void show_mouse (BITMAP *bmp): Muestra el ratón en una cierta zona (que normalmente será "screen"). Para ocultarlo, se usaría "show_mouse(NULL)".
  • void set_mouse_sprite (BITMAP *sprite): Cambia la forma del puntero del ratón. Para volver a la flecha normal, se usaría "set_mouse_sprite(NULL)". 
  • void scare_mouse() . Oculta el ratón. Normalmente será necesario antes de dibujar algo en pantalla, en la zona en que se encuentra el ratón. De lo contrario , cuando movamos el ratón se borraría lo que acabamos de dibujar (se vería el fondo anterior).
  • void unscare_mouse() . Vuelve a mostrar el ratón después de ocultarlo con "scare_mouse()".


En cualquier caso, deberá aparecer "install_mouse()" e "install_timer()" en nuestro programa antes de usar estas funciones. En el próximo apartado veremos un ejemplo sencillo de cómo usar el ratón en nuestros juegos.

12b. Quinto Juego: Puntería.

El juego será poco espectacular porque todavía no sabemos algo que sería casi fundamental para un juego de este tipo (y casi de cualquiera): cómo medir el tiempo. Lo usaremos simplemente para ver cómo utilizar el ratón en la práctica.

La idea básica del juego será:

  Dibujar un recuadro en pantalla, con posición y tamaño al azar
  Comprobar si se pulsa el ratón
  Si se ha pulsado dentro del recuadro -> un punto más para el jugador
  En cualquier caso, dibujar otro rectángulo distinto.
  Todo ello, repetido hasta que se pulse una tecla.

Así de sencillo. Sería más razonable que el juego acabase después de un cierto número de intentos, o de un cierto tiempo. De igual modo, sería más entretenido si los puntos obtenidos dependiesen del tiempo que tardáramos en acertar, incluso también del tamaño de la diana. Es más, sería preferible que la diana no fuese cuadrada, pero eso haría que fuese más difícil comprobar si se ha pulsado el ratón dentro de la zona de la diana, y esa es una complicación que no nos interesa todavía.

Por eso, en la próxima entrega veremos algo sobre temporizadores, unas nociones mínimas de matemáticas que podamos aplicar a nuestros juegos, y eso nos permitirá basarnos en la idea de este juego, pero con blancos móviles y que tengan formas "más reales".

De momento allá va nuestra toma de contacto:

/*------------------------------*/ 
/*  Intro a la programac de     */ 
/*  juegos, por Nacho Cabanes   */ 
/*                              */ 
/*    ipj12c.c                  */ 
/*                              */ 
/*  Duodecimo ejemplo: juego de */ 
/*  "punteria"                  */ 
/*  (Version inicial, sin       */ 
/*  temporizadores)             */ 
/*                              */ 
/*  Comprobado con:             */
/*  - Djgpp 2.03 (gcc 3.2)      */
/*    y Allegro 4.02 - MsDos    */
/*  - MinGW 2.0.0-3 (gcc 3.2)   */
/*    y Allegro 4.02 - Win      */
/*  - DevC++ 4.9.9.2(gcc 3.4.2) */
/*    y Allegro 4.03 - Win XP   */
/*------------------------------*/ 
 
#include <stdlib.h>         /* Para "rand" */
#include <allegro.h>
 
 
/* ------------------------------------------------ */
/*                                                  */
/* -------------- Cuerpo del programa ------------- */
 
int main()
{
 
  #define ANCHOPANTALLA 320
  #define ALTOPANTALLA 200
  #define MAXLADODIANA 50
 
  int 
      posXraton = 160,
      posYraton = 100,
      posXdiana,
      posYdiana,
      ladoDiana,
      puntos = 0,
      dibujarDiana = 1;
 
  allegro_init();              /* Inicializamos Allegro */
  install_keyboard();
  install_timer();
  install_mouse();
 
                               /* Intentamos entrar a modo grafico */
  if (set_gfx_mode(GFX_SAFE, ANCHOPANTALLA, ALTOPANTALLA, 0, 0) != 0) {
      set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
      allegro_message(
          "Incapaz de entrar a modo grafico\n%s\n",
          allegro_error);
      return 1;
  }
 
  /* ----------------------- Si todo ha ido bien: empezamos */
 
  srand(time(0));
  show_mouse(screen);
 
 
            /* Parte repetitiva: */
  do {
      rest(50); /*Pausa de 50 ms */
 
      if (dibujarDiana) {
          /*Calculo nueva posicion de la diana */
          posXdiana = rand() % (ANCHOPANTALLA - ladoDiana);
          posYdiana = rand() % (ALTOPANTALLA - ladoDiana);
          ladoDiana = (rand() % MAXLADODIANA) + 2;
 
          /* Oculto raton y redibujo */
          scare_mouse();
          clear_bitmap(screen);
          rectfill(screen, 
              posXdiana, posYdiana, posXdiana+ladoDiana, posYdiana+ladoDiana,
              palette_color[14]);
          textprintf(screen, font, 4,4, palette_color[13], 
              "Puntos: %d", puntos); 
 
          /*Vuelvo a mostrar ratony marco como dibujado */    
          unscare_mouse();
          dibujarDiana = 0;
      }
 
 
    // Si se pulsa el botn, compruebo si es dentro del recuadro.
    // Si es as, aumento puntos. En cualquier caso,dibujo nueva diana
 
    if (mouse_b & 1) {
 
        if ((mouse_x >= posXdiana) && (mouse_x <= posXdiana+ladoDiana) &&
               (mouse_y >= posYdiana) && (mouse_y <= posYdiana+ladoDiana)) {
            puntos ++;
        }
 
        dibujarDiana = 1;              
    }
 
  }
  while ( !keypressed() );  /* Repetimos hasta pulsar tecla */
 
 
  return 0;
 
}
 
                     /* Termino con la "macro" que me pide Allegro */
END_OF_MAIN();