37. Descomposición en clases del juego en modo gráfico.

Nuestro "arkanoid" en modo consola ha pasado de ocupar 120 líneas, cuando era un único fuente, a casi 300 línwas cuando lo hemos descompuesto en clases.

Esto suele ocurrir. Descomponer en clases un programa existente permite obtener fuentes indiviuales más pequeños, y, por tanto, más legibles y con una misión más clara. También permite repartir el trabajo entre grupos de programadores, para que sea más fácil desarrollar en paralelo. Pero no es una tarea fácil, y es habitual que el resultado "sea más grande" que el original (en cuanto a "número de líneas totales de programa").

Descomponer nuestro juego en modo gráfico va a ser bastante más trabajoso, porque era un proyecto de un tamaño mucho mayor que el "frontón". A cambio, a partir de este momento será más fácil ampliarlo y localizar fallos, así que vamos a hacerlo...

Esta vez podríamos pensar en clases como Descomponer nuestro juego en modo gráfico va a ser bastante más trabajoso, porque era un proyecto de un tamaño mucho mayor que el "frontón". A cambio, a partir de este momento será más fácil ampliarlo y localizar fallos, así que vamos a hacerlo...


Por una parte, podríamos distinguir la clase "Juego", que engloba a todo el proyecto, de una clase "Partida", que sería una sesión concreta de juego. El "Juego" podría contener un menú inicial, una pantalla de ayuda, otra de créditos (información sobre los desarrolladores). La "Partida" necesitaría un personaje, varios enemigos, varios premios, varios obstáculos, y tendremos que recorrer una serie de habitaciones, que podríamos llamar "niveles" de juego. Más adelante añadiremos posibilidades adicionales, como que nuestro personaje pueda saltar, o caer desde las alturas, o disparar... pero de momento, vamos a comenzar "imitando" las funcionalidades de la versión anterior del juego.

El diagrama de clases, sin detallar apenas métodos, y sin incluir otras clases que es posible que nuestro juego necesite más adelante, podría ser:

FreddyTwo, clases

El fuente se va a convertir en 15 clases, con más de 1.500 líneas de código (muchas de las cuales serán comentarios, claro), así que no tiene sentido verlo entero, pero sí algún fragmento.

Por ejemplo, un "Elemento gráfico" ya no será un struct, sino una clase, en la que los campos del struct se convertirán en atributos, y además tendrá algún método, como los que permitan dibujarlo en pantalla o comprobar si hay colisión con otro elemento gráfico:

public class ElemGrafico
{
    // Atributos
 
    protected bool visible;
    protected int x;
    protected int y;
    // ...
 
    // Constructor: Carga la imagen que representara a este elemento grafico
    // Ancho y alto prefijados
    public ElemGrafico(string nombre)
    {
        imagen = new Imagen(nombre);
        visible = true;
        ancho = 32;
        alto = 32;
    }
 
    // Constructor: Carga la imagen que representara a este elemento grafico
    // Ancho y alto indicados por el usuario
    public ElemGrafico(string nombre, int nuevoAncho, int nuevoAlto)
    {
        imagen = new Imagen(nombre);
        visible = true;
        ancho = nuevoAncho;
        alto = nuevoAlto;
    }
 
 
    // Dibuja el elemento gráfico en sus coordenadas actuales  
    public  void DibujarOculta()
    {
        if (visible)
            imagen.DibujarOculta(x, y);
    }
 
    //...

Y un "Enemigo" será un tipo de "Elemento gráfico", que además será capaz de moverse por sí mismo (calcular la siguiente posición de su movimiento):

public class Enemigo : ElemGrafico
{
    public Enemigo()
        : base("enemigo.png", 36, 42)
    {
        incrX = 5;
    }
 
    //  Mover el personaje a su siguiente posición (movimiento automático)
    public  void Mover()
    {
        if (x < 50)
            incrX = 5;
 
        if (x > 700)
            incrX = -5;
 
        x += incrX;
    }
} 

Lo que antes eran las funciones "MostrarPresentacion", "MostrarAyuda" y "MostrarCreditos" ahora se convertirán en el método "Mostrar" de cada una de esas clases. La clase "Partida" absorberá toda la lógica de una sesión de juego, que ahora se verá simplificada porque podemos delegar en clases auxiliares. Por ejemplo, el método "Dibujar", que antes ocupaba 42 líneas, ahora se convertirá en:

public void Dibujar()
{
    Hardware.BorrarPantallaOculta(0, 0, 0);
    miMarcador.DibujarOculta();
      // Elementos del nivel: fondo, premios y enemigos
    miNivel.DibujarOculta();
    miPersonaje.DibujarOculta();
    Hardware.VisualizarOculta();
}

Puedes descargar el juego completo, incluyendo fuentes, proyecto, imágenes y ejecutable, para probar los cambios por ti mismo.