sábado, 3 de diciembre de 2011

[Culebrilla] Diseño general

Después de un lapso de tiempo volvemos a la carga con el taller. Ya hemos visto cierta teoría para acometer nuestro pequeño proyecto y ya va siendo hora para ponernos a hacer el juego. Tenemos la idea de cómo va a ser el juego, por lo tanto ahora nos toca diseñar "las tripas" del juego. Como vamos a usar C++ en modo orientado a objetos (mayormente) vamos a empezar modelando las clases que necesitaremos. Repasemos el cutremontaje que apareció en la entrada de la presentación del proyecto:



Bien, tenemos varios elementos en el juego:
  • El fondo, que comprende el fondo gráfico (las típicas nubes creadas con el filtro del editor gráfico) y el borde de las cajas marrones.
  • La culebrilla en sí, formada por varios círculos enganchados unos a otros
  • La comida de la culebrilla (lo que se conoce como un "item")
  • El marcador con los segundos que lleva el jugador y el record de tiempo
Aparte de estos elementos que podemos sacar de la captura tenemos que tener en cuenta el texto que mostraremos cuando el juego esté en estado "Game Over". Además necesitamos una clase que sirva de "soporte" o "contenedor" de las clases que conforman el juego.

Dicho esto podemos modelar el juego con las siguientes clases. Como siempre esta no es ni la única ni la mejor forma de hacerlo, simplemente es mi aproximación. A la hora de modelar un programa virtualmente tenemos infinidad de formas de hacerlo. El nivel de sofisticación, granularidad etc depende del que lo haga, del tiempo que dispone, de las exigencias de ampliación, si tiene en cuenta la mantenibilidad del código, etc etc. En nuestro caso esto nos bastará:
  • El fondo lo representaremos con la clase Background. Se encargará de pintar el fondo, los bordes y además calculará si la caja que le pasamos colisiona con el borde o no (ya veremos esto más adelante)
  • La culebrilla la representaremos con la clase Snake. Se encargará de pintar la culebrilla (con el número de círculos que toque), de comprobar si colisiona consigo misma, de calcular si colisiona con una caja que le pasemos y de proveer la caja de su cabeza
  • La comida la modelamos con la clase Item. Esta clase se encargará de buscar un sitio en el área de juego válido para aparecer (que no colisione con la culebrilla), de desaparecer si colisiona con la caja que le proporcionemos y de aparecer otra vez en un sitio válido
  • Para el marcador tenemos la clase HUD (Heads-Up Display), que se encargará de mostrar los datos correspondientes
  • Para mostrar el texto de "Game Over" no usaremos ninguna clase, simplemente mostraremos el texto correspondiente en este estado
  • La clase "contenedora" de las anteriores la llamaremos Game. Tendrá las instancias necesarias de las clases expuestas y se encargará de gestionar el juego
Ya hemos visto las clases, ¿Ahora qué? ¿Cómo funciona todo esto? Empecemos por el nivel de abstracción más alto, que es la clase Game. De esta clase sólo creamos una instancia en la función main() del programa, donde está el bucle principal. En este bucle comprobamos si el usuario ha pulsado alguna tecla y si es así se lo indicamos a nuestra instancia de Game, que se encarga de responder a estos eventos. También en el bucle principal llamamos al método update() de Game para que éste actualice su estado y posteriormente a draw() para mostrar todo en pantalla. Con esto ya casi que tenemos hecho el fichero main.cpp.

Dentro de Game tenemos las instancias de las clases Background, Snake, Item y HUD, una de cada una. En el método update() de Game está todo el juego: con un switch comprobamos en qué estado estamos, si en "Game Over" o jugando. Si es el primero mostramos el texto con el record, si es el segundo hay que gestionar las diferentes instancias: primero llamamos al update() de la instancia de la clase Snake e Item. Cuando llamamos al update() de Snake tenemos que preguntarle en Game si la cosa sigue o es Game Over, ya que puede haber una colisión consigo misma. Una vez hecho esto tenemos que comprobar si la culebra ha colisionado con el borde del área y si ha colisionado con la comida (si está visible).

Para comprobar si ha colisionado con el borde le pedimos a la instancia de la clase Snake la caja de la cabeza, que está en coordenadas de mundo. Esta caja se la pasamos a la instancia de Background, que nos indicará si colisiona con el borde o no. Si colisiona es Game Over, sino seguimos con los cálculos. Con comprobar la colisión entre la cabeza de la culebrilla y el borde es suficiente, no hace falta comprobar todo el cuerpo de la culebrilla. Esto es así porque por la forma en que se mueve si hay colisión siempre será la cabeza la primera en colisionar.

Si la instancia de Item está en estado "comible", le pasamos la caja de la cabeza de Snake. Si colisiona indicamos a Snake que efectivamente ha comido una pieza más, sino no hacemos nada. También en caso de colisión hay que indicar a Item que ha sido comido y que actúe en consecuencia. En este caso la instancia Item tendrá que buscar un nuevo sitio en el área de juego en función del cuerpo de la culebrilla.

Y eso es básicamente todo lo que tiene que hacer la clase Game. Quizás hemos visto demasiados conceptos nuevos de golpe en esta entrega, pero cuando veamos el código fuente todo quedará bien claro.

¡Hasta la próxima entrega!

No hay comentarios:

Publicar un comentario