martes, 22 de junio de 2010

Programando videojuegos: Tutorial XNA Game Studio 4.0 parte 4

crab-icon ball c#

Hoy en The Code Crab vamos a poner un fondo animado para nuestro futuro superventas. La técnica que vamos a seguir es bien sencilla: Tendremos una imagen que tenga el mismo ancho que la pantalla pero mucho mas alta, y la empezaremos a dibujar por abajo, subiendo poco a poco, hasta llegar a la parte superior que será similar a la parte inferior. Cuando pasa esto volvemos a la parte de abajo y ya tenemos efecto de scroll.

Dado que XNA solo soporta imágenes de resolución X x Y, donde ni X ni Y rebasan los 2000 y pico pixeles (no se cuantos exactamente) es posible que para un scroll largo haga falta dividir el fondo en diferentes imágenes. No vamos a dar un ejemplo de ese caso, pero que sepáis que sería exactamente lo mismo pero un poco mas complicado. Lo más importante es que si tenemos 3 imágenes A, B y C, para hacer una transición fluida de una a otra la ultima parte que se dibuje de una tiene que ser equivalente a la primera de la siguiente, esto es, el final de A y el principio de B coinciden, el final de B y el principio de C coinciden y el final de C y el principio de A coinciden.

Una vez tenemos claro lo que queremos hacer, necesitamos saber la resolución de nuestra ventana para preparar la imagen. Para ello, como recordareis, tenemos las propiedades PreferredBackBufferHeight y PreferredBackBufferWidth de graphics. Aunque si queréis se pueden alterar, yo no lo voy a hacer, voy a dejar la resolución por defecto. Como no la altero, voy a imprimir estos datos por pantalla, para saber que resolución tiene nuestra ventana:

 protected override void Initialize()
{
Console.WriteLine(graphics.PreferredBackBufferHeight + "x" + graphics.PreferredBackBufferWidth);
_fondo = new Fondo(graphics.PreferredBackBufferHeight, graphics.PreferredBackBufferWidth);
_nave = new Nave(graphics.PreferredBackBufferHeight, graphics.PreferredBackBufferWidth);
this.Window.Title="Space Burst";
base.Initialize();
}


Con esto, ejecutamos nuestro proyecto, y no ha cambiado nada, pero si vamos a la ventana de resultados veremos algo así:



image No os olvidéis de borrar la línea ahora que ya sabéis el tamaño de la ventana. Con esto ya sabemos la resolución de la ventana, 480x800, con lo que ahora necesitamos una imagen loquesea x 800 donde los primeros y los últimos 480 px sean iguales. A partir de una imagen real del espacio, algo de photoshopeo cutre (se me da fatal) y algo de paciencia, yo he conseguido esto, aunque si os apetece podéis haceros vuestro propio fondo.



spaceBackgr Con esto, tenemos ya un fondo de 1700x800 con el principio y el final igual (Hay que pinchar en la imagen para verla completa). Lo metemos en la carpeta y lo agregamos a los recursos del proyecto, como venimos haciendo con todas las imágenes. Tras esto ya estamos preparados para crear la clase Fondo (esta es bastante sencillita):



 private const int anchoImagen = 800;
private const int altoImagen = 1700;
private int altoVentana;
private Texture2D imagen;
private Rectangle rectangulo;
public Fondo(int altoVentana, int anchoVentana)
{
rectangulo = new Rectangle(0, altoImagen - altoVentana, anchoVentana, altoVentana);
this.altoVentana = altoVentana;
}


En cuanto a las propiedades, a estas alturas no os sorprenderá mucho, así que iré rápido. Guardamos el ancho y el alto de la imagen, así como el alto de la ventana (para saber cuanto tiene que medir el rectángulo) que será pasado en el constructor. También almacenamos un rectángulo con la sección de la imagen que se dibujara, empezando por la parte inferior de la imagen y un Texture2D con la imagen en sí.



        public void LoadContent(ContentManager Content)
{
imagen = Content.Load("spaceBackgr");
}
public void Update()
{
rectangulo.Y -= 1;
if (rectangulo.Y <= 0)
rectangulo.Y = ((altoImagen - altoVentana) -15);
}
public void Draw(SpriteBatch spbtch)
{
spbtch.Draw(imagen, new Vector2(0, 0), rectangulo, Color.White);
}


En cuanto a los métodos, tendremos los 3 tradicionales de XNA que ya conoceréis. LoadContent y Draw son iguales que en Nave, por lo que no merece mucho la pena pararse (en cuanto a Draw la única diferencia es que lo dibujamos en (0,0), ya que queremos que ocupe toda la pantalla).



Lo importante aquí (relativamente, pues es bastante simple en realidad) es el método Update, en el cual disminuimos en 1 la coordenada Y de rectángulo, para subir hacia arriba el rectángulo y crear un efecto scroll. A mi personalmente me gusta el efecto que queda al disminuir en 1 cada Update, pero si se aumenta el numero aumentara la velocidad del scroll. Eso queda a gusto de cada uno. También controlamos que si Y es 0 o menos, volvemos al principio, volviendo a mostrar la parte de abajo de la imagen, que como ya mencioné anteriormente debería ser igual. Ese –15 extra se debe a que si ponemos las coordenadas iniciales sin mas se apreciara un rebote, dado a que la imagen se esta moviendo constantemente, y que probablemente la parte de arriba y la de abajo no sean exactamente iguales pixel a pixel. A base de prueba y error para el caso de esta imagen he sacado que el numero apropiado son 15 px, pero si usáis otra imagen esto cambiara.



Ahora que ya tenemos lista la clase Fondo, solo nos queda agregar una instancia en la clase principal Game1, y las invocaciones a todos los métodos necesarios:



    public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Nave _nave;
Fondo _fondo;

public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}

protected override void Initialize()
{
//Recordar comentar la siguiente linea
//Console.WriteLine(graphics.PreferredBackBufferHeight + "x" + graphics.PreferredBackBufferWidth);
_fondo = new Fondo(graphics.PreferredBackBufferHeight, graphics.PreferredBackBufferWidth);
_nave = new Nave(graphics.PreferredBackBufferHeight, graphics.PreferredBackBufferWidth);
this.Window.Title="Space Burst";
base.Initialize();
}

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

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

protected override void Update(GameTime gameTime)
{
// Cuidado con esto, no usamos mando.
if (Keyboard.GetState().IsKeyDown(Keys.Escape))
this.Exit();
_fondo.Update();
_nave.Update();

base.Update(gameTime);
}

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

spriteBatch.Begin();
_fondo.Draw(spriteBatch);
_nave.Draw(spriteBatch);
spriteBatch.End();

base.Draw(gameTime);
}
}


Es importante recordar, que como se menciono anteriormente, para que la imagen A se dibuje por encima de la imagen B, hay que dibujar primero B y luego A. Por tanto, el Fondo debe ser lo primero en dibujarse, o sino tapara el resto de objetos. Por lo demás, nada nuevo.



image



Tras ejecutar vemos el efecto obtenido. ¿Que os parece? Yo creo que no esta mal, queda un poco raro la nave pixelada con la imagen real del espacio, pero bueno, siempre podéis cambiar vuestro fondo por otro :P



Podéis descargaros el código fuente del tutorial hasta este momento en el siguiente enlace: http://www.megaupload.com/?d=RB2TYYLJcangrejus guaperus



Con esto concluye esta parte del tut orial. En la siguiente parte nos ponemos serios, introduciendo enemigos y detección de colisiones, no os lo perdáis. ¡Sed buenos cangrejos!



PD: ¡¡Gracias a Sara por el aporte del cangrejo!!



Etiquetas de Technorati: ,,,,,

14 comentarios:

  1. La imagen del fondo no la ha subido completa. A ver si lo logro arreglar.

    ResponderEliminar
  2. arreglado, esta colgada en imageshack. Avisadme si el enlace se rompe.

    ResponderEliminar
  3. Ya he terminado esta parte del tutorial, muy bueno.
    Le puse una velocidad de 2px al fondo.

    ResponderEliminar
  4. Me encantan tus tutoriales sobre XNA y como vas aplicando un buen diseño de objetos para animar sprites, mover el fondo y realizar disparos. De aquí a nada tienes un superventas. Me gustaría que siguieras con ellos, diseñando la lógica de colisiones e incluso algo de inteligencia artificial. No te desanimes que por aquí hay gente que te lee y aprende mucho.

    ResponderEliminar
  5. Hombre, sobre lo de la IA y deteccion de colisiones son temas bastante avanzados, que sin duda usare pero no se hasta que punto los podre explicar bien o si sera mas acto de fe, pero sin duda seguire, y intentare seguir explicandolo todo :)

    ResponderEliminar
  6. buen tuto si señor,a la espera de enemigos para dispara,xD,grácias

    ResponderEliminar
  7. Hola Wardamo, e portado el codigo ya compilado a otros PC's (tres en concreto) y no me funciona, aún instalando el Framework 4.0 no rula, jeje, sabes porque puede ser??Por cierto, soy el Mismo que Juan Antonio,Saludos

    ResponderEliminar
  8. Perdona la tardanza, estaba de vacaciones. Que es lo que falla? Si lo haces igual en todos los pcs y en unos funciona y en otros no yo creo q puede ser por 2 cosas distintas, o no te va bien xna por ser un ordenador antiguo o no te ha instalado bien (es bastante facil, el instalador era una beta bastante poco estable cuando yo lo instale al menos). En ambos casos puedes probar con una versión un poco mas antigua de xna, no te servira el proyecto pero las clases si. Solo cambia como insertar las imagenes y puede que algun import (no estoy seguro). Cualquier otra duda sobre post antiguos mejor mandamela al mail que me enterare mejor (lo acabo de poner en el perfil que no lo tenia publico) Un saludo.

    ResponderEliminar
  9. Muy bueno tu tutorial. Lo probé en VS2010 y XN4 Final. Funciona perfecto. Facilísimo. Le cambié la velocidad del fondo según vaya para adelante, para atras o sin moverse. Te felicito. Gracias

    ResponderEliminar
  10. Excelente tutorial, espero que en un futuro agregues modelos 3d

    ResponderEliminar
  11. amigo me encanto tu tuto pero me gustaría si me ayudaras en como hacer que el fondo no salte a la primera instancia sino que pueda hacer un scroll constante....... gracias^^

    ResponderEliminar
  12. esta muy bien explicado y funciona muy bien. Gracias por ese aporte

    ResponderEliminar
  13. buenisimo tutorial,bien explicado y funciona perfectamente

    ResponderEliminar
  14. amigo disculpa no se si me podías pasar el proyecto y si tu vieses otros blog o tutoriales de programación de juegos porfavor mandamelos te lo agradecere mucho ^_^ (quepuy_1996@hotmail.com) gracias por anticipado

    ResponderEliminar