Generation Apps – Día 8 – Aplicar recursos de temas para Windows Phone

Recursos de temas de Windows Phone

Un tema es un conjunto de recursos utilizados para personalizar los elementos visuales en un Windows Phone. Tu puedes crear aplicaciones que preservan el «look and feel» nativo del dispositivo desde un punto estético (Por ejemplo, Colores de fondo y colores de acento). Aprende mas acerca de como trabajar con estos recursos para asegurar que la interfaz de tu aplicación se mantenga consistente con el tema que el usuario este utilizando en su Windows Phone.

Cuando un usuario habilita un tema del sistema en las configuraciones del dispositivo, solo los colores tema-relacionados de la aplicación cambian. Fuentes de tipografía y tamaños permanecen constantes. Por ejemplo, si el usuario cambia el color del tema del Windows Phone a verde, solo los elementos de interfaz de usuario configurados con la paleta de recursos del sistema de tu aplicación serán de color verde. En la lección de hoy, te mostrare como puedes lograr esto.

Aplicando recursos de tema

A continuación te mostrare como aplicar un conjunto de atributos de recursos del sistema, o aplicar todo un estilo de recursos en diversos elementos de la interfaz de usuario.

Preparando la aplicación

  1. Ejecuta Visual Studio 2010 Express para Windows Phone.

  2. Crea un nuevo proyecto seleccionando haciendo clic en Archivo | Nuevo Proyecto

  3. La ventana de Nuevo Proyecto aparece. Expande las plantillas de Visual C#, y selecciona la plantilla Silverlight para Windows Phone.

  4. Ahora selecciona la plantilla Aplicación Windows Phone. Coloca de nombre de proyecto tu preferencia.

  5. Da clic a OK. La ventana de selección de plataforma Windows Phone aparece. Selecciona Windows Phone OS 7.1 para la versión de Windows Phone objetivo.

  6. Da clic a OK. El proyecto nuevo es creado y se despliega el archivo MainPage.xamlen el diseñador de Visual Studio.

  7. En el MainPage.xaml, reemplaza el grid nombrado LayoutRootcon lo siguiente:

    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
    
        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="ApplicationTitle" Text="THEME RESOURCES" Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock x:Name="PageTitle" Text="example" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>
    
        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    
        </Grid>
    </Grid>

Para aplicar recursos de temas del sistema

  1. En MainPage.xaml, agrega el siguiente código dentro del grid nombrado ContentPanel:

    <Rectangle 
        Height="100" 
        HorizontalAlignment="Left" 
        Margin="12,33,0,0" 
        Name="rectangle1" 
        Stroke="{StaticResource PhoneForegroundBrush}" 
        StrokeThickness="1" 
        VerticalAlignment="Top" 
        Width="200" 
        Fill="{StaticResource PhoneAccentBrush}" />

    Este código crea un rectángulo en la parte superior izquierda de la pagina. Nota el atributo Fill y como usa el valor StaticResource para especificar el recurso brush nombrado PhoneAccentBrush. Este brush particular permite que el color del rectángulo responda a los cambios realizados en el color de tema del sistema operativo.

  2. En MainPage.xaml, agrega el siguiente código dentro del grid ContentPanel, debajo del rectángulo.

    <TextBlock 
        Height="45" 
        HorizontalAlignment="Left" 
        Margin="20,154,0,0" 
        Name="textBlock1" 
        Text="background =" 
        VerticalAlignment="Top" 
        Width="400" 
        FontFamily="{StaticResource PhoneFontFamilySemiLight}" 
        FontSize="{StaticResource PhoneFontSizeLarge}"/>

    Este código crea un control TextBlock, nombrado TextBlock1, situado debajo del rectángulo. El demarcado StaticResource es aplicado a los atributos FontFamilyFontSize. Al texto del TextBlock se le aplica atributos de estilo como  la fuente PhoneFontFamilySemiLight (Segoe WP SemiLight)  y el tamaño de la fuente grande (PhoneFontSizeLarge).

  3. En MainPage.xaml, agrega el siguiente código dentro del grid ContentPanel, debajo del rectángulo y el text block.

    <TextBlock 
        Height="35" 
        HorizontalAlignment="Left" 
        Margin="21,205,0,0" 
        Name="textBlock2" 
        Text="accent color = " 
        VerticalAlignment="Top" 
        Width="400" 
        Style="{StaticResource PhoneTextAccentStyle}"/>

    Este código crea un segundo TextBlock, nombrado TextBlock2 y aplica un recurso de estilo que afectan un grupo de propiedades como la fuente (PhoneTextAccentStyle), un tamaño de fuente (PhoneFontSizeNormal) y el color de primer plano (PhoneAccentBrush).

Fuente:
How to: Apply Theme Resources for Windows Phone

Interacción del Framework XNA con un control de XBOX 360

Uno de los componentes mas poderosos de XNA es la capacidad de interactividad de usuario por los diferentes medios, como un teclado y mouse, la pantalla táctil o sensores de un Windows Phone, e incluso utilizando controles de la consola Xbox 360. En este articulo, te quiero mostrar lo fácil que se puede lograr esto.

El control para XBOX 360

Lo primero que vas a necesitar evidentemente es un controlador para XBOX 360. NOTA IMPORTANTE: este control debe ser el alámbrico, y si es inalámbrico, debes contar con el receptor inalámbrico para la computadora. Los cables adaptables para controles inalámbricos no funcionarán como un control con cable, ya que estos solo energizan el control para enviar los comandos de manera inalámbricas, así que de igual forma necesitarás el receptor inalámbrico para la pc.

Implementación del ejemplo

Comienza abriendo Visual Studio 2010, y crea un proyecto de tipo Windows Game (4.0), y nómbralo “ControlXboxXNA”. No vamos a necesitar importar ninguna librería ni crear ninguna clase adicional, implementaremos todo en la clase principal. Lo que vamos a implementar como ejemplo es desplegar una imagen de un control de XBOX 360, y este indicará cuales botones del control están siendo presionados por el usuario, a modo que observes como se capturan los eventos del control. Descarga la siguiente imagen:

Y también descarga este circulo q es nuestro indicador para los botones presionados:

Una vez descargados a tu computadora, prosigue a agregarlos al Content de tu proyecto (clic derecho sobre el Content Project en el explorador de solución, clic en «agregar->elemento existente» y agrega las dos imágenes.

En la clase Game1, agrega los siguientes atributos debajo de la declaración SpriteBatch spriteBatch:

Vector2 controlPos;          //Posicion del control
Texture2D controlTex;        //Imagen del control
List<Vector2> botonesPos;    //Posicion de los botones
List<Texture2D> botonesTex;  //Indicadores de los botones activos
bool[] botonesActivos;       //Banderas de botones activos

En el método Initialize, inicializaremos las posiciones del control y los botones para los indicadores. botonesActivos es nuestro arreglo de banderas para indicar aquellos botones que esten presionados. Cada bool en el arreglo corresponderá con un botón y estará asignado en el orden en que se declaran las posiciones de botonesPos.

protected override void Initialize()
{
	controlPos = new Vector2(160,80);
	botonesPos = new List();
	botonesPos.Add(new Vector2(525, 233));   // 00 boton A
	botonesPos.Add(new Vector2(560, 198));   // 01 boton B
	botonesPos.Add(new Vector2(525, 163));   // 02 boton Y
	botonesPos.Add(new Vector2(485, 198));   // 03 boton X

	botonesPos.Add(new Vector2(493, 70));    // 04 boton RT
	botonesPos.Add(new Vector2(525, 100));   // 05 boton RB
	botonesPos.Add(new Vector2(437, 280));   // 06 boton RS
	botonesPos.Add(new Vector2(245, 70));    // 07 boton LT
	botonesPos.Add(new Vector2(210, 100));   // 08 boton LB
	botonesPos.Add(new Vector2(214, 213));   // 09 boton LS

	botonesPos.Add(new Vector2(295, 250));   // 10 boton Arriba
	botonesPos.Add(new Vector2(295, 310));   // 11 boton Abajo
	botonesPos.Add(new Vector2(265, 280));   // 12 boton Izquierda
	botonesPos.Add(new Vector2(325, 280));   // 13 boton Derecha

	botonesPos.Add(new Vector2(315, 210));   // 14 boton start
	botonesPos.Add(new Vector2(370, 205));   // 15 boton central
	botonesPos.Add(new Vector2(425, 210));   // 16 boton select

	botonesTex = new List();
	botonesActivos = new bool[17];
	base.Initialize();
}

Ahora pasemos al método LoadContent. En este método simplemente cargaremos las texturas del control, y el circulo azul para indicar botones presionados.

protected override void LoadContent()
{
	spriteBatch = new SpriteBatch(GraphicsDevice);
	controlTex = Content.Load<Texture2D>("ControlXbox");
	Texture2D botonATex = Content.Load<Texture2D>("CirculoAzul");
	foreach (var boton in botonesPos)
	{
		botonesTex.Add(botonATex);
	}
}

En cuanto al método Update, lo primero q haremos es inicializar todas las banderas de botonesActivos en false a modo de obtener un estado del control y detectar cuales botones han sido presionados en ese instante. Al finalizar el método, en el Draw se desplegarán aquellos indicadores que fueron colocados n true y al finalizar ese método, se vuelve a ejecutar Update, repitiéndose el ciclo. Copia y pega las siguientes instrucciones en el método Update.

for (int i = 0; i < botonesActivos.Length; i++)
{
	botonesActivos[i] = false;
}

GamePadState control = GamePad.GetState(PlayerIndex.One);
if (control.IsConnected)
{
	if (control.Buttons.A == ButtonState.Pressed)
	{
		botonesActivos[0] = true;
	}
	if (control.Buttons.B == ButtonState.Pressed)
	{
		botonesActivos[1] = true;
	}
	if (control.Buttons.X == ButtonState.Pressed)
	{
		botonesActivos[2] = true;
	}
	if (control.Buttons.Y == ButtonState.Pressed)
	{
		botonesActivos[3] = true;
	}
	if (control.Triggers.Right == 1f)
	{
		botonesActivos[4] = true;
	}
	if (control.Buttons.RightShoulder == ButtonState.Pressed)
	{
		botonesActivos[5] = true;
	}
	if (control.Buttons.RightStick == ButtonState.Pressed)
	{
		botonesActivos[6] = true;
	}
	if (control.Triggers.Left == 1f)
	{
		botonesActivos[7] = true;
	}
	if (control.Buttons.LeftShoulder == ButtonState.Pressed)
	{
		botonesActivos[8] = true;
	}
	if (control.Buttons.LeftStick == ButtonState.Pressed)
	{
		botonesActivos[9] = true;
	}
	if (control.DPad.Up == ButtonState.Pressed)
	{
		botonesActivos[10] = true;
	}
	if (control.DPad.Down == ButtonState.Pressed)
	{
		botonesActivos[11] = true;
	}
	if (control.DPad.Left == ButtonState.Pressed)
	{
		botonesActivos[12] = true;
	}
	if (control.DPad.Right == ButtonState.Pressed)
	{
		botonesActivos[13] = true;
	}
	if (control.Buttons.Start == ButtonState.Pressed)
	{
		botonesActivos[14] = true;
	}
	if (control.Buttons.BigButton == ButtonState.Pressed)
	{
		botonesActivos[15] = true;
	}
	if (control.Buttons.Back == ButtonState.Pressed)
	{
		botonesActivos[16] = true;
	}
}

En cuanto al método Draw, desplegamos primero la imagen del control, luego proseguimos a ir por cada bandera, desplegando aquellos indicadores que fueron colocados en true. El método Draw queda de la siguiente manera.

protected override void Draw(GameTime gameTime)
{
	GraphicsDevice.Clear(Color.CornflowerBlue);
	spriteBatch.Begin();
	spriteBatch.Draw(controlTex, controlPos, Color.White);
	for (int i = 0; i < botonesPos.Count; i++)
	{
		if (botonesActivos[i])
		{
			spriteBatch.Draw(botonesTex[i], botonesPos[i], Color.White);
		}
	}
	spriteBatch.End();
	base.Draw(gameTime);
}

Lo que queda es armar la solución y al ejecutar la aplicación, oprime botones en el control y observa en la aplicación los círculos azules indicando los botones presionados en tiempo real.

De esta forma XNA trabaja con controles de XBOX 360. Si estas teniendo problemas armando la solución, puedes descargarlo del siguiente link:

Proyecto ControlXboxXNA

Despliegue de sprites animados 2D

En el capitulo anterior se explicó la manera más directa de desplegar objetos en la escena de nuestro juego. Sin embargo, estos elementos estáticos resultan aburridos y monótonos para cualquier juego que estemos trabajando. En este capitulo te voy a explicar como hacer sprites animados. Abre Visual Studio 2010, crea un nuevo proyecto de tipo Windows Game (4.0). En el Content, descarga la siguiente imagen y agrégala al Content de tu proyecto:

Ahora en el proyecto, agrega una nueva clase y nómbrala «Animation». En esta clase implementaremos la mecánica necesaria para desplegar sprites animados.

Dinámica de animación

En principio contamos de una tira de imágenes que representan una animación. Lo que queremos implementaremos es una función capaz de desplegar una sola imagen, y trasladando la imagen, desplegamos la siguiente imagen consecutiva, y así sucesivamente, dándonos la ilusión de una imagen animada, como el efecto StopMotion.

En la clase Animation

Para esta clase, primero debemos agregar las siguientes librerías fundamentales de XNA.

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

Ahora agregaremos los siguientes atributos:

// La imagen animada representada por un grupo de imagenes
Texture2D spriteStrip;

// Valor para escalar el sprite
float scale;

// Tiempo desde la ultima vez que se actualizo la imagen
int elapsedTime;

// Tiempo de despliegue por imagen
int frameTime;

// Numero de imagenes que conforman la animacion
int frameCount;

// Indice de la imagen actual
int currentFrame;

// Color de la imagen a desplegar
Color color;

// El area de la imagen que vamos a desplegar
Rectangle sourceRect = new Rectangle();

// El area donde queremos desplegar la imagen
Rectangle destinationRect = new Rectangle();

// Ancho de la una imagen
public int FrameWidth;

// Alto de una imagen
public int FrameHeight;

// Estado de la animacion
public bool Active;

// Repetir animacion
public bool Looping;

// Posicion del sprite
public Vector2 Position;

Para esta clase, utilizara tres métodos muy importantes: InitializeUpdate y Draw. Para el método Initialize, evidentemente inicializaremos todos los atributos para luego desplegar la imagen. Observa que se le pasan la mayoría de los valores por parámetros, la idea es para mantener esta clase lo mas genérica posible, a modo de re-utilizarla para cargar múltiples sprites con diferentes imágenes y propiedades.

public void Initialize(Texture2D texture, Vector2 position,
	int frameWidth, int frameHeight, int frameCount,
	int frametime, Color color, float scale, bool looping)
{
	// Mantener copias locales de los valores pasados
	this.color = color;
	this.FrameWidth = frameWidth;
	this.FrameHeight = frameHeight;
	this.frameCount = frameCount;
	this.frameTime = frametime;
	this.scale = scale;

	Looping = looping;
	Position = position;
	spriteStrip = texture;

	// Hacer reset a los tiempos
	elapsedTime = 0;
	currentFrame = 0;

	// Activar la animacion por defecto
	Active = true;
}

En cuanto al método Update, como ya se explicó la mecánica para animar sprites, aquí se implementa la lógica para desplegar la tira de imágenes, mostrándola una a una para dar la sensación de movimiento de los sprites. La lógica del método Update es el siguiente:

public void Update(GameTime gameTime)
{
	// No actualizar si la imagen esta desactivada
	if (Active == false) 
		return;

	// Actualizar tiempo transcurrido
	elapsedTime += (int)gameTime.ElapsedGameTime.TotalMilliseconds;

	// Si elapsedTime es mayor que frame time
	// debemos cambiar de imagen
	if (elapsedTime > frameTime)
	{
		// Movemos a la siguiente imagen
		currentFrame++;
		// Si currentFrame es igual al frameCount 
		// hacemos reset currentFrame a cero
		if (currentFrame == frameCount)
		{
			currentFrame = 0;
			// Si no queremos repetir la animacion
			// asignamos Active a falso
			if (Looping == false)
				Active = false;
		}
		// Reiniciamos elapsedTime a cero
		elapsedTime = 0;
	}
	// Tomamos la imagen correcta multiplicando el currentFrame 
	// por el ancho de la imagen
	sourceRect = new Rectangle(
		currentFrame * FrameWidth, 0, FrameWidth, FrameHeight);

	// Actualizamos la posicion de la imagen en caso que esta
	// se desplace por la pantalla
	destinationRect = new Rectangle(
		(int)Position.X - (int)(FrameWidth * scale) / 2,
		(int)Position.Y - (int)(FrameHeight * scale) / 2,
		(int)(FrameWidth * scale),
		(int)(FrameHeight * scale));
}

Finalmente llegamos en el método Draw, donde simplemente invocamos el método spriteBatch.Draw SIEMPRE y cuando active este en verdadero. Aquí esta el método Draw:

public void Draw(SpriteBatch spriteBatch)
{
    if (Active)
    {
        spriteBatch.Draw(spriteStrip, destinationRect, sourceRect, color);
    }
}

Este es todo el código necesario para nuestra clase Animacion. Ahora solo debemos instanciarla, inicializarla y llamarla para desplegarla en escena y e invocar su método Update para que refresque las imágenes para lograr la animación del sprite.

En la clase Game1

Para nuestra clase principal, comenzaremos agregando los siguientes atributos:

Animation playerAnimation;
Vector2 spritePos;

Proseguimos a dirigirnos al método LoadContent, donde cargaremos la imagen del Content a la variable playerTexture e instanciar la posición inicial del sprite, y se la enviamos por parámetros al método Initialize a nuestro objeto playerAnimation:

protected override void LoadContent()
{
	spriteBatch = new SpriteBatch(GraphicsDevice);

	playerAnimation = new Animation();
	Texture2D playerTexture = Content.Load<Texture2D>("Ken");

	spritePos = new Vector2(
		GraphicsDevice.Viewport.TitleSafeArea.X +
		GraphicsDevice.Viewport.TitleSafeArea.Width / 2,
		GraphicsDevice.Viewport.TitleSafeArea.Y +
		GraphicsDevice.Viewport.TitleSafeArea.Height / 2);

	playerAnimation.Initialize(playerTexture,  
		spritePos, 106, 110, 6, 80, Color.White, 1f, true);
}

Ahora en el método Update, basta con solo invocar el método Update de nuestro objeto playerAnimation:

playerAnimation.Update(gameTime);

Y Finalmente, en el método Draw, invocamos el método Draw de nuestro objeto playerAnimation. Recuerda que debes encapsular esta instrucción con los metodos spriteBatch.Begin(); y spriteBatch.End(); de lo contrario, no veras ningún elemento desplegado en escena.

protected override void Draw(GameTime gameTime)
{
	GraphicsDevice.Clear(Color.CornflowerBlue);
	spriteBatch.Begin();
	playerAnimation.Draw(spriteBatch);
	spriteBatch.End();
	base.Draw(gameTime);
}

Ya en este punto, queda armar la solucion y esperar que se ejecute tu aplicacion con una animacion de Ken corriendo. Te anexo una captura de la aplicacion:

Experimenta las propiedades de la clase Animacion, y analiza los resultados. Qué ocurre si le aumentas el currentTime? Qué ocurre si le aumentas el scale? Es importante que estudies el comportamiento de esta clase para que tengas un mayor control y entendimiento sobre sprites animados. Este es un camino para animar sprites, pero puede haber muchas otras formas para hacerlo.

Si presentas problemas armando tu solución, puedes descargar el proyecto final en el siguiente enlace:

Proyecto AnimacionXNA

Ejemplo XNA parte 3 – Interactividad con el Jugador

Ya contamos con la estructura básica de nuestro juego con el Contenido Gráfico, una clase Jugador, donde posee los atributos necesarios como la vida de la aeronave, la puntuación acumulada, despliegue del elemento en la escena, entre otros. El siguiente paso para nuestro juego de ejemplo será incluir la interacción del usuario.

En la clase Game1.cs

Agregaremos tres nuevos atributos a la clase principal de nuestro juego. Anéxalas debajo de la declaración Jugador jugador.

KeyboardState KeyboardActual;
KeyboardState KeyboardPrevio;
float jugadorVel;

En Initialize, asignaremos un valor arbitrario para la velocidad de desplazamiento de la nave:

protected override void Initialize()
{
	jugador = new Jugador();
	jugadorVel = 8.0f;

	base.Initialize();
}

Ahora deberás agregar un método UpdateJugador, donde este irá chequeando si alguna de las teclas de dirección del teclado ha sido presionada, y en caso que lo estén, se suman o restan el valor de velocidad de la nave para desplazarla a lo largo de la pantalla. Al final de estas condiciones, usando el método Clamp, validamos que la nave del jugador permanezca dentro de las dimensiones de la pantalla. Pega este método antes del método Update.

private void UpdateJugador(GameTime gameTime)
{
	if (KeyboardActual.IsKeyDown(Keys.Left))
	{
		jugador.Position.X -= jugadorVel;
	}
	if (KeyboardActual.IsKeyDown(Keys.Right))
	{
		jugador.Position.X += jugadorVel;
	}
	if (KeyboardActual.IsKeyDown(Keys.Up))
	{
		jugador.Position.Y -= jugadorVel;
	}
	if (KeyboardActual.IsKeyDown(Keys.Down))
	{
		jugador.Position.Y += jugadorVel;
	}
	jugador.Position.X = MathHelper.Clamp(jugador.Position.X, 0, 
		GraphicsDevice.Viewport.Width - jugador.Width);
	jugador.Position.Y = MathHelper.Clamp(jugador.Position.Y, 0, 
		GraphicsDevice.Viewport.Height - jugador.Height);
}

Por último, evidentemente debemos invocar este método constantemente dentro del Game Loop. Eso se hace llamando UpdateJugador dentro del método Update, y previamente asignar los estados de teclado (KeyboardPrevio y KeyboardActual).

protected override void Update(GameTime gameTime)
{
	if (GamePad.GetState(PlayerIndex.One).Buttons.Back == 
                ButtonState.Pressed) this.Exit();

	KeyboardPrevio = KeyboardActual;
	KeyboardActual = Keyboard.GetState();
	UpdateJugador(gameTime);

	base.Update(gameTime);
}

En este punto, el juego ya cuenta con una clase Jugador donde se despliega el elemento en escena, y además puede ser controlado con las flechas de dirección del teclado.

Ya con la interactividad del juego es un gran avance, sin embargo, todavía nos falta agregarle el factor diversión. No te preocupes que todo esto se ira implementando mas adelante y sin mayor dificultad. Puedes descargarte el proyecto en esta fase en el siguiente enlace:

Proyecto Shooter Parte 3

Interacción en XNA para Windows Phone: Usando el Acelerómetro

El acelerómetro es un sensor que se encuentran en la mayoría de dispositivos móviles de hoy en día. Básicamente, este sensor constantemente mide el cambio de las aceleraciones asociadas con el fenómeno de peso experimentada por una masa de prueba que se encuentra en el marco de referencia del dispositivo. En otras palabras, es un sensor capaz de detectar el mínimo movimiento de posición del dispositivo en cualquiera de sus tres ejes. Para el caso particular de Windows Phones, es importante que tengas presente la convención de los ejes para este tipo de dispositivos:

Es importante tener en cuenta la convención de ejes de coordenadas del dispositivos con el que queremos desarrollar una aplicación que involucre utilizar el acelerómetro. Otra facilidad que nos ofrece el emulador de Windows Phone es la funcionalidad de simular el cambio de orientación del teléfono, ideal cuando queremos hacer pruebas de nuestra aplicación utilizando el acelerómetro para aquellos desarrolladores que no cuentan con el teléfono físico.

Puedes establecer la posición inicial del teléfono (modo retrato levantado, modo horizontal levantado, modo retrato acostado y modo horizontal acostado), y en base a esa posición deberás implementar tu aplicación. Ahora prosigamos a implementar un ejemplo sencillo donde una pequeña imagen que se desplazara de acuerdo a la orientación a la que se encuentre el dispositivo.

Ejemplo de una aplicación utilizando el acelerómetro

Abre Visual Studio 2010 y crea un proyecto tipo Windows Phone Game (4.0), y nómbralo «AcelerometroXNA». Ahora debemos agregar una referencia para utilizar el Acelerómetro. Dale clic-derecho a la carpeta Referencias y dale clic a «agregar Referencia», y busca la librería .NET «Microsoft.Devices.Sensors».

Una vez agregada esa referencia, importa la librería en la clase Game1.cs:

using Microsoft.Devices.Sensors;

Ahora, nuevamente agreguemos una imagen al Content de nuestro proyecto. En este ejemplo usaremos la siguiente imagen:

Dale clic-derecho a AcelerometroXNAContent, y selecciona «agregar->ítem existente», y selecciona la imagen que descargaste previamente.

Regresando a nuestra Game1.cs, en el constructor Game1, reemplázalo por este:

Accelerometer accelerometer;
Texture2D circuloTex;
Vector2 circuloPos;
Vector2 circuloVel;

En el constructor de Game1.cs, debemos establecer explícitamente las dimensiones de la pantalla, a modo que este evite de rotar automáticamente independientemente a como manipulemos el dispositivo.

public Game1()
{
	graphics = new GraphicsDeviceManager(this);
	graphics.PreferredBackBufferHeight = 800;
	graphics.PreferredBackBufferWidth = 480;
	Content.RootDirectory = "Content";
	TargetElapsedTime = TimeSpan.FromTicks(333333);
	InactiveSleepTime = TimeSpan.FromSeconds(1);
}

En el método Initialize, inicializamos la posición de nuestra imagen en el centro de la pantalla, instanciamos nuestra clase acelerómetro, y le agregamos un manejador de evento (AccelReadingChanged) que se disparara cada vez que el sensor detecte un cambio de valor de aceleración en cualquiera de sus tres ejes; y por último, hacemos Start al acelerómetro para que comience a recibir datos del sensor.

protected override void Initialize()
{
	circuloPos = new Vector2(
		graphics.PreferredBackBufferWidth / 2, 
		graphics.PreferredBackBufferHeight / 2);
	accelerometer = new Accelerometer();
	accelerometer.ReadingChanged += new EventHandler
		<AccelerometerReadingEventArgs>(AccelReadingChanged);
	accelerometer.Start();
	base.Initialize();
}

Ahora proseguimos a cargar nuestra imagen de marte a nuestra variable de textura circuloTex. Esto se realiza en el método LoadContent.

protected override void LoadContent()
{
	spriteBatch = new SpriteBatch(GraphicsDevice);
	circuloTex = Content.Load<Texture2D>("planet-mars");
}

Algo muy importante que debemos hacer en el método UnloadContent particularmente en este ejemplo. Debemos liberar el recurso del acelerómetro cuando finalice la ejecución de la aplicación. Esto se hace invocando al método Stop de nuestro objeto accelerometer. Luego hacemos Unload a nuestro contenido para liberar el contenido grafico cargado a memoria.

protected override void UnloadContent()
{
	accelerometer.Stop();
	Content.Unload();
}

En el método Initialize habíamos declarado un método manejador de evento del acelerómetro llamado AccelReadingChanged. He aquí dicho método. Puedes observar que al circuloVel se le suman los valores detectados por el sensor, y estos a su vez son sumados a la posición de nuestra elemento grafico. Solo estamos trabajando con el eje X e Y, lo que significa que la posición inicial del dispositivo es horizontal, con la orientación de pantalla modo Landscape.

void AccelReadingChanged(object sender, AccelerometerReadingEventArgs e)
{
	circuloVel.X += (float)e.X;
	circuloVel.Y -= (float)e.Y;
	circuloPos += circuloVel;
}

En cuanto al método Update, que debemos hacer? Obviamente asegurarnos que nuestro planeta marte no se salga de los bordes de la pantalla. Esto es cuidando que la posición de nuestra imagen no sobrepase las dimensiones de nuestro viewport. Esto se hace con las siguientes instrucciones:

Viewport viewport = graphics.GraphicsDevice.Viewport;
if (circuloPos.X < 0) 
{ 
	circuloPos.X = 0; 
	circuloVel.X = 0; 
} 
else if (circuloPos.X > viewport.Width - circuloTex.Width)
{
	circuloPos.X = viewport.Width - circuloTex.Width;
	circuloVel.X = 0;
}
if (circuloPos.Y < 0) 
{ 
	circuloPos.Y = 0; 
	circuloVel.Y = 0; 
} 
else if (circuloPos.Y > viewport.Height - circuloTex.Height)
{
	circuloPos.Y = viewport.Height - circuloTex.Height;
	circuloVel.Y = 0;
}

Finalmente, llegamos al método Draw, donde simplemente desplegamos nuestra imagen en la posición vinculada al cambio de valores que se encuentre recibiendo el acelerómetro.

protected override void Draw(GameTime gameTime)
{
	GraphicsDevice.Clear(Color.CornflowerBlue);
	spriteBatch.Begin();
	spriteBatch.Draw(circuloTex, circuloPos, Color.White);
	spriteBatch.End();
	base.Draw(gameTime);
}

Ya el proyecto listo, proseguimos a armar nuestra solución y ejecutamos. Una vez que se ejecute el emulador, despliega el panel de herramientas adicionales, en la sección del acelerómetro. Ajusta la orientación inicial a «Landscape Flat», y dale clic a la esfera roja del emulador y trasládala hacia diversas direcciones para que observes como la imagen de marte se desplaza por la pantalla de acuerdo a la dirección de orientación del dispositivo.

De esta forma se trabaja con el Acelerómetro para Windows Phone utilizando el Framework XNA. Si tu proyecto presenta problemas de armar la solución, puedes descargarte el ejemplo:

Proyecto Acelerometro XNA

Interacción en XNA para Windows Phone: Reconocimiento de Gestos

XNA posee funcionalidades innatas para reconocer los gestos más comunes con los que interactuamos normalmente con cualquier dispositivo móvil en la actualidad. Simplemente se debe habilitar el reconocimiento de gestos, y así el juego tendrá la capacidad de interpretar cualquier tipo de gesto realizado por parte del usuario. Los gestos más comunes se muestran en el siguiente diagrama.

Un Tap es el gesto cuando le damos un toque rápido en algún punto de la pantalla. Double Tap son dos toques consecutivos en un mismo punto, como hacer un doble clic. El Pinch y Stretch son el típico gesto que conocemos para hacer zoom-in y zoom-out en una foto o una imagen. El gesto Pan ocurre cuando deseamos trasladarnos por un scrollbar, o navegar por un mapa o imagen. Flick es un toque rápido con alguna dirección, lo que causa una especie de inercia a algún elemento que estemos trasladando.

A continuación implementaremos un ejemplo sencillo de reconocimiento de gestos utilizando el Framework XNA para Windows Phone.

Ejemplo de Reconocimiento de Gestos

Crea un proyecto Windows Phone Game (4.0), y nómbralo «GestosXNA», selecciona versión del sistema operativo 7.1. Haz un clic-derecho sobre el Content del proyecto, y agrega un nuevo Ítem de tipo «Sprite Font», y nómbralo «SpriteFont1».

En la clase Game1.cs, después de la declaración del atributo SpriteBatch spriteBatch; agrega los siguientes atributos:

SpriteFont spriteFont;
String message = "Realiza un gesto";
Vector2 messagePos = Vector2.Zero;
Color color = Color.Black;

Ahora, en el método Initialize, y agrega la siguiente instrucción antes de la línea base.Initialize(); y agrega lo siguiente:

TouchPanel.EnabledGestures = GestureType.Tap | GestureType.DoubleTap | 
                        GestureType.Hold | GestureType.HorizontalDrag | 
                        GestureType.VerticalDrag | GestureType.FreeDrag | 
                        GestureType.DragComplete | GestureType.Pinch | 
                        GestureType.PinchComplete | GestureType.Flick;

De esta forma, estamos habilitando cuales gestos deseamos que nuestra aplicación sea capaz de reconocer en tiempo de ejecución. En el método LoadContent, cargaremos la fuente agregada del Content, de esta manera:

protected override void LoadContent()
{
	spriteBatch = new SpriteBatch(GraphicsDevice);
	spriteFont = Content.Load<Texture2D>("SpriteFont1");
}

Ahora, en el método Update es donde en cada ciclo del Game Loop, el juego realizara una captura del estado de la pantalla táctil, registrando gestos realizados por el usuario, y los irá reconociendo en tiempo real. El método completo está implementado de la siguiente forma:

if (TouchPanel.IsGestureAvailable)
{
	GestureSample gesture = TouchPanel.ReadGesture();
	switch (gesture.GestureType)
	{
		case GestureType.Tap:
			message = "Tap";
			color = Color.Red;
			break;
		case GestureType.DoubleTap:
			message = "Doble Tap";
			color = Color.Orange;
			break;
		case GestureType.Hold:
			message = "Hold";
			color = Color.Yellow;
			break;
		case GestureType.HorizontalDrag:
			message = "Arrastre horizontal";
			color = Color.Blue;
			break;
		case GestureType.VerticalDrag:
			message = "Arrastre vertical";
			color = Color.Indigo;
			break;
		case GestureType.FreeDrag:
			message = "Arrastre libre";
			color = Color.Green;
			break;
		case GestureType.DragComplete:
			message = "Gesto arrastre completado";
			color = Color.Gold;
			break;
		case GestureType.Flick:
			message = "Flick";
			color = Color.Violet;
			break;
		case GestureType.Pinch:
			message = "Pinch";
			color = Color.Violet;
			break;
		case GestureType.PinchComplete:
			message = "Gesto pinch completado";
			color = Color.Silver;
			break;
	}
	messagePos = gesture.Position;
}

Primero se verifica si el TouchPanel tiene el reconocimiento de gestos habilitados. Luego TouchPanel.ReadGesture() devuelve un GestureSample en cada ciclo del GameLoop, que es analizado en el switch(gesture), y se detendrá en el caso correspondiente al gesto reconocido, cambiando el valor del string message, informando el gesto detectado, y finalmente, se le asigna la posición del mensaje donde ocurrió el gesto analizado.

En el método Draw, proseguimos a dibujar nuestro string:

protected override void Draw(GameTime gameTime)
{
	GraphicsDevice.Clear(Color.CornflowerBlue);
	spriteBatch.Begin();
	spriteBatch.DrawString(spriteFont, message, messagePos, color);
	spriteBatch.End();
	base.Draw(gameTime);
}

Listo! Ahora compila la solución y si todo esta correctamente codificado, veras la siguiente pantalla:

Ya con la aplicación ejecutándose en el emulador Windows Phone, realiza gestos utilizando tu mouse y verás que son reconocidos por nuestra aplicación, mostrando un mensaje sobre el gesto detectado, y en la posición donde ocurrió.

Desafortunadamente no podrás realizar gestos multitouch como el Pinch; para eso necesitarías hacer deploy del proyecto a un teléfono físico. Sin embargo, puedes realizar cualquier otro gesto habilitado. Experimenta y realiza todos los gestos disponibles.

Si tu proyecto presenta errores de compilación, puedes descargarte el ejemplo:

Proyecto GestosXNA

Despliegues de Sprites en 2D

Bien, ya visto en articulos anteriores todo el basamento teórico de como funciona el Framework XNA, las versiones que han salido desde su lanzamiento en el 2007, el soporte para las diversas plataformas de Microsoft (Windows PC, Xbox 360 y Windows Phone), el funcionamiento de XNA y por último, la estructura de un proyecto de un Windows Game XNA en Visual Studio, es hora que empecemos a codificar y dibujar elementos.

Paso 1. Crear el proyecto

Debes abrir Visual Studio 2010 con XNA Game Studio 4 previamente instalado, y crear un proyecto Windows Game (4) como se mostró en el artículo anterior.

Paso 2. Agregar una imagen a nuestro Content

Ahora debemos agregar una carpeta en el Content donde colocaremos nuestras imágenes. No es requerido crear un directorio, pero lo recomiendo para mantener nuestro contenido gráfico organizado. Simplemente da un clic-derecho en tu proyecto Content, le das clic a agregar->nueva carpeta, y lo nombraremos «Imagenes».

Para agregar una imagen (que la pueden encontrar aqui para descargar), dale clic-derecho a la carpeta «Imagenes» ya creada, luego clic en agregar->item existente. Te saldrá un explorador de archivos, para que busques el archivo de imagen que deseas agregar a tu proyecto. Selecciona la imagen que descargaste previamente (smiley.png), y le das clic a «Agregar»).

Paso 3. Dibujar el sprite en la Escena

Ya que tenemos nuestra imagen en el Content de nuestro proyecto, debemos cargarla y desplegarla escribiendo código. Lógicamente para desplegar esta imagen, dependemos lógicamente de dos cosas: Posición y Textura. Para eso, agrega las siguientes dos variables dentro de tu clase principal (Game1), debajo de spriteBatch. El código se vería de la siguiente manera:

Vector2 es un tipo de datos que posee dos campos float x,y; a modo de trabajar con coordenadas de dos dimensiones. Ininializa posicion en el método Initialize:

Ahora procedemos a cargar la imagen a nuestro juego. Esto se hace en el método LoadContent. El código quedaría así:

Puedes observar que se instancia la variable spriteBatch (el cual puedes verlo por los momentos como el pincel encargado de pintar todos los elementos en la escena de nuestro juego). También observa como se carga una textura2D con el método Load. Toma en cuenta el string que pasamos como parámetro; es la ruta del elemento que queremos cargar. Por esto, es importante colocar en qué directorio se encuentra si esta organizado de esta manera.

Paso 4. Desplegar nuestro sprite en la escena

Recapitulando, hemos agregado nuestra imagen archivo al contenido de nuestro proyecto; hemos agregado una variable para la posición donde desplegaremos nuestro sprite, y declaramos una variable Texture2D, donde realizamos la carga de nuestra imagen del contenido a la aplicación. Ahora, sin más preámbulos, dibujemos nuestro sprite. Esto se implementa en el método Draw.

Para pintar nuestro sprite, debemos utilizar nuestra variables spriteBatch, ya que este es el encargado de dibujar los elementos en la escena como ya mencione a priori. Todos los elementos que vayamos a dibujar se hacen mediante su método Draw(), y estos deben estar encerrados dentro de los métodos spriteBatch.Begin() y spriteBatch.End(). Muy importante estos dos métodos, porque estamos avisando a nuestra aplicación que vamos a pintar elementos, y cuando terminemos, invocamos el método End(). En nuestro caso, nuestro código se vera así:

Fíjate el método spriteBatch.Draw(). Solo estamos pasando la textura que deseamos desplegar, la posición, y el color que queremos que sea desplegado. Si colocamos Blanco, se mostraran los colores originales de la imagen. Cualquier otro color modificara los colores de nuestro sprite.

Finalmente, al ejecutar nuestra aplicación, tendrás un resultado como este:

Sencillo, no crees? Esta es la manera mas elemental para desplegar un sprite en nuestro juego. El método Draw que utilizamos es el mas básico disponible, ya que este método cuenta con 7 formas distintas de invocar con diversos parámetros, incluyendo efectos, manejo de rectángulos para dibujar ciertas áreas, etc. Para proyectos más complejos, se trabajan con clases donde encapsulan estas propiedades (textura, posición), y se le agregan mas propiedades para ser mas flexibles ante transformaciones y efectos.

Estructura de un proyecto XNA

Para crear un proyecto XNA, debemos abrir Visual Studio 2010 con el XNA Game Studio 4.0 previamente instalado. Da un clic en Archivo, Nuevo Proyecto, y ve a la categoría de plantillas «XNA Game Studio», y selecciona la plantilla Windows Game (4.0). Escribe el nombre de proyecto deseado y le das click en  aceptar.

Una vez creado el proyecto, verás la clase principal del proyecto que por defecto se llama Game1. En el Solution Explorer, veras la siguiente estructura de carpetas.

Proyecto WindowsGame1

En esta sección de la solución es donde se desarrolla toda la lógica del juego. La clase principal es la llamada Class1, donde se encuentra bien definidos los métodos principales a implementar acorde al funcionamiento del framework XNA. Existe otra clase estática llamada Program.cs, donde aquí en realidad es el que invoca la Clase1 para ejecutarla; sin embargo, tu labor es implementar tu juego es en la clase Class1.

También veras un archivo llamado «Game.ico», donde este es un pequeño icono de 32×32 pixeles que se coloca en la esquina superior izquierda de la ventana de nuestro juego al momento de ejecutar nuestra aplicación. Este icono también se mostrara si lo anclas a la barra de inicio.

Entre otros elementos dentro de este proyecto, se encuentra el directorio “Properties”, donde se encuentra el archivo “AssemblyInfo.cs”, que es el archivo configuración de ensamblado de nuestro proyecto. Al abrirlo, veras que contiene toda la información acerca de la versión de la aplicación, el Titulo del juego, producto, descripción, año, etc. También se encuentra el directorio «References», donde en esta se encuentra referencias a las librerías fundamentales requeridas para el funcionamiento de este proyecto. Si deseas trabajar con librerías externas encontradas en la web (Como trabajar con el Kinect, o librerias de físicas, etc.), con descargarlas y hacerles referencia, ya se encuentran disponibles para utilizarlas en tu juego.

En este punto, te sugiero que trabajes con directorios de clases, a modo de trabajar un proyecto de manera organizada por grandes módulos esenciales, como física, sonido, gráficos, interacción entrada/salida, etc.

Proyecto WindowsGame1Content

Para este proyecto de tipo Windows Game Library (4.0) que se genera automáticamente en la solución al momento que se crea un Windows Game (4.0), es donde se coloca todo el contenido gráfico audiovisual del juego. Se recomienda que organices el contenido en directorios, para imágenes, archivos de configuración, sonidos, videos, música de fondo, etc. Esta sección ya se encuentra referenciada por el proyecto principal (que se puede ver dentro del directorio «Content References»).

Diseña un sitio como este con WordPress.com
Comenzar