viernes, 27 de enero de 2012

Level 00: Nuevo Proyecto y Uso de Texto

"Los primeros 40 años de vida nos dan el texto; los 30 siguientes, el comentario."
Arthur Schopenhauer (1788 - 1860 - Filósofo alemán.

Antes que nada...

Aviso: El contenido puede estar incompleto. Iré actualizando en caso de que necesite hacer algunas correciones o agregar nuevos temas.

¡Llegó la hora de empezar!

Esta es la primer entrada relacionada a la programación de videojuegos 2D en XNA 4.0. En esta oportunidad, vamos a comenzar viendo temas básicos (...y algo aburridos, debo admitir) necesarios para poder comenzar con un videojuego. Estos son los siguientes temas que veremos esta vez:
  1. Cómo comenzar un Nuevo Proyecto
  2. Cómo cambiar el tamaño de Pantalla del juego.
  3. Qué es y cómo crear un SpriteFont.
  4. Cómo cargar un archivo al proyecto.
  5. Cómo mostrar un Texto en la Pantalla.
Así que bueno, ¡comencemos de una buena vez!



Comenzando un Nuevo Proyecto

Una vez instalado XNA Game Studio 4.0, busquemos la carpeta de Microsoft Visual Studio 2010 Express que también fue instalada. Luego ejecutemos la aplicación Microsoft Visual Studio 2010 Express for Windows Phone. Tendría que aparecer la siguiente ventana de inicio:
Imágen 1.0: Pantalla de inicio

Luego, para comenzar un nuevo proyecto, vamos a la pestaña File y hacemos click en New Project. Aparecerá lo siguiente:
Imágen 1.1: Selección de un Nuevo Proyecto

Elije un nombre para tu proyecto y una dirección donde se guardarán sus datos. En mi caso, mi proyecto se va a llamará Prueba01 y lo guardaré en el Escritorio de Windows.
Cuando todo esté listo para comenzar, seleccionan OK y les aparecerá el siguiente código automáticamente (el cual fue explicado brevemente en Press Start!: Introducción a XNA 4.0 ):

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Prueba01
{
    /// 
    /// This is the main type for your game
    /// 
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

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

        /// 
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// 
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// 
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// 
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
        }

        /// 
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// 
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// 
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// 
        /// Provides a snapshot of timing values.
        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);
        }

        /// 
        /// This is called when the game should draw itself.
        /// 
        /// Provides a snapshot of timing values.
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here

            base.Draw(gameTime);
        }
    }
}

Para ejecutar el código de nuestro proyecto, apretamos F5. Si lo hacemos ahora, tendría que salir la siguiente pantalla:
Imagen 1.2: Ejecución de un Proyecto
 
Para nada emocionante, la verdad ¿eh?. Pues quieras creerlo o no, esto ya es tu primer juego y gana quien soporte más tiempo observar la amigable pantallita azul. Puedes invitar a toda tu familia... pero no cuentes conmigo.


Configurando el Tamaño de Pantalla


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

            // this.graphics.IsFullScreen = true;
            
            //this.graphics.PreferredBackBufferWidth = 600;
            //this.graphics.PreferredBackBufferHeight = 800;
        }

Antes de comenzar, configuremos un poco el tamaño de pantalla. Si deseas ejecutar tu juego en pantalla completa, debes activar la propiedad IsFullScreen como "true" (línea 6). O si prefieres, puedes modificar el valor de su ancho y su alto (líneas 8 y 9 respectivamente) con las propiedades PreferredBackBufferWidth (ancho) y PreferredBackBufferHeigth (alto) con las medidas que elijas (por ejemplo 600x800 pixeles).

En este caso, decidí usar el tamaño de pantalla que tiene inicialmente. Por eso, las propiedades están a modo de comentarios.


Creación de un SpriteFont

Aviso: Aprende más sobre SpriteFont aquí.

¡Bien! Ya configuramos la pantalla, ahora veamos cómo podemos mostrar un simple texto en ella. En este caso vamos a mostrar el valor del ancho y alto de la pantalla. Aunque en un futuro, lo que aprendamos ahora, seguramente nos servirá para mostrar los puntos de vida de un jugador, o su puntaje, o un texto que narre una historia, etc. Por ahora,  vayamos por lo básico.

Antes de poder escribir algo, es necesario crear un SpriteFont (fuente de texto). Para ello, hacemos click derecho en Prueba01Content y seleccionamos "New Item", como muestra la siguiente imagen:

Imágen 2.0: Añadir un nuevo Sprite Font


Luego, elijan un nombre (SpriteFont1 es en mi caso) y seleccionen el item llamado "Sprite Font", el cual representará la fuente de texto que usaremos para mostrar.

Entonces se generará el siguiente código en una nueva pestaña:
<?xml version="1.0" encoding="utf-8"?>
<!--
This file contains an xml description of a font, and will be read by the XNA
Framework Content Pipeline. Follow the comments to customize the appearance
of the font in your game, and to change the characters which are available to draw
with.
-->
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
  <Asset Type="Graphics:FontDescription">

    <!--
    Modify this string to change the font that will be imported.
    -->
    <FontName>Arial</FontName>

    <!--
    Size is a float value, measured in points. Modify this value to change
    the size of the font.
    -->
    <Size>14</Size>

    <!--
    Spacing is a float value, measured in pixels. Modify this value to change
    the amount of spacing in between characters.
    -->
    <Spacing>0</Spacing>

    <!--
    UseKerning controls the layout of the font. If this value is true, kerning information
    will be used when placing characters.
    -->
    <UseKerning>true</UseKerning>

    <!--
    Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
    and "Bold, Italic", and are case sensitive.
    -->
    <Style>Regular</Style>

    <!--
    If you uncomment this line, the default character will be substituted if you draw
    or measure text that contains characters which were not included in the font.
    -->
    <!-- <DefaultCharacter>*</DefaultCharacter> -->

    <!--
    CharacterRegions control what letters are available in the font. Every
    character from Start to End will be built and made available for drawing. The
    default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
    character set. The characters are ordered according to the Unicode standard.
    See the documentation for more information.
    -->
    <CharacterRegions>
      <CharacterRegion>
        <Start>&#32;</Start>
        <End>&#126;</End>
      </CharacterRegion>
    </CharacterRegions>
  </Asset>
</XnaContent>


Lo más importante sucede en las líneas 14, 20 y 26:
  • La línea 14 se refiere al tipo de fuente que vamos a usar. En esta caso usé "Arial" como fuente.
  • La línea 20 indica el valor (en float) del tamaño que el texto tendrá. En este caso, el tamaño de fuente es de 14.
  • La línea 26 establece el valor (en float) del espaciado entre los caracteres del texto. En este caso es de 0.
Si deseas cambiar alguna de estas características, sólo modifica sus valores por los que quieras.


Carga de un archivo (SpriteFont)

Aviso: Aprende más sobre el método LoadContent() aquí.

Ahora que creamos un SpriteFont, podemos escribir algo en pantalla. Primero, declaramos una variable de instancia que hará referencia al SpriteFont que acabamos de crear. Lo hacemos agregándola de esta manera, al comienzo de la clase Game1 (ubicada en la línea 12 del código mostrado al principio) :
public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;        
     
        SpriteFont font;                        // Se guardará el tipo de fuente que se usará en los textos.
        


Luego, continuamos cargando el SpriteFont que creamos hace un rato, que estará referenciada por la variable font. Para ello, añadimos lo siguiente dentro del método LoadContent() de la clase Game01:

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

            font = Content.Load<SpriteFont>("SpriteFont1");
        }

El método Load() carga aquellos archivos que necesitará el juego, como por ejemplo archivos mp3, jpg, png... y en este caso necesitamos un SpriteFont. El método necesita como parámetro el nombre del archivo, que aquí es "SpriteFont1". Podría simplificarse la expresión del método de la siguiente manera:

instanciaDeArchivo = Content.Load <tipoDeArchivo>("NombreDelArchivo");

En donde:
  • instanciaDeArchivo: es una variable de instancia que hace referencia a un archivo en particular (unaImagen, unSonido, unaTextura). En este caso la variable se llama font.
  • tipoDeArchivo: es la clase de archivo que se desea cargar. Puede ser un archivo de la clase SpriteFont, Texture2D, Texture3D, Song...etc. Aquí usamos un SpriteFont.
  • nombreDelArchivo: es el nombre del archivo que fue cargado en la carpeta Content. En este caso es "SpriteFont1"Es muy importante que todo los archivos que vayas a usar (gráficos, donidos, texturas) los guardes en esa carpeta. Para ello, sólo arrastra con el mouse el archivo que quieras guardar en el proyecto y deposítalo dentro de la carpeta Content.


Escribir un Texto en Pantalla

Aviso: Recomiendo que mires antes un post sobre cómo es el Sistema de Coordenadas en 2D aplicado en XNA. De todas formas, explicaré un poco de esto en la siguiente Entrada. Muchas gracias al ingeniero David Mariscal Fernández por la información.

Todo listo para escribir algo en pantalla. Para ello añadimos lo siguiente dentro del método Draw() de la clase Game1:

protected override void Draw(GameTime gameTime)
        {
            string anchoPantalla = Convert.ToString(GraphicsDevice.Viewport.Width);
            string altoPantalla = Convert.ToString(GraphicsDevice.Viewport.Height);                     
            
            GraphicsDevice.Clear(Color.CornflowerBlue);
            

            // ============ Comienza dibujado ===================
            spriteBatch.Begin();

            // Se dibuja el texto donde informa el valor de Ancho de la pantalla
            spriteBatch.DrawString(font,
                                "Ancho: " + anchoPantalla,
                                new Vector2(GraphicsDevice.Viewport.TitleSafeArea.X,
                                            GraphicsDevice.Viewport.TitleSafeArea.Y),
                                Color.White,
                                0f,
                                new Vector2(0,0),
                                0.8f,
                                SpriteEffects.None,0f);

            // Se dibuja el texto donde informa el valor de Alto de la pantalla
            spriteBatch.DrawString(font,
                                    "Alto: " + altoPantalla,
                                    new Vector2(GraphicsDevice.Viewport.TitleSafeArea.X+20,
                                                GraphicsDevice.Viewport.TitleSafeArea.Y+50),
                                    Color.DarkBlue,
                                    MathHelper.ToRadians(90),
                                    new Vector2(0,0),
                                    0.8f,
                                    SpriteEffects.None,
                                    0f);
           
            spriteBatch.End();
            // ============ Termina dibujado ===================           
                 

            base.Draw(gameTime);
        }

En las líneas 3 y 4, lo único que hice fue convertir el valor del ancho y alto de la pantalla a String y guardar esos valores en su respectivas variables.

Pero lo más importante de este método sucede entre las líneas 11 y 36. Todo aquello que esté escrito entre el método spriteBatch.Begin() y el método spriteBatch.End() se dibujará en pantalla por cada loop del juego. Es decir, mientras el juego funcione, se dibujará a cada momento todo lo que esté entre esos 2 métodos.

Se observa también que se usa el método DrawString() en las líneas 14 y 25. Este método sirve para dibujar un texto en pantalla y requiere (en este caso) de los siguientes parámetros:
  1. spriteFont: [SpriteFont] fuente para mostrar texto.
  2. text: [String] Cadena de texto a escribir.
  3. position: [Vector2] Ubicación (en coordenadas de la pantalla) en donde se escribirá el texto.
  4. color: [Color] El color que tendrá el texto.
  5. rotation: [float] Ángulo de rotación (en radianes) del texto que se girará respecto a su centro. En este caso usé uno de los métodos de MathHelper
  6. origin:  [Vector2] Coordenadas orígen del texto. El valor predeterminado es (0,0), que representa su esquina superior izquierda.
  7.  scale: [float] Factor de escala.
  8. effects: [SpriteEffects] Efectos que se aplican al texto (negrita, cursiva).
  9. layerDepth: [float] Profundidad de la capa. De forma predeterminada, 0 representa la capa anterior y 1 representa una capa posterior.
 Es importante saber que para representar la posición de un texto, una imagen... en fin un objeto, se usa la clase Vector2, que indica su posición en coordenadas (x,y). Hablaremos mejor de esto en las próximas entradas. 

Así entonces, si presionamos F5 se obtiene como resultado lo siguiente:

Imagen 3.0: Demostración de Texto en Pantalla

Vemos que el texto "Ancho: " no tiene rotación. Por eso el quinto parámetro (rotation) vale cero. En cambio el texto "Alto: " Tiene una rotación de 90º. Pude hacer eso con la ayuda de uno de los métodos de la clase MathHelper. Esta clase resulta muy útil para ahorrarnos el trabajo de hacer algunos cálculos. Por ejemplo: 

Pregunta: ¿Cuántos radianes son equivalentes a 90 grados?
Respuesta: No lo sé, pero si usas el método ToRadians() de la clase MathHelper y le pasas como parámetro esos 90 grados, te aseguro que te los convierte en radianes.

Si, ya sé que no soy muy amigo de las matemáticas. Pero Wikipedia si, asi que puedes ver un artículo sobre radianes e informarte en lugar de seguir mi mal ejemplo :) ... o puedes pedirle ayuda de vez en cuando a nuestro querido amigo MathHelper, como yo :D



Conlusión
Hemos visto algo muy aburrido para mi gusto (... en verdad ¬¬ ). El uso de texto no es la gran cosa, pero seguramente saber esto nos resultará muy útil más adelante. Puedes descargar el código fuente:



Y esto es todo por ahora. Les prometo que la próxima, hacemos algo más entretenido. Vamos a comenzar a controlar el movimiento de un objeto que represente al jugador. Y luego podremos ver temas más emocionantes, como la detección de colisiones.

Tengo varios proyectos ya armados y funcionando, pero el problema es armar las Entradas del blog. Realmente me lleva mucho tiempo organizar, corregir y tratar de hacer todo lo más entendible posible. Pero cuando tenga listas las entradas, las subo.

¡Hasta entonces!


¡Sigue adelante, siempre!

3 comentarios:

quiero agarrar el hilo de esto para poder crear mi tesis de grado en xna seguire viendo tus ejemplos suerte

NOOOO!!

Me hice un pequeño tiempo en el laburo para salvarte, amigo anubis!!
XNA està practicamente muerto. Microsoft ya no lo va a tratar más, lo abandonó.

http://gamasutra.com/view/news/185894/Its_official_XNA_is_dead.php

No creo que tengas problemas en terminar el juego, pero te recomiendo que veas otras opciones, como SFML que usa C++, o LibGDX que usa Java.

Un abrazo y te deseo mucho éxito con tu tesis!

P.D: terminé abandonando este foro apenas leì la noticia :(

Publicar un comentario