20 febrero 2011

Un juego modular con XNA (Convertir el juego de SDL a XNA - 2)


El esqueleto que hemos creado con XNA sirve para juegos sencillos, pero un único fuente de gran tamaño no es lo más sencillo de mantener. En cuanto la lógica del juego se complica, convendrá desglosar en clases que encapsulen los detalles del comportamiento de nuestro personaje, de nuestro(s) enemigo(s), etc.

Así, podríamos comenzar por crear una clase "Elemento gráfico", similar a la que ya usábamos con SDL (aunque todavía mucho más simple), de la que heredáramos para crear nuestro Personaje, los enemigos, los objetos del fondo, etc. Esta clase podría ser así de momento:


class ElemGrafico
{
protected Texture2D miImagen;
protected int x, y;
protected int incrX, incrY;

public ElemGrafico(string nombreFichero)
{
x = 0; y = 0;
incrX = 0; incrY = 0;
CargarImagen(nombreFichero);
}

/// Mueve el elemento grafico a otra posicion
public void MoverA(int nuevaX, int nuevaY)
{
x = (short)nuevaX;
y = (short)nuevaY;
}

/// Carga la imagen que representara a este elemento grafico
public void CargarImagen(string nombre)
{
miImagen = Texture2D.FromStream(miPartida.GetGraphics(),
new FileStream ( nombre ,FileMode.Open ) );

contieneImagen = true;
contieneSecuencia = false;
}


/// Dibuja en pantalla oculta, como parte de un "SpriteBatch"
public void DibujarOculta(SpriteBatch listaSprites)
{
listaSprites.Draw(miImagen, new Vector2(x, y), Color.White);
}
}
Y a partir de ella, podríamos crear clase como la que representa a nuestro enemigo, así:

class Enemigo : ElemGrafico
{
// Constructor
public Enemigo(Partida p)
: base("imagenes/enemigo.png")
{
x = 400; // Valores iniciales
y = 400;
incrX = 2;
}

// Métodos de movimiento
public void Mover()
{
x += incrX;

if ((x <> 700))
incrX = (short)(-incrX);
}
}


De forma que el cuerpo del programa tendría cosas como:


protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);

miPersonaje = new Personaje();
miPersonaje.MoverA(400, 300);

miEnemigo = new Enemigo();
}


protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}


protected override void Update(GameTime gameTime)
{
MoverElementos();
ComprobarTeclas();

base.Update(gameTime);
}


protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);

spriteBatch.Begin();
miPersonaje.DibujarOculta(spriteBatch);
miEnemigo.DibujarOculta(spriteBatch);
spriteBatch.End();

base.Draw(gameTime);
}


public void MoverElementos()
{
miEnemigo.Mover();
}


public void ComprobarTeclas()
{
if (Keyboard.GetState().IsKeyDown(Keys.Left))
miPersonaje.MoverIzquierda();
if (Keyboard.GetState().IsKeyDown(Keys.Right))
miPersonaje.MoverDerecha();
...
}


Es decir, creará el personaje, el enemigo, los moverá y dibujará, y podemos incluso crear funciones auxiliares MoverElementos, ComprobarTeclas, ComprobarColisiones, etc., que acerquen la rígida estructura que propone XNA a la estructura "clásica" que habíamos creado por encima de SDL.

Si quieres ver el fuente completo, puedes descargar el mini-proyecto desde su página en Google Code, con el nombre miner002XNA:

http://code.google.com/p/manic-miner/downloads/list