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".