Quisiera comenzar esta serie de
ejemplos con uno de los videojuegos más conocidos del mundo. El clásico PONG, creado por Allan Alcorn para la compañía Atari en 1972.
Voy a demostrar a continuación como hacer
una versión de este juego.
El lenguaje utilizado en esta ocasión es C,
simple y puro, sin gráficos ni complejidades.
La pelota y las paletas serán representadas por caracteres ASCII.
La pelota y las paletas serán representadas por caracteres ASCII.
Primero presento el código completo:
Ahora pasamos a explicar todo el
código en detalle, función por función.
En principio utilizamos 3 librerías:
stdio.h - la librería estándar de C
para funciones de input/output
time.h – esta librería la
necesitamos para la función clock()
conio.h - utilizamos esta librería
para tener acceso a las siguientes funciones:
-kbhit(): devuelve verdadero si una
tecla es presionada
-getch(): devuelve un valor ingresado
por teclado sin buffer ni echo
-clrscr(): limpia la pantalla
-gotoxy(int x, int y): mueve el cursor
a la posición indicada. Recibe 2 ints como parámetro.
La librería conio.h no es parte del
ANSI C sino que es una librería de Borland que de cualquier manera
es muy común y esta incluida en muchos compiladores.
Si queremos compilar el ejemplo en un
sistema UNIX/Linux podemos encontrar funciones parecidas en la
librería ncurses.h. En este ejemplo no vamos a cubrir esas
funciones.
Vamos a empezar a analizar el código
desde afuera hacia adentro.
La primera función es sleep(), cuya declaración es void sleep (float).
A continuación la función completa:
Esta función puede parecer un poco complicada.
Esta construida para demorar la
ejecución del loop del juego. Para esto recibe como parámetro un
valor de tipo float que será el que determine la velocidad.
Para poder utilizarla lo que tenemos
que saber es que al llamarla le pasamos n segundos
y realiza una pausa de ese largo.
Si con eso es
suficiente pueden pasar a la función siguiente... sino la
explicación completa:
clock() devuelve la
cantidad de pulsos de reloj transcurridos desde el inicio del
programa.
Lo que
hacemos es multiplicar el valor que le pasamos a sleep() en segundos
por la constante CLOCKS_PER_SEC para pasarlo a pulsos de reloj, esto
se lo sumamos al valor devuelto por clock() para establecer un punto
de referencia y lo guardamos en la variable goal
de tipo de dato clock_t.
A
continuación haceoms un bucle while vacío para esperar que el valor
devuelto por las llamadas a clock() alcance el valor de referencia
goal.
Ahora continuamos con una función
muy sencilla que es la que utilizaremos para mostrar los puntos, void
muestroPuntos();
Sencillísimo,
las variables Buenas y
Malas
son las que tienen almacenados los puntos. Esta función los imprime
en pantalla en la posición indicada por gotoxy().
La
próxima función es void
leoTecla();
que es la que utilizamos para leer los caracteres ingresados por
teclado.
Lo
primero que vemos es que la función kbhit() devuelve verdadero si se
presionó una tecla. En ese caso el valor ingresado se almacena en
tecla.
El
switch determina si tecla
es
equivalente al código para la flecha de abajo del teclado (numero 72,
almacenado en la constante TECLA_ABA declarada al comienzo del
programa) y en ese caso incrementa el valor de posPaleta
que
determina la posición de la paleta sobre el eje Y.
Lo mismo ocurre para la tecla de la flecha hacia arriba.
El
ultimo case corre si la tecla presionada fue 'e', entonces llama a
exit()
para salir del programa y le pasa 0 como parámetro (0 indica que el
programa termino sin errores).
Pasamos
a int
chequearColision(int donde);
que nos sirve para hacer rebotar la pelota contra la paleta y contar
los puntos.
Cada
uno de los dos grandes bloques if
son para cada paleta. Lo que hacen es comprobar primero si la
posición de la pelota es igual a la de cualquiera de las partes de
la paleta determinadas por donde.
En
ese caso cambia la dirección de la pelota. Por ejemplo: si flagIzq
es igual a 1 eso significa que la pelota esta yendo hacia la
izquierda y entonces modifica el valor de flagIzq
por
0 y el de flagDer
por 1 para cambiar la trayectoria, caso contrario invierte
los valores. Esto mismo para las dos paletas. Si el rebote es hacia
adentro de la cancha entonces el valor de Buenas
es incrementado en 1.
Seguimos
con void
dibujoPaletas(int);
que como su nombre lo indica imprime las paletas en pantalla.
Antes
que nada esta función recibe como parámetro un int
que será la posición de la paleta sobre el eje Y.
char
x almacena
el valor ASCII del caracter utilizado para representar la paleta.
El
bucle for
imprime en pantalla dos paletas de 3 caracteres de longitud, una del
lado izquierdo y otra del lado derecho de la pantalla.
Ahora
que tenemos armadas todas las funciones que necesitamos vamos a poner
la pelota a rebotar por la pantalla y combinarlas todas en la función
void pelota().
Podemos
dividir esta parte del código en 4 grandes bucles while.
Cada uno representa una de las 4 posibles trayectorias de la pelota
(arriba-izquierda, arriba-derecha, abajo-izquierda, abajo-derecha)
representadas por las 4 variables flarArr,
flagAba,
fladIzq
y flagDer
y que tendrán un valor de 1 si están encendidas o 0 si están
apagadas.
Tomamos
el primer bucle while.
Primero limpia la pantalla con clrscr(). Luego actualiza de acuerdo
al rumbo establecido el valor de pelotaX
y pelotaY
que es donde almacenamos la posición de la pelota, entonces va a la
posición con gotoxy(pelotaX, pelotaY) e imprime en pantalla
pelotaChar
que contiene el caracter ASCII que utilizamos para la pelota (se lo
asignamos en la declaración al comienzo del programa).
Después de imprimir en pantalla la pelota llamamos a la función leoTecla()
para comprobar si el jugador movió las paletas, dibujamos entonces
las paletas con la posición actualizada llamando a
dibujoPaletas(). Comprobamos
si la pelota rebota en la paleta, para eso llamamos a
chequearColision(posPaleta)
y le pasamos la posición de las paletas sobre el eje Y.
Finalmente
llamamos a muestroPuntos()
y después a sleep(
(float) velo)
donde velo
es el valor que fijamos en la declaración de dicha variable para
demorar entre una vuelta del juego y la siguiente.
Los
dos bloques if
que hay antes de cerrar el while
comprueban si la posición de la pelota alcanzó alguna de las cuatro
paredes de la cancha y en ese caso realizan las modificaciones de
valores necesarias en las variables de dirección para cambiar la
trayectoria. Si alguna de las paredes laterales es alcanzada se
incrementa el valor de Malas
en 1.
En
cada vuelta del loop del juego se ejecuta el bucle while
correspondiente a la dirección de la pelota en esa vuelta.
Por ultimo la función principal:
Este
bucle es el que mantiene la pelota en movimiento todo el tiempo y el
programa ejecutándose. Al pasarle el valor 1
al while
lo que hacemos es producir un loop infinito ya que le pasamos un
estado que no se modifica nunca.
Esto es todo en lo que respecta al funcionamiento de este programa.
He tratado de mantenerlo lo mas sencillo posible para que quede
expuesto el funcionamiento básico del juego. Como siempre, existen
otras formas de resolver los problemas planteados por el programa.
Espero que la forma elegida en este ejemplo haya resultado clara.
Link para descargar el código:
https://dl.dropbox.com/u/103165598/PALETAS.C?dl=1
No hay comentarios:
Los comentarios nuevos no están permitidos.