11. Rompe ladrillos 2D paso a paso |
![]() |
![]() |
Videojuegos - Curso de Programación de juegos | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Escrito por Vicengetorix | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]() Haremos paso a paso, el tipico juego de romper ladrillos (Arkanoid), solo usando 2D, para comprobar de lo que somos capaces y el resultado nos de animos para seguir. Aviso: el tema sera larguito, pero satisfactorio. Ademas, he comprobado en foros que hay bastante interes en hacer juegos 2D y, tal vez, quien siga el curso no vea claro como hacerlo con lo que hemos visto. Comprobaremos como para hacer un juego 2D no hace falta exprimir el OpenGL. Simplemente inicializaremos, cargaremos los graficos, usaremos solo proyeccion ortogonal, y plantaremos los graficos en pantalla donde nos convenga. El resto de la programacion no es especifica de OpenGL, es simplemente C (C++ lo usaremos poco o nada de momento). Aviso que el codigo del juego no es el mas estructurado del mundo ni el mas optimizado, simplemente funciona y esta muy comentado para que quien quiera seguirlo no se pierda y sepa en cada momento que hace cada linea de codigo y cada variable. Antes de entrar a ver el codigo vamos a ver conceptos que usaremos para el juego y que luego veremos como se implementan. Primero donde guardamos los datos del muro, las posiciones de los ladrillos. Lo guardaremos en un simple array con tantas posiciones como posibles ladrillos habra en el muro. Un ejemplo. En un muro hubiera 4 filas de posibles ladrillos y en cada fila hay 10 ladrillos, el array sera de 40 posiciones (4*10), si fueran 10 filas serian 100 posiciones ( 10*10) y asi sucesivamente. Una vez que decidimos cual es el maximo de filas de ladrillos, definimos el array. A la hora de pintar el muro calcularemos las coordenadas en base a la posicion en el array. El tipo del array sera un simple int y el significado sera simplemente que un 0 sera que no hay ladrillo en esa posicion (un hueco en el muro) y otro numero correspondera al tipo (en nuestro programa solo cambia el color) de ladrillo a pintar. Todos los niveles (muros) estaran definidos en un array de 2 dimensiones en el que la primera sera la pantalla o nivel y la segunda dimension las posiciones de los ladrillos de cada nivel. De este array se copiara cada nivel a otro array de trabajo, de 1 sola dimension, cuando empiece el juego (nivel 0) o cada vez que cambiemos de pantalla. Otro concepto es el del movimiento de la bola. Lo haremos sumando a la posicion de la bola un vector de movimiento que calcularemos en base al angulo de saque y que luego cambiaremos con los rebotes simplemente cambiando el signo de la componente del vector de movimiento del lado que choca (Ejemplo: si choca con el borde derecho o izquierdo de pantalla cambiamos de signo la componente X del vector). Otro tema muuuuy importante es el de los choques. En este caso tendremos que controlar el choque de la bola con la paleta y con los ladrillos del muro. El tema de la deteccion de choques es un tema peliagudo en los juegos. Nosotros no nos vamos a complicar. Lo que haremos sera comprobar en el eje X, si la bola esta dentro del rango de coordenadas del ladrillo (o la paleta); si es asi haremos la misma comprobacion en el eje Y. Si las dos comprobaciones son positivas es que hay choque. Para comprobar si el choque es contra un lateral del ladrillo o por arriba o abajo (para rebotar en un eje o en otro) lo que haremos sera hacer la misma comprobacion de antes pero restando el vector de movimiento a la pelota (la posicion en que estaba antes). Si en la posicion inmediatamente anterior estaba fuera del rango del ladrillo en el eje X, es que el choque es contra un lado vertical y cambiaremos de signo la componente X del vector de movimiento. Haremos lo mismo con la componente Y en cuyo caso el choque habra sido contra en lado horizontal del ladrillo y cambimos de signo la componente Y del vercor de movimiento de la pelota. Los graficos que usaremos son sencillos. Yo no soy muy habil en esa materia pero en la red hay muchas cosas interesantes que podemos usar como graficos o programas que con muy poco nos hacen parecer expertos. Tan solo usaremos estos graficos: la bola, la paleta, los ladrillos (5 colores), el fondo, el logo de UnSitioWeb y tres graficos hechos rapidamente con un programa de graficos vectoriales para la partida perdida, ganada y cambio de nivel. Puedes encontrar todos los graficos necesarios en el fichero ZIP descargable al final del capitulo. Contiene tambien el programa compilado y el codigo. Ya nos resta solo empezar con el codigo. Respecto a los capitulos anteriores he quitado buena parte, ya que todo lo relativo a 3D e iluminacion no tiene sentido en este juego. Primero como siempre veremos las definiciones globales. (En esta ocasion completas).
Los comentarios son bastante explicitos. Las funciones GeneraFuente y Escribe no las pondremos ya que no cambian desde el capitulo en que se vieron. En todo caso solo hay que ajustar el tamaño de la fuente, el color y el nombre de la fuente que usamos. Aparece una nueva funcion: CargaTextura que lo unico que hace es cargar una imagen con la libreria Corona y crear la textura correspondiente sin mipmaps. No hay porque repetir 7 veces el codigo por 7 texturas que carguemos.
Luego la funcion GeneraLista cambia y generamos 2 listas, una para la bola y otra para los ladrillos (la paleta sera la misma con otra textura). El metodo se vio en su momento y es exactamente igual asi que no reproducimos el codigo aqui. En la funcion ProcesaMensajes cambiamos un par de cosas. Controlamos el evento de activacion de la ventana para que si minimizamos o cambiamos de ventana no siga el programa moviendose. Para ello en el apartado de definiciones globales habiamos definido el indicador correspondiente.
Y en el evento de destruccion de la ventana debemos destruir todas las texturas que vamos a crear.
La funcion CreaVentana seguira como siempre excepto porque he incorporado un icono a la ventana. Eso no es la materia de este capitulo y puede no hacerse. La funcion WinMain si que tiene alguna cosa nueva. Si nos fijamos en las definiciones globales veremos que definiamos el numero PI, incluiamos el fichero "math.h" y definiamos dos arrays llamados seno y coseno. Si leemos los comentarios quedara clara su funcion. Son para precalcular los senos y cosenos de todos los grados de 0 a 359 y asi tener acceso inmediato a funciones trigonometricas durante la ejecucion del programa. Ahora cargaremos esos arrays con los datos, pasando los grados a radianes, que es lo que aceptan las funciones de "math.h".
Luego cargamos el array de ladrillos que usaremos durante el bucle, tal y como explique antes, con el primer nivel (0) para empezar la partida. Mientras cargo el array, cuento los ladrillos (en la variable NLadrillos) para, mas tarde, controlar cuando hemos terminado con todos en el juego.
La ultima modificacion es para que el juego se pare si la ventana no esta activa. Ya estaba todo preparado en la funcion ProcesaMensajes. Si la ventana no esta activa no ejecuto el bucle principal.
En la funcion IniciaGL solo cambiamos estas cosas. Evitamos que OpenGL tenga encuenta la profundidad ya que nuestro programa es en 2D y nos sirve con que lo ultimo que pintamos se pinte encima. Comentamos la linea que habilita el test de profundidad.
Ponemos proyeccion ortogonal en base al tamaño de la ventana.
Compongo la medida de los ladrillos y la paleta, y pongo la posicion inicial de la paleta.
Tambien cargo las texturas con la funcion que creamos para ello y dejo ya habilitado el blending para la transparencia de los PNG's y dejo habilitadas tambien las texturas (casi todo en este programa tendra textura). Observad como los identificativos de las texturas de los ladrillos se guardan en un array que nos sera util a la hora de seleccionar la textura del ladrillo del muro a partir del array de ladrillos.
Llegado a este punto entramos en el corazon del programa, el bucle principal donde esta la logica del juego. La funcion Pinta() sera el autentico cerebro de nuestro programa. Empezamos como siempre limpiando la pantalla y cargando la matriz identidad en la matriz de modelo (por si acaso)
Lo primero que pintamos es el fondo de la ventana y luego el logo de UnSitioWeb. lo demas ira encima de estas dos cosas.
Luego pinto los ladrillos a partir del array de trabajo que contiene el nivel actual.
Pinto la paleta. Sera un ladrillo con otra textura.
Ahora un bloque de codigo dentro de un if que contempla la situacion de estar en el momento de bola parada y estar apuntando con una rayita a donde vamos a sacar. El bloque empieza asi:
Dentro de este bloque coloco la bola encima de la paleta, de forma que no se mueva sola si no solidaria a la paleta, y dibujo la rayita que marca la direccion del saque en base a la variable angulo.
Escribo la leyenda debajo de la ventana para la situacion que tratamos.
Y para terminar el bloque actual (el if) compruebo las teclas de flecha arriba y flecha abajo que seran las que permitan al jugador cambiar la direccion de saque y modifico la variable angulo en base a ellas. Luego termino el bloque ("}")
El codigo siguiente se ejecutara siempre, en todas las situaciones con juego parado o no. Primero pinto la bola (las coordenadas en este caso son las del centro, la bola es de 16x16 pixels). Escribo en pantallas el numero de vidas que quedan y la pantalla en la que estamos. Luego compruebo las teclas de mover a la derecha y la izquierda la paleta y actuo en consecuencia en cada caso (tambien actualizo la variable de la direccion de la paleta para luego usarla en el choque de bola y paleta).
Nos ocupamos ahora de lo que pasa cuando presiono espacio para sacar.
Toca ver que hacemos cuando presionamos F1 al terminar un nivel. Hay dos posibilidades, que siguamos la partida o que hayamos ganado porque en nivel terminado es el ultimo. Actualizo variables y cargo el array de los ladrillos con los del nivel siguiente (o el primero si empezamos de nuevo por que hemos ganado).
La situacion de haber perdido y dar a F1 sin vidas. Inicializo variables y ladrillos para empezar nueva partida.
El bloque de codigo que viene a continuacion esta dentro de un if que se ejecuta cuando estamos en juego. En este bloque esa el control de la pelota, incluida la deteccio de choques, a mi parecer la parte mas espesa de este capitulo. El bloque empieza asi.
Primero evito que la bola se salga de los bordes de la ventana y que rebote al llegar a uno.
Compruebo si la bola baja mas que la posicion de la paleta. Si fuera asi se nos ha colado la bola y perdemos una vida y paramos el juego.
El siguiente codigo se ocupa de controlar el posible choque de la bola con la paleta. Usaremos el metodo explicado al principio del capitulo. Se complica un poco al poderse mover la paleta en el eje X. En base al movimiento de la paleta en el momento del choque modificaremos el angulo en que rebota la pelota para dar mas interes al juego. Tambien tendremos que tener cuidado ya que si la pelota y la paleta chocan en movimiento con componontes contrarias en el eje X, la comprobacion que hacemos de ver si la posicion de la bola menos el vector de movimiento (el frame anterior) chocaban tambien, ya no sirve. Para este caso concreto usaremos un indicador para ver si se han dado los casos normales. Si no fuera asi modificariamos la posicion de la bola en el eje X tanto como la velocidad de la paleta. Si no observamos este caso, podria la bola atravesar la paleta como un fantasma o si implementamos el resto de forma diferente, hacer raros al chocar en esta situacion.
Despues comprobamos si la bola choca con los ladrillos y "destruimos el ladrillo" con el que si choque. El metodo de calculo del choque es el mismo pero sin la complicacion del movimiento de la paleta. Simplemente comprobaremos si la bola esta en zona de ladrillos y si es asi recorreremos el array que los contiene igual que cuando lo pintabamos. Si encontramos algun ladrillo que choca, lo sustituiremos por un hueco y terminaremos de buscar.
Ya para termimar el if que iniciamos mas arriba solo queda la comprobacion de si hemos acabado con todos los ladrillos y despues mover la bola.
Resta solo contemplar cuando se pintan los carteles de ganar la partida, perder y cambio de nivel. Ganar la partida.
Cambio de nivel.
Perder la partida.
¡POR FIN! Hemos terminado con con el codigo nuevo para nuestro juego 2D rompe ladrillos. Lo que queda de la funcion Pinta(), ya se vio en anteriores capitulos. Es el codigo para visualizar los FPS en pantalla o no y para aumentarlos o disminuirlos. Esta parte del codigo se puede quitar si no se quiere cambiar los FPS pero mientras programas puede ser util que el juego vaya mas rapido o mas lento. Luego cambia el "back buffer" y el "front buffer".
Con esto terminaria la funcion Pinta() y nuestro programa. El juego deberia verse asi. ![]() Espero que despues de esto os dediqueis, como siempre os aconsejo, a modificar el codigo y ver que pasa. El metodo cientifico, el de prueba y error es con el que mas se aprende. El codigo completo es este:  usw11.cpp Tambien podeis bajar un ZIP con el programa compilado y listo para jugar, y que incluye el codigo: Muro. Ahora con vuestro permiso me retirare a descansar que el esfuerzo de comentar el codigo (mas que el de hacer el programa) y explicarlo en este capitulo ha sido agotador. ¡Sólo los usuarios registrados pueden escribir comentarios!
3.26 Copyright (C) 2008 Compojoom.com / Copyright (C) 2007 Alain Georgette / Copyright (C) 2006 Frantisek Hliva. All rights reserved." |