04 octubre 2007

Liberando y rompiendo

Uno de los videojuegos más viejos es uno llamado Break-Out (traducción: ¿rompiendo afuera?) y es un ejercicio interesante para poderlo programar usando Visual Basic 2005. En este ejemplo, voy a hacer un programa que se vea así:


Deja te platico como está armado esta onda: en la parte superior de la ventana, tengo 20 PictureBoxes de colores (azul, verde, amarillo y rojo), hay otro PictureBox pequeño con la imagen de una pelotita (bueno, intenté que se pareciera un poco) y hasta abajo hay otro PictureBox que se va a poder mover de manera horizontal. La idea es que la pelota viaja por la ventana y al toparse con algún bloque de color, desaparece el cuadro y rebota en dirección contraria. Lo mismo sucede cuando la pelota toca alguna orilla de la ventana. El jugador controla con su mouse el PictureBox que está abajo (solo se mueve horizontalmente) para evitar que la pelota salga por la parte inferior de la ventana. O sea que el chiste es eliminar todos los bloques de colores y no dejar que la pelota salga de la ventana. Cada vez que la pelota salga por la parte inferior de la pantalla, se pierde una vida y por cada bloque que desaparece, la puntuación aumenta 10 puntos.

De una vez te digo los nombres que le puse a cada uno de estos controles para que luego veas el código y entiendas como está la movida. Estos son los nombres de los controles:
  • Pegador: PictureBox que mueve el usuario con el mouse.
  • Pelota: PictureBox con la imagen de la pelota que rebota por todos lados.
  • Rojo1, Rojo2, Rojo3, Rojo4 y Rojo5: son los PictureBoxes rojos que están hasta arriba.
  • Amarillo1, Amarillo2, ... Amarillo5: PictureBoxes amarillos.
  • Verde1, Verde2, ... Verde5: PictureBoxes verdes.
  • Azul1,... Azul5: PictureBoxes azules.
  • Label1: Etiqueta donde informo al usuario el número de vidas que le quedan.
  • Label2: Etiqueta donde informo al usuario su puntuación.
  • Timer1: Un timer que se encarga de mover a pelota.
Para empezar, dentro de la clase Form1, declaro unas variables. El código se ve así:


intVelX indica el número de pixeles que voy a aumentar para que la pelota se mueva horizontalmente y intVelY indica el número de pixeles que le voy a aumentar para que se desplace de manera vertical. Recuerda que el sistema de coordenadas comienza en la esquina superior izquierda. A veces esto se confunde a la hora del desplazamiento horizontal: la coordenada 0,0 está en esa esquina, así que para que la pelota viaje hacia arriba, le tengo que restar valor.

intScore acumula la punturación, intVidas tiene el número de pelotas que todavía le quedan al jugador y intSinBolas me va a servir para saber cuando se eliminaron todos los bloques (para decirle al usuario que ya ganó).

En este ejemplo, el juego comienza en cuanto se abre la ventana. Para esto, solo tengo que habilitar Timer1 para que empiece la función. Si quieres ponerle un botón para que arranque el juego, escribe este código en el evento Click del botón.


Ahora vamos a hablar del código que nos mueve todo el asunto (bueno, solo nos mueve la pelota). Es el evento Tick del Timer1 que se ejecutará cada X número de milisegunos (que definí en las propiedades del Timer en la vista de diseño de Form1). Primero asigno a mi propiedad SinBolas (más adelante explico como funciona la propiedad), la cual es interfaz para accesar la variable intSinBolas, en 0. Luego llamo a una función que se encarga de ver si la pelota chocó con algo para rebotarlo, acumular puntos, etc. Ese lo explico más adelantito. Al llamar a esta función, altera el valor de SinBolas. Si devuelve un valor de 1, quiere decir que ya no hay bloques en la pantalla y que ganó. Si este es el caso, para el Timer1 y le manda una mensaje para decirle que ganó.

Si no se han acabado los bloques, aumento la coordenada X de Pelota para que se mueva por la pantalla (para eso uso una propiedad PelotaX que accesa a Pelota.Left). Después de moverlo, tengo la condición que se encarga de ver si la pelota todavía está dentro de la ventana. Si ya llegó a una orilla de la ventana (o sea, la posición X de Pelota es menor a 0 o mayor al ancho, Width, de la ventana), invierte la cantidad que se le va a sumar a la pelota y esto causa que rebote ne sentido contrario al que iba.

Después de encargarme del desplazamiento horizontal, le sumo a la posición Y (propiedad PelotaY que es la interfaz de Pelota.Top) para que se mueva en sentido vertical. La siguiente condición veo si Pelota no ha salido por la parte inferior de la pantalla (o sea que el jugador no pudo poner a Pegador en el lugar correcto para rebotar la pelota hacia otro lado). Si salió por la parte inferior, para el timer, le quita una vida, le indica al jugador el número de vidas que le quedan, y vuelve a posicionar la pelota para volver a jugar. En caso que ya se le terminaron las vidas, le manda mensaje que se la pe¡nó de rayuela en medio; si no, le dice que falló y vuelve a arrancar el timer. Aquí está el código:


Ya estuvo lo complicado. Ahora voy a mostrar como se mueve Pegador. Voy a programar la movida del Pegador en el evento MouseMove del Form1. Una nota: si no encuentras donde se pone el código de esta onda, abre la ventana de código de la forma y selecciona en el combobox de la izquierda Form1 events y luego del de la derecha, elige MouseMove. ¡Listo! La función MouseMove recibe de manera cuasimágica un parámetro llamado e, la cual tiene datos acerca del estado del mouse. En este caso, e.X dice en qué coordenada X está el cursor del mouse en ese momento. Entonces, agarro ese valor y acomodo a Pegador de acuerdo a eso (hago que la posición del cursor en X esté a la mitad de Pegador). Aquí está el código:


Una función que había mencionado cuando expliqué el código del Timer1_Tick, es una que se encarga de ver si Pelota chocó (lo que se llama elegantemente "colisionó") con Pegador o con cualquier otro bloque de color. Para hacer esto, hice una función, llamada RevisaColisiones, que se encarga de ello. , a su vez, llama a dos versiones diferentes de otra función, RevisaColision, que revisa si la pelota está dentro de un PictureBox y desaparecerlo, si es necesario. Aquí se ve como funciona RevisaColisiones, y más adelante vamos por RevisaColision.


Si te fijas, RevisaColisiones no tiene nada de especial. Como que es el gerente que hace que RevisaColision haga todo el trabajo pesado, así que vamos directo a ver como funciona.

En primer lugar, hay dos versiones de RevisaColision. Estoy aprovechando la característica de la programación orientada a objetos (OOP) llamada polimorfismo. No ahondo en estos conceptos, pero si tienes dudas, visita los links de este párrafo para que veas que onda.

La primera versión de RevisaColision recibe dos parámetros: Imagen que es el PictureBox que quiero revisar (o sea, ver que Pelota no esté "adentro" de él) y Oculta que es booleano que tiene un valor de verdadero o falso (más adelante vemos como funciona).

En primer lugar, reviso si Imagen está visible. Si es así, tengo un if grandototote que lo único que hace es revisar las coordenadas de Pelota en relación a Imagen. Si está "adentro", entonces invierte la velocidad vertical, VelY, para que rebote, actualizo la puntuación y, si quiero que desaparezca Imagen (o sea, que Oculta tiene un valor verdadero), entonces lo oculto (para esto uso la propiedad Visible de Imagen). En dado caso que no esté visible, quiere decir que ya lo desaparecimos hace rato, así que no hago nada. Aquí está el código:

La segunda versión, la versión sobrecargada de RevisaColision, se llama igual, pero en lugar de recibir dos parámetros, recibe solo una: el PictureBox que ando revisando. Si te fijas, lo único que hace es llamar a la versión "normal" de RevisaColision, con el parámetro Oculta=True. Me conviene hacerlo así porque en el único caso en que quiero que no desaparezca el PictureBox es cuando estoy revisando a Pegador. En todos los demás casos, o sea, con los bloques rojos, amarillos, verdes y azules, quiero que desaparezcan. Aquí está el código de las dos versiones de RevisaColision:


Aunque no era del todo necesario, hice propiedades para accesar diferentes variables que uso a cada rato. Básicamente una propiedad, o Property para escucharnos bien pseudoprogramadores apantalladores, es un intermediario entre una variable y el programa. Tiene definida dos partes que se pueden programar: Get que dice qué valor regresar y Set que dice cómo se va a asignar un valor. En mi caso, no estoy haciendo nada especial más que demostrando la sintáxis de una Property. A lo mejor luego escribo ejemplos con propiedades más interesantes...

Volviendo a la realidad, hice una propiedad PelotaX y PelotaY que devuelven los valores de Pelota.Left y Pelota.Top respectivamente. Aquí está el código:


Ya que me emocioné con esto de las propiedades, también hice propiedades que controlan las variables globales que mencioné al principio de todo este rollo. Aquí están las propiedades que sirven de interfaz para la velocidad horizontal, vertical y la puntuación:


Éstas son las últimas propiedades que hice. Éstas se encargan de el número de vidas y ver si quedan bloques en la ventana:


¡Listo! Con esto creo que tendrás un jueguito para que te entretengas unos 5 minutos en jugar antes de aburrirte, jeje. Pero demuestra el uso de propiedades, del polimorfismo y una que otra idea rara. Si tienes dudas, déjame un comentario y intentaré contactarme contigo en cuanto pueda. ¡Hasta la próxima!
Publicar un comentario
Related Posts Plugin for WordPress, Blogger...