August 2009 - Artículos

Normal 0 21 false false false MicrosoftInternetExplorer4

El objetivo en este pequeño ejemplo es, continuando con el ejemplo anterior, realizar una pantalla donde se nos mueva hasta 5 pelotas colisionando entre ellas. Se mostrará una pelota inicial y cuando el usuario pulse la tecla “espacio” del teclado añadirá una pelota a la pantalla.

 

Las posiciones iniciales de las pelotas han de ser un valor aleatorio y se ha de controlar que al mostrar una pelota nueva no se ponga encima de una de las existentes.

 

Bien pues pongámonos manos a la obra.

 

Sobre la clase cfSprite realizada en el articulo anterior solamente variaremos lo siguiente :

 

-          Añadir los siguientes atributos

 

public Vector2 centro { get { return posicion + (tamano / 2); } }

public float radio { get { return tamano.X / 2; } }

 

-          Añadir el método CirculoColision para detectar si nuestra pelota choca con otra

 

public bool CirculoColision(cfSprite otroSprite)

   {   

       return (Vector2.Distance(this.centro, otroSprite.centro) <

           this.radio + otroSprite.radio);

   }

 

-          Añadir el método Dispose. En el ejemplo anterior se realizaba desde la clase Game pero la intención ahora es gestionar un arraylist de pelotas. Para realizar el dispose se recorrerá el arraylist llamando a los dispose de los objetos cargados.

 

public void Dispose()

   {

     if (!this.textura.IsDisposed) this.textura.Dispose();

   }

 

 

Ahora lo que nos falta es modificar nuestra clase Game1.cs.

 

Añadiremos using System.Collections; para poder generar un atributo tipo arraylist como ArrayList balls = new ArrayList();

 

También tendremos los atributos :

 

Texture2D ballTexture;

Vector2 ballSize = new Vector2(80f, 80f);

Vector2 windowSize = new Vector2(800f, 600f);

KeyboardState oldKeyboardState;

 

No hay mucho que explicar, simplemente atributos que necesitaremos en nuestros métodos. El KeyboardState lo utilizo para guardar la última tecla pulsada, es decir, para que si el usuario pulsa la tecla “espacio” del teclado solo lance una pelota ya que si no lo controlará lanzaría las pelotas sucesivamente. Esto lo veremos más adelante.

 

Bien lo primero que tenemos que hacer es calcular una posición inicial y aleatoria donde posicionaremos la pelota, para ello utilizamos el siguiente método :

 

protected Vector2 getPosicioAleatoria()

  {

    Random r = new Random(DateTime.Now.Millisecond);

 

      int x = r.Next(0, int.Parse((this.windowSize.X - this.ballSize.X).ToString()));

      int y = r.Next(0, int.Parse((this.windowSize.Y - this.ballSize.Y).ToString()));

           

      return new Vector2(x, y);

 

}

 

 

En el LoadContent() de nuestra clase Game1 cargaremos el recurso de la imagen de la pelota y llamaremos a un método para que la lance a pantalla:

 

this.ballTexture = Content.Load<Texture2D>("pelota");

this.llençaPilota();

 

A continuación tenemos el método llençaPilota(), donde añadiremos a nuestro array de pelotas una nueva controlando que la posición no colisione con ninguna que tenemos en pantalla.

 

protected void llençaPilota()

  {

  cfSprite novaPilota;

  //calculo de la posicion y velocidad aleatoria

 

   Vector2 posicioIni = this.getPosicioAleatoria();

 

   Random r = new Random(DateTime.Now.Millisecond);

   Vector2 velocitat = new Vector2(r.Next(-5, 5),r.Next(-5, 5));

 

   novaPilota = new cfSprite(this.ballTexture,

                                        posicioIni,

                                        this.ballSize,

                                        this.windowSize,

                                        velocitat

                                        );

 

  //control para que no salga encima de otra pelota.

  int numColisions = 1;

  while (numColisions>0)

    {

       numColisions = 0;

       foreach (cfSprite p in this.balls)

       {

         if (novaPilota.CirculoColision(p))

          {

            novaPilota.posicion = this.getPosicioAleatoria();

             numColisions++;

           }

        }

     }

 

   this.balls.Add(novaPilota);

   }

 

Para que se nos muestre por pantalla simplemente añadimos el siguiente código en el método Draw() :

 

foreach (cfSprite p in this.balls)

        p.Draw(spriteBatch);

 

y hacemos lo mismo en el metodo Dispose :

foreach (cfSprite p in this.balls)

        p.Dispose();

 

Ahora solo nos falta controlar el teclado y actualizar las posiciones de las pelotas en pantalla, esto igual que el ejemplo anterior se realiza en el método Update , es decir, en el bucle del juego.

 

        protected override void Update(GameTime gameTime)

        {

 

            KeyboardState keyboardState = Keyboard.GetState();

            if (keyboardState.IsKeyDown(Keys.Space) && !this.oldKeyboardState.IsKeyDown(Keys.Space))

            {

                //restición de 5 pelotas a la pantalla.

                if (this.balls.Count<5)

                    this.llençaPilota();

            }

            this.oldKeyboardState = Keyboard.GetState();

                       

            if (keyboardState.IsKeyDown(Keys.Escape))

                this.Exit();

 

            //movem les pilotes

            foreach (cfSprite p in this.balls)

            {

                p.Mover();

                //control de colisio

                foreach (cfSprite p2 in this.balls)

                {

                    if (p.CirculoColision(p2))

                    {

                        Vector2 tempVelocity = p.velocidad;

                        p.velocidad = p2.velocidad;

                        p2.velocidad = tempVelocity;

                    }

                }

            }

           

            base.Update(gameTime);

        }

 

Básicamente controlamos que la tecla pulsada sea el espacio y que no la haya mantenido pulsada (oldKeyboardState).

 

Finalmente recorremos las pelotas y gestionamos la colisión entre ellas.

 

Ya estamos listos para ejecutar y si todo ha ido bien deberíamos ver un resultado similar a esto :

 


 

cfMoverPelota2.zip

Bien este han sido un ejemplo muy básicos de cómo podemos empezar a manejarnos con XNA, aunque lo realizado dista años ( por no decir siglos..) de algo parecido a un juego, hemos introducido objetos en la escena, colisiones entre ellos y una minima interacción con el usuario.

 

con no comments
Archivado en:

 

 

Bien siguiendo con el ejemplo anterior ahora vamos a mover un sprite por la pantalla.

 

Para mover nuestro sprite necesitamos los siguientes atributos : Textura, posición, tamaño de la imagen, velocidad i tamaño de la ventana.

 

La textura y la posición ya la usamos en el ultimo ejemplo, ahora necesitamos el tamaño de la ventana y de la imagen para controlar que no se nos salga el sprite de pantalla sino que rebote sobre las paredes. La velocidad es un vector2 igual que la posición, lo que debemos hacer es sumar los dos vectores para tener la posición nueva.

 

Si añadimos atributos a la clase también modificamos el constructor de la misma para que reciba los mismos parámetros.

 

Por ultimo para finalizar las modificaciones sobre la clase del sprite, generamos un método Mover que nos gestiona la próxima posición calculando que  el objeto no se salga de la pantalla.

 

Finalmente la clase cfSprite quedaría :

 

   class cfSprite

    {

        public Texture2D textura  { get; set; }

        public Vector2 posicion  { get; set; }

        public Vector2 tamano { get; set; }     

        public Vector2 velocidad { get; set; }

        private Vector2 tamanoVentana { get; set; }

 

 

        public cfSprite(Texture2D nuevaTextura, Vector2 nuevaPosicion, Vector2 nuevoTamano,

                        Vector2 nuevoTamanoPantalla, Vector2 nuevaVelocidad)

        {

            this.textura = nuevaTextura;

            this.posicion = nuevaPosicion;

            this.tamano = nuevoTamano;

            this.tamanoVentana = nuevoTamanoPantalla;

            this.velocidad = nuevaVelocidad;

        }

 

        public void Mover()

        {

            //  verificamos los casos en los que el objeto sale de pantalla

 

            // derecha

            if (posicion.X + tamano.X + velocidad.X > tamanoVentana.X)

                velocidad = new Vector2(-velocidad.X, velocidad.Y);

            //  izquierda

            if (posicion.X + velocidad.X < 0)

                velocidad = new Vector2(-velocidad.X, velocidad.Y);

            //  arriba

            if (posicion.Y + velocidad.Y < 0)

                velocidad = new Vector2(velocidad.X, -velocidad.Y);

            //  abajo

            if (posicion.Y + tamano.Y + velocidad.Y > tamanoVentana.Y)

                velocidad = new Vector2(velocidad.X, -velocidad.Y);

 

            posicion += velocidad;

        }

 

        public void Draw(SpriteBatch spriteBatch)

        {

            spriteBatch.Draw(this.textura, this.posicion, Color.White);

        }

    }

 

Bien ahora simplemente hemos de modificar la case Game1.cs

 

1.- Generar un atributo : cfSprite mySprite;

 

2.- Establecer el tamaño de la ventana en el constructor ( game1() )

 

//establecemos el tamano de la pantalla

graphics.PreferredBackBufferWidth = 800;

graphics.PreferredBackBufferHeight = 600;

 

3.-  Instanciamos el sprite en el metodo LoadContent()

 

Vector2 windowSize = new Vector2(graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight);

 

 

this.mySprite = new cfSprite(Content.Load<Texture2D>("pelota"),

                            Vector2.Zero, new Vector2(80f, 80f),

                            windowSize, new Vector2(3, 1)

                            );

 

4.- Añadir en el método Update la llamada para que se mueva el sprite

 

this.mySprite.Mover();

 

5.- Añadir en el metodo Draw

 

spriteBatch.Begin(SpriteBlendMode.AlphaBlend);

this.mySprite.Draw(spriteBatch);

spriteBatch.End();

 

6.- Asegurarnos de descargar los recursos como siempre

 

this.mySprite.textura.Dispose();

spriteBatch.Dispose();

 

Bien ya lo tenemos, si ejecutamos veremos una pelota de tenis rebotando por la pantalla del juego, adjunto el código fuente para que lo puedan examinar.

cfMoverPelota.zip

con 1 comment(s)
Archivado en:

Empecemos como siempre por un poco de teoría para situarnos y poder más adelante aplicar los conocimientos a la practica para asentarlos definitivamente.

 

Un sprite es un tipo de mapa de bits dibujados en la pantalla del ordenador, es decir, una imagen en 2 dimensiones que puede ser manipulada independientemente del escenario del juego. Esta imagen puede contener áreas transparentes para mostrar una imagen que no sea rectangular a la vista del usuario.

 

Evidentemente la animación del sprite se genera modificando su posición en pantalla, al llamar al método Draw del objeto y gracias a XNA Framework nos ahorraremos de borrar la posición anterior del sprite.

 

Sistema de coordenadas 2D. En un modelo en dos dimensiones tenemos las coordenadas X,Y para situar nuestro sprite. Funciona exactamente igual que un eje de coordenadas pero con la característica que el punto (0,0) es la parte superior izquierda de la pantalla.

 

Aquí os pongo un ejemplo grafico :

 

 

 

Para poder dibujar un sprite en pantalla utilizaremos el objeto SpriteBatch del namespace Microsoft.Xna.Framework.Graphics. Ref. http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.spritebatch.aspx

 

Dentro de este objeto tenemos métodos como el Draw para añadir un sprite a renderizar o el DrawString para añadir una cadena de texto a renderizar.

 

Nos centraremos ene l método Draw, el cual podemos ver que esta sobrecargado y dispone de diferentes formas de llamarlo. Por ejemplo cogemos los parámetros Texture2D, Vector2 (será la posición del sprite en pantalla) i Color.

 

Para hacer una orientación a objetos crearemos una clase que se llame cfSprite con los atributos textura y posición que asignaremos en el constructor por ejemplo. También generaremos un método Draw que recibirá por parámetro un objeto SpriteBatch e invocará a su método Draw pasándole los atributos.

 

using Microsoft.Xna.Framework.Graphics;  

using Microsoft.Xna.Framework; 

 

namespace cfDemo

{

    class cfSprite

    {

        public Texture2D textura { get; set; }  

        public Vector2 posicion { get; set; } 

 

        public cfSprite (Texture2D nuevaTextura , Vector2 nuevaPosicion){

            textura  = nuevaTextura;

            posicion = nuevaPosicion;

        }

 

        public void Draw(SpriteBatch spriteBatch)

        {

            spriteBatch.Draw(this.texture, this.position, Color.White);

        }

    }

}

 

Bien ya disponemos una clase con la que podemos generar todos los sprites que deseemos.

 

Ahora lo que necesitamos es una imagen para nuestro sprite, por ejemplo podemos buscar una pelota en bing.com y guardarnos el fichero. Para añadir este fichero como recurso de nuestro proyecto es tan fácil como hacer copiar del fichero y en la carpeta “content” de nuestro proyecto hacer pegar!, también tiene la opción haciendo click con el botón derecho sobre la carpeta content.

 

Para cargar un sprite nos iremos a la clase Game1.cs y realizaremos las siguientes modificaciones :

 

1.- Generar un atributo : cfSprite mySprite;

 

2.- Añadir en el método LoadContent()

 

// Instanciar nuestro sprite

            mySprite = new cfSprite(Content.Load<Texture2D>("pelota"), new Vector2(0f, 0f));

 

Bien analicemos un poco esta línea :

 

-          Para cargar el fichero es tan simple como Content.Load<Texture2D>("pelota") donde pelota es el nombre que tenemos en el explorador de la solución, podemos ver que no hace falta poner la extensión física del fichero para cargarlo.

 

-          Para establecer la posición, (0,0) en este caso, podemos pasarle los valores de tipo float. También podemos utilizar la constante Vector2.Zero.

 

3.- Añadir en el método Draw

 

            spriteBatch.Begin(SpriteBlendMode.AlphaBlend); //si nuestro sprite tiene transparencies

            mySprite.Draw(spriteBatch);

            spriteBatch.End();

 

4.- Añadir en el método UnloadContent()

 

mySprite.texture.Dispose(); //para liberar los recursos

 

Bien ya podemos ejecutar y maravillarnos con lo buenos que somos :D jejeje, bien próximamente le daremos movimiento ( simplemente es actualizar su posición dentro del bucle del programa ) e intentaremos gestionar colisiones.

 

 

 

con no comments
Archivado en:

Antes de ponernos a programar como locos creo que lo mejor es entender un poco que nos genere el visual Studio cuando seleccionamos un nuevo proyecto tipo XNA.

 

Program.cs : Contiene el Main de nuestro proyecto , instancia el juego (game class) y llama a su método Run().

 

static class Program

{

static void Main(string[] args)

{

using (Game1 game = new Game1())

{

game.Run();

}

}

}

 

Viendo el código podemos apreciar que le podemos pasar parámetros por línea de comandos a nuestro main i que utilizar el using para definir el ámbito final del cual el objeto se destruye, es decir, llamara a su método dispose automáticamente.

 

Game1.cs : Clase que hereda de Microsoft.Xna.Framework.Game i nos proporciona una serie de métodos para inicializar nuestro juego, así como el bucle principal que controla el juego.

 

Atributos de la clase:

 

GraphicsDeviceManager graphics;

 

Este objeto es el handler para la capa de gráficos, es decir, el objeto que utilizaremos para manejar la configuración de la tarjeta grafica. Ref. http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphicsdevicemanager.aspx

 

SpriteBatch spriteBatch;

 

Este objeto es usado para dibujar texto e imágenes en 2D, entraremos en más profundidad en otros post pero para quien baja más rápido que yo ( cosa no muy difícil ) aquí tiene la referencia de MSDN  http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.spritebatch.aspx

 

Métodos de la clase:

 

- Constructor :

 

        public Game1()

        {

            graphics = new GraphicsDeviceManager(this);

            Content.RootDirectory = "Content";

        }

 

Podemos observar que se instancia el objeto Graphics (atributo de la clase ) i que se asigna la carpeta Content que vemos en el explorador del proyecto como el directorio raíz donde guardaremos todos los contenidos del juego, es decir, sonidos, gráficos, modelos 3d o efectos

 

A esto se le llama Content Pipeline i es una de las cosas más interesantes que nos aporta XNA ya que nos facilita la carga de recursos, si queréis más información aquí hay un buen post http://juank.black-byte.com/xna-content-pipeline/

 

Para una aplicación Windows podemos establecer el tamaño de la pantalla en el constructor con el siguiente código :

 

            this.graphics.PreferredBackBufferWidth = 800;

            this.graphics.PreferredBackBufferHeight = 600;

 

También tenemos la opción this.graphics.IsFullScreen para establecer true (pantalla completa) o false.

 

- Initialize :

 

       protected override void Initialize()

        {

            // TODO: Add your initialization logic here

            base.Initialize();

        }

 

Este método se lanza cuando desde el main se llama al método Run del objeto Game. Dentro de este método inicializaremos cualquier cosa que no sea recursos gráficos, los cuales es mejor inicializarlos en el método LoadContent.

 

- LoadContent

 

        protected override void LoadContent()

        {

            // TODO: load content

        }

 

Como hemos comentado anteriormente, aquí es el lugar indicado para cargar los recursos gráficos.

 

- UnloadContent

 

        protected override void UnloadContent()

        {

            //  Free the previously alocated resources

        }

 

En este metodo descargaremos los recursos cargados, por ejemplo mySprite.texture.Dispose();

 

- Bucle del juego

 

Bien todos sabemos que el corazón de todo juego es un bucle que actualizar los cálculos de los objetos en función de las posiciones y de los inputs del usuario, redibujar la pantalla y tiene una condición de salida que finaliza el juego ( el usuario pulsa salir, ha muerto o ha ganado ).

 

Xna nos proporciona básicamente dos métodos para gestionar esto, que son Update y Draw :

 

 

       protected override void Update(GameTime gameTime)

        {

            // Allows the game to exit

            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)

                this.Exit();

 

            // TODO: Add your update logic here

 

            base.Update(gameTime);

        }

 

Por defecto nos pone una línea para controlar el GamePad de la Xbox, pero como yo no tengo la Xbox en los futuros ejemplos cambiare todo a control de teclado Windows. El parámetro gameTime es importante para la lógica del juego como veremos más adelante. Ref. http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.game.update.aspx

 

      protected override void Draw(GameTime gameTime)

        {

            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

            base.Draw(gameTime);

        }

 

En este método es donde se pintan los sprites o gestionamos lo que deseemos a nivel de render de objetos por pantalla. En este código he puesto por ejemplo que borre la pantalla y ponga un color azul de fondo. Ref. http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.game.draw.aspx

 

 

Bien hemos hecho un vistazo a una estructura básica de un proyecto de XNA para entender la lógica de la programación antes de ponernos a “jugar”, Resumiendo un poco esta lógica seria :

 

 

Main  (program.cs)

            Constructor     (game1.cs)

            Initialize()         

            LoadContent()

            Run()

                        Update()

                        Draw()

UnloadContent()

Fin del programa

con 1 comment(s)
Archivado en:

Bueno aquí estamos otra vez después una parada considerable.

 

Actualmente considero que no tengo nivel suficiente como para postear cosas interesantes sobre programación de software de gestión ya que no me dedico a ello, por lo que no lo voy ha hacer más. Por el contrario, actualmente empiezo a tener tiempo libre en mi vida personal por lo que me estoy empezando a mirar XNA.

 

Aunque mi nivel es inicial sobre el tema, iré aprendiendo sobre el y posteando las pocas cosas interesantes que realice. Para empezar me centrare todo en 2D hasta que lo domine, para después empezar a ver el 3D (de aquí a mucho seguro ;p  jejeje ).