16. Agrupando datos: structs

Cada enemigo tiene su coordenada X, su coordenada Y, su velocidad (incremento de X)... pero estos datos son parte de 3 arrays distintos. Parece más natural pensar en el enemigo como un conjunto de esas 3 cosas (y, de paso, alguna más, como su carácter representativo en pantalla), que guardaremos en un "struct". Como los obstáculos, premios y enemigos comparten muchas cosas con un enemigo, podemos crear un tipo de datos "Elemento gráfico" que nos permita guardar las características que nos puedan llegar a interesar de cualquier de ellos:

struct ElemGrafico {
    public float x;
    public float y;
    public int xInicial;
    public int yInicial;
    public float incrX;
    public float incrY;
    public char simbolo;
    public bool visible;
}

Así, el "personaje" sería un "ElemGrafico":

ElemGrafico personaje;

Y para los enemigos tendríamos un "array de structs":

int numEnemigos = 10;
ElemGrafico[] enemigos = new ElemGrafico[numEnemigos];

De igual modo, tendríamos otro array de "elementos gráficos" para los obstáculos y otro para los premios. Cualquiera de ellos lo recorreríamos con un "for" para dar sus valores iniciales:

for (int i=0; i<numEnemigos; i++)
{
    enemigos[i].x = generador.Next(0,80);
    enemigos[i].y = generador.Next(1,24);
    enemigos[i].incrX = 0.5f;
    enemigos[i].simbolo = '@';
}

Y también para dibujar sus elementos:

for (int i=0; i<numEnemigos; i++)
{
    Console.SetCursorPosition(
        (int) enemigos[i].x, (int) enemigos[i].y);
    Console.Write( enemigos[i].simbolo );
}

O para comprobar colisiones:

for (int i=0; i<numEnemigos; i++)
{
    if (( (int) enemigos[i].x == personaje.x)
            && ( (int) enemigos[i].y == personaje.y))
    {
        vidas --;
        if (vidas == 0)
            fin=true;
        personaje.x = personaje.xInicial;
        personaje.y = personaje.yInicial;
    }
}

El programa completo no tiene muchos más cambios que esos:

// Primer mini-esqueleto de juego en modo texto
// Versión "k"
 
using System;
using System.Threading; // Para Thread.Sleep
 
public class Juego03k
{
    struct ElemGrafico {
        public float x;
        public float y;
        public int xInicial;
        public int yInicial;
        public float incrX;
        public float incrY;
        public char simbolo;
        public bool visible;
    }
 
    public static void Main()
    {
        ElemGrafico personaje;
        personaje.xInicial = 40;
        personaje.yInicial = 12;
        personaje.x = personaje.xInicial;
        personaje.y = personaje.yInicial;
        personaje.simbolo = 'A';
 
        int numPremios = 10, numEnemigos = 10, numObstaculos = 20;
 
        // Reservo espacio para los datos repetitivos
        ElemGrafico[] obstaculos = new ElemGrafico[numObstaculos];
        ElemGrafico[] enemigos = new ElemGrafico[numEnemigos];
        ElemGrafico[] premios = new ElemGrafico[numPremios];
 
        int vidas = 3;
        int puntos = 0;
 
        bool fin = false;
        ConsoleKeyInfo tecla;  // Tecla pulsada
 
        // Genero las posiciones de los elementos al azar
        Random generador = new Random();
        for (int i=0; i<numObstaculos; i++)  // Obstaculos
        {
            obstaculos[i].x = generador.Next(0,80);
            obstaculos[i].y = generador.Next(1,24);
            obstaculos[i].simbolo = 'o';
        }
 
        for (int i=0; i<numEnemigos; i++)  // Enemigos
        {
            enemigos[i].x = generador.Next(0,80);
            enemigos[i].y = generador.Next(1,24);
            enemigos[i].incrX = 0.5f;
            enemigos[i].simbolo = '@';
        }
 
        for (int i=0; i<numPremios; i++)  // Premios
        {
            premios[i].x = generador.Next(0,80);
            premios[i].y = generador.Next(1,24);
            premios[i].simbolo = '/';
            premios[i].visible = true;
        }
 
 
 
        // ------ Bucle de juego ------
        while( ! fin  )
        {
            // -- Dibujar --
            Console.Clear();
 
            // Marcador
            Console.Write("Vidas: {0}  -  Puntos {1}", 
                vidas, puntos);
 
            for (int i=0; i<numObstaculos; i++)  // Obstáculos
            {
                Console.SetCursorPosition(
                    (int) obstaculos[i].x, (int) obstaculos[i].y);
                Console.Write( obstaculos[i].simbolo );
            }
 
            for (int i=0; i<numEnemigos; i++)  // Enemigos
            {
                Console.SetCursorPosition(
                    (int) enemigos[i].x, (int) enemigos[i].y);
                Console.Write( enemigos[i].simbolo );
            }
 
            for (int i=0; i<numPremios; i++)  // Premios
            {
                if (premios[i].visible)
                {
                    Console.SetCursorPosition(
                        (int) premios[i].x, (int) premios[i].y);
                    Console.Write( premios[i].simbolo );
                }
            }
 
            Console.SetCursorPosition(
                (int) personaje.x, (int) personaje.y);
            Console.Write( personaje.simbolo );
 
 
            // -- Leer teclas y calcular nueva posición --
            if (Console.KeyAvailable)
            {
                tecla = Console.ReadKey(false);
 
                if(tecla.Key == ConsoleKey.RightArrow) personaje.x++;
                if(tecla.Key == ConsoleKey.LeftArrow) personaje.x--;
                if(tecla.Key == ConsoleKey.DownArrow) personaje.y++;
                if(tecla.Key == ConsoleKey.UpArrow) personaje.y--;
            }
 
            // -- Mover enemigos, entorno --
            for (int i=0; i<numEnemigos; i++)  // Enemigos
            {
                enemigos[i].x = enemigos[i].x + enemigos[i].incrX;
                if (( (int) enemigos[i].x == 0)
                        || ( (int) enemigos[i].x == 79))
                    enemigos[i].incrX = - enemigos[i].incrX;
            }
 
            // -- Colisiones, perder vidas, etc --
            for (int i=0; i<numObstaculos; i++)  // Obstáculos
            {
                if ((obstaculos[i].x == personaje.x)  
                    && (obstaculos[i].y == personaje.y))
                {
                    vidas --;
                    if (vidas == 0)
                        fin=true;
                    personaje.x = personaje.xInicial;
                    personaje.y = personaje.yInicial;
                }
            }
 
            for (int i=0; i<numPremios; i++)  // Obstáculos
            {
                if ((premios[i].x == personaje.x)  
                    && (premios[i].y == personaje.y)
                    && premios[i].visible )
                {
                    puntos += 10;
                    premios[i].visible = false;
                }
            }
 
            for (int i=0; i<numEnemigos; i++)  // Enemigos
            {
                if (( (int) enemigos[i].x == personaje.x)
                        && ( (int) enemigos[i].y == personaje.y))
                {
                    vidas --;
                    if (vidas == 0)
                        fin=true;
                    personaje.x = personaje.xInicial;
                    personaje.y = personaje.yInicial;
                }
            }
 
            // -- Pausa hasta el siguiente "fotograma" del juego --
            Thread.Sleep(40);
        }
    }
}
 

Ejercicio propuesto: Haz que el personaje no se mueva de 1 en 1 haciendo "x++", sino usando el valor de su campo "incrX".