Generation Apps – Día 24 – Crear aplicaciones de prueba para Windows Phone

El Windows Phone Store te permite diseñar en implementar modos de prueba de tu aplicación. La experiencia muestra que usuarios disfrutan probar una aplicación mas limitada antes de comprarla.

La plataforma Windows Phone Application te facilita la capacidad de proveer versiones de prueba y completas dentro de un mismo contenedor XAP. Usuarios deseando comprar una aplicación que están probando pueden hacerlo con una experiencia muy similar al Windows Phone Store.

No hay restricciones acerca de como tu diseñes la experiencia de prueba de tu aplicación. Puedes determinar el grado de funcionalidad que le quieres exponer al usuario, ya sea el modo de prueba tenga una duración limitada, o como quieres alentar al usuario a comprar tu aplicación. También puedes determinar si los datos y estados de la aplicación son mantenibles si un usuario desea descargar la versión de prueba de la aplicación.

Cuando quieras publicar un modo de prueba de aplicación al Windows Phone Store, marca el check box de Trial Application y el cliente del Windows Phone Store mostrará un botón Try para descargar la aplicación en modo de prueba.

Visión General de Modo de Prueba

Para implementar un modo de prueba de tu aplicación, debes definir e implementar como el comportamiento de prueba difiere del comportamiento de la versión completa de la aplicación. Para ejecutar la aplicación en el modo correcto cuando es ejecutado por el usuario, tu código debe determinar si la aplicación se esta ejecutando en versión de prueba o completa. Para proveer una ruta de compra tu código debe ejecutar el cliente del Windows Phone Store.

El estado de prueba/full de la ejecución de derechos del usuario para una aplicación son mantenidos en una licencia. Cuando el usuario prueba un aplicación, Licencias de prueba no expiran pero cuando el usuario compra la versión completa, se descarga la licencia completa de la aplicación.

Para determinar si la licencia en la aplicación instalada en un Windows Phone es modo Prueba o Completa, Windows Phone 7 provee  métodos que pueden retornar TRUE si la aplicación esta ejecutándose bajo la licencia de prueba y FALSE si la aplicación se esta ejecutando bajo una licencia completa.

Para permitir a un usuario corriendo una aplicación de prueba y desea comprar la versión completa, Windows Phone 7 provee métodos para iniciar la aplicación cliente de Windows Phone  Store en la pagina de compra de la aplicación.

Desarrolladores del Framework XNA deberán usar la clase GamerServices.Guide para crear la experiencia de prueba y compra. Utiliza la propiedad Guide.IsTrialMode para obtener el tipo de licencia actual y el método Guide.ShowMarketplace para iniciar la experiencia de compra del videojuego.

Aplicaciones de Silverlight pueden usar métodos del Framework XNA ya descritos en el párrafo anterior o usando el método IsTrial() para obtener el tipo de licencia actual directamente, y el método Show() de la clase MarketplaceDetailTask para iniciar la experiencia de compra.

Implementando el modo de prueba

Determinando el estado de modo prueba y navegación hacia el Windows Phone Store para la compra de la aplicación debe ser simulada por tu código cuando estas probando o depurando tu aplicación. Métodos para esta funcionalidad no funcionan en modos de debug o testing ya que al requerir la licencia de la aplicación debe estar completa y publicada en el Store.

Aplicaciones que usan el Framework XNA deberían siempre usar la clase GamerServices.Guide para usar esta funciones. Esta clase posee funcionalidades de simulación de  modos de prueba y compra.

Aplicaciones de Silverlight pueden utilizar la misma clase GamerServices.Guide o implementar su propio comportamiento personalizado. Usando llamadas de GamerServices.Guide en una aplicación Silverlight puede ahorrarte trabajo simulando el estado de licencia de prueba durante el momento de depuración y testing. Usando métodos como LicenceInformation.IsTrial y MarketplaceDetailTask.Show permiten mas oportunidades para personalizar tus métodos de depuración y testing.

Buenas practicas para Modos de Prueba

  • Aplicaciones XNA deberían siempre utilizar la clase GamerServices.Guide para implementar modos de prueba.

  • Al asignar la bandera Guide.SimulateTrialMode a TRUE, siempre enciérralos en un bloque #if DEBUG / #endif

  • Revisar el estado IsTrial() siempre cuando la aplicación cargue o resuma su ejecución.

  • No dependas de modos de prueba de tiempo limitado para proteger el valor de tu aplicación. Si no se valida esto correctamente, el usuario puede simplemente re-instalar tu aplicación para utilizar nuevamente todas las funcionalidades que ofrece.
  • Provee una forma fácil a los usuarios para comprar la aplicación una vez instalada en modo de prueba.

  • Informa al usuario el por que deberían comprar la aplicación completa. Típicamente en el caso de los juegos, la aplicación muestra screenshots de mas niveles, ítems y puntos que pueden alcanzar si compran la versión completa.

Fuente:
Creating Trial Applications Overview for Windows Phone

Generation Apps – Día 19 – El micrófono con Silverlight/XNA

Windows Phone provee una plataforma multimedia robusta que permite aplicaciones a hacer streaming de video y audio, manejo del radio, y la integración con el Hub de música y videos. En la lección de este día, te mostrare la forma como obtener input de audio utilizando el micrófono del Windows Phone con aplicaciones Silverlight usando la clase Microsoft.Xna.Framework.Audio.Microphone.

  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.xaml en el diseñador de Visual Studio.

  7. En el Solution Explorer, da un clic derecho a References -> Add Reference…

  8. Agrega la librería Microsoft.Xna.Framework de la lista de componentes .NET y da clic a OK.

  9. Si ves un dialogo advirtiéndote acerca de agregar assembly de Silverlight, da clic en Yes.

  10. Agrega las siguientes directivas en el archivo MainPage.xaml.cs

    using System.IO;
    using System.Windows.Threading;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Audio;
  11. En MainPage.xaml.cs, declara las siguientes variables globales en tu clase MainPage

    public partial class MainPage : PhoneApplicationPage
    {
        Microphone microphone = Microphone.Default;
        byte[] buffer;
        MemoryStream stream = new MemoryStream();
        SoundEffect sound; 
        // ...
  12. Ya que estamos usando XNA Game Studio en una aplicación Silverlight, debemos simular el Game Loop que XNA implementa por nosotros. Agrega el siguiente código en el constructor de la clase MainPage después de la llamada InitializeComponent para simular el Game Loop de XNA:

    // Timer to simulate the XNA Game Studio game loop (Microphone is from XNA Game Studio)
    DispatcherTimer dt = new DispatcherTimer();
    dt.Interval = TimeSpan.FromMilliseconds(50);
    dt.Tick += delegate { try { FrameworkDispatcher.Update(); } catch { } };
    dt.Start();
  13. Ahora agrega un manejador de evento Microsoft.BufferReady en el constructor de la clase MainPage:

    microphone.BufferReady += new EventHandler<EventArgs>(microphone_BufferReady);

    Tu declaración de variables y eventos del constructor se deberá ver algo como esto:

    public partial class MainPage : PhoneApplicationPage
    {
        Microphone microphone = Microphone.Default;
        byte[] buffer;
        MemoryStream stream = new MemoryStream();
        SoundEffect sound;
    
        // Constructor
        public MainPage()
        {
            InitializeComponent();
    
            // Timer to simulate the XNA Game Studio game loop (Microphone is from XNA Game Studio)
            DispatcherTimer dt = new DispatcherTimer();
            dt.Interval = TimeSpan.FromMilliseconds(33);
            dt.Tick += delegate { try { FrameworkDispatcher.Update(); } catch { } };
            dt.Start();
    
            microphone.BufferReady += new EventHandler<EventArgs>(microphone_BufferReady);
        } 
        ...
  14. Implementa el manejador de evento BufferReady. Este manejador copia los datos del micrófono a un buffer y escribe ese buffer a un stream:

    void  microphone_BufferReady(object sender, EventArgs e)
    {
        microphone.GetData(buffer);
        stream.Write(buffer, 0, buffer.Length);
    }
  15. Ahora agrega una manera que el usuario pueda activar el micrófono para captar audio. Puedes agregar un botón Start (nombrado recordButton) donde este crea un buffer de 1 segundo y empieza a coleccionar datos llamando al método Microphone.Start.

    private void recordButton_Click(object sender, RoutedEventArgs e)
    {
        microphone.BufferDuration = TimeSpan.FromMilliseconds(1000);
        buffer = new byte[microphone.GetSampleSizeInBytes(microphone.BufferDuration)];
        microphone.Start();
    }
  16. Agrega una manera para que el usuario pueda detener la captura de audio del micrófono. Puedes agregar un botón Stop (nombrado stopButton), y agregarle un manejador de evento clic, donde este chequea si el estado del micrófono esta activo, lo detiene.

    private void stopButton_Click(object sender, RoutedEventArgs e)
    {
        if (microphone.State == MicrophoneState.Started)
        {
            microphone.Stop();
        }
    }
  17. Finalmente, agrega un botón para que el usuario pueda reproducir la grabación capturada por el micrófono. Agrégale un manejador de evento Clic, donde este instancia un nuevo objeto SoundEffect usando el stream de donde los datos de audio fueron guardados. Luego, se invoca SoundEffect.Play para reproducir la grabación.

    private void playButton_Click(object sender, RoutedEventArgs e)
    {
        sound = new SoundEffect(stream.ToArray(), microphone.SampleRate, AudioChannels.Mono);
        sound.Play();
    }

Observa que con unos cuantos pasos, puedes acceder al micrófono para hacer cualquier cantidad de cosas que desees implementar en tu aplicación.

Fuente:
How to: Acces the Microphone in a Silverlight Application for Windows Phone

Ejemplo XNA parte 9 – Despliegue de explosiones

Ya tenemos nuestra nave con un arma para detonar las minas y defenderse, pero estas desaparecen cuando son destruidas. Aburrido, verdad? En esta parte de nuestro juego ejemplo de XNA implementaremos el despliegue de explosiones para los enemigos destruidos por la aeronave.

En la clase Game1

Comienza agregando lo siguientes atributos:

Texture2D explosionTexture;
List<Animation> explosions;

Estamos declarando la textura que tendrá el efecto de animación de la explosión, y una lista de Animacion para las explosiones, ya que puede que más de un enemigo sea destruido por el Jugador dentro de la escena, ocasionando múltiples explosiones. En el método Initialize, agrega esta línea al final:

explosions = new List<Animation>();

Simplemente estamos instanciando la lista de explosiones. Ahora en el LoadContent al final, cargaremos la imagen de explosión en nuestra textura con la siguiente instrucción:

explosionTexture = Content.Load<Texture2D>("explosion");

Es necesario contar con un método para agregar explosiones a la lista, para eso creamos el método AddExplosion, como se ve a continuación:

private void AddExplosion(Vector2 position)
{
	Animation explosion = new Animation();
	explosion.Initialize(explosionTexture,position, 
		134, 134, 12, 45, Color.White, 1f,false);
	explosions.Add(explosion);
}

Análogo cuando implementamos los enemigos, este método instancia una animación, la inicializa y finalmente la agrega en la lista de explosiones. Ahora el detalle es, Cuando llamamos este método? Ese es el truco, debemos llamarlo cuando la vida de los enemigos sea menor o igual a cero. Ve al método UpdateEnemies, y dentro de la instrucción if(enemies[i].Active == false), agrega las siguientes instrucciones debajo de ‘{‘:

// Si la vida del enemigo i es <= 0
if (enemies[i].Health <= 0)
{
	// Agregamos una explosion
	AddExplosion(enemies[i].Position);
}

Nota que cuando la condición se cumple, le estamos pasando como parámetro cuál enemigo esta siendo destruido, y la posición donde se mostrara la explosión. Ahora agregaremos el método UpdateExplosions para actualizar los elementos de explosión en la escena:

private void UpdateExplosions(GameTime gameTime)
{
	for (int i = explosions.Count - 1; i >= 0; i--)
	{
		explosions[i].Update(gameTime);
		if (explosions[i].Active == false)
		{
			explosions.RemoveAt(i);
		}
	}
}

Ahora debemos invocar este método en Update, después de UpdateProjectiles():

// Actualizando las explosiones
UpdateExplosions(gameTime);

En cuanto al método Draw, debemos dibujar todos los elementos de la lista de explosiones. Agrega las siguientes líneas de instrucciones al final del método:

// Dibujamos las explosiones
for (int i = 0; i < explosions.Count; i++)
{
	explosions[i].Draw(spriteBatch);
}

Ya terminamos de implementar las explosiones. Ahora arma la solucion y ejecuta el juego. Si todo esta bien codificado, se debera desplegar nuestro juego, y la aeronave destruyendo las minas, causando multiples explosiones. Aqui te muestro el snapshot del juego ejemplo en ejecucion con explosiones.

Ya ves el juego mas llamativo, verdad? Sin embargo, le faltan efectos de sonido a nuestro Shooter para hacerlo mas interesante. Ese será el siguiente paso. Puedes descargarte el proyecto en este punto dando clic en el siguiente enlace:

Proyecto Shooter Parte 9

Ejemplo XNA parte 8 – Implementado la acción disparar del Jugador

Ya se puede observar el progreso de nuestro juego ejemplo. Ya cuenta con elementos animados como la nave y los enemigos, un fondo que le da una profundidad y una sensación de mayor movimiento en el entorno. Por los momentos, la única forma de destruir los enemigos es estrellando la nave contra ellos. Es hora de equipar la nave con un arma para disparar proyectiles contra las minas explosivas.

Comienza agregando una nueva clase llamada Projectile.cs; y elimina las declaraciones using que trae por defecto, y agrega las siguientes:

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

Ya que estaremos trabajando con clases y metodos del framework XNA. Ahora prosigue a agregar los siguientes atributos de la clase Projectile. En los comentarios observa la importancia de cada atributo.

// Imagen del proyectil
public Texture2D Texture;

// Posicion del proyectil
public Vector2 Position;

// Estado del proyectil
public bool Active;

// Cantidad de daño del proyectil
public int Damage;

// Limite visible del juego
Viewport viewport;

// Ancho del proyectil
public int Width
{
	get { return Texture.Width; }
}

// Alto del proyectil
public int Height
{
	get { return Texture.Height; }
}

// Velocidad de desplazamiento
float projectileMoveSpeed;

Ahora agreguemoe el metodo Initialize para inicializar las propiedades de la clase. Observa que le llegan tres parametros, el viewport que son las dimensiones de la escena del juego, la textura del proyectil que en nuestro caso es un rayo laser azul, y por ultimo la posicion inicial del proyectil.

public void Initialize(Viewport viewport, 
	Texture2D texture, Vector2 position)
{
	Texture = texture;
	Position = position;
	this.viewport = viewport;
	Active = true;
	Damage = 2;
	projectileMoveSpeed = 20f;
}

Como la idea de los proyectiles es que viajen de izquierda a derecha contra las minas enemigas, en el método Update evidentemente debemos incrementar la posición con respecto al eje X de acuerdo a la velocidad establecida en el método Initialize (proyectileMoveSpeed = 20f). Por último verificamos si el proyectil se ha salido de la escena para desactivar su despliegue, con el propósito de evitar desplegar imágenes que no podremos visualizar en la escena.

public void Update()
{
	// Projectiles always move to the right
	Position.X += projectileMoveSpeed;
	// Deactivate the bullet if it goes out of screen
	if (Position.X + Texture.Width / 2 > viewport.Width)
	Active = false;
}

Finalmente para esta clase, agregamos el método Draw para visualizar un proyectil en la pantalla.

public void Draw(SpriteBatch spriteBatch)
{
	spriteBatch.Draw(Texture, Position, null, Color.White, 0f,
	new Vector2(Width / 2, Height / 2), 1f, SpriteEffects.None, 0f);
}

Ya con nuestra nueva clase Projectile lista, proseguimos a hacer unas modificaciones en la clase principal.

En la Clase Game1

Comenzamos declarando los siguientes atributos.

// Textura de un proyectil
Texture2D projectileTexture;
// Lista de proyectiles
List <Projectile>projectiles;
// Tasa de proyectiles a disparar
TimeSpan fireTime;
// Tiempo de disparo anterior
TimeSpan previousFireTime;

Nota que agregamos una lista de proyectiles. Hacemos esto porque planeamos desplegar múltiples proyectiles disparados por la nave, por ende esta estructura de dato dinámica es ideal para llevar control de cada proyectil en escena en todo momento. fireTime nos dice en cada cuanto intervalo de tiempo el jugador disparará un proyectil en escena, ya verás cómo eso funciona más adelante. En el método Initialize, al final, agrega las siguientes instrucciones:

projectiles = new List<Proyectile>();
// Un laser se dispara cada cuarto de segundo
fireTime = TimeSpan.FromSeconds(.15f);

Observas el valor de fireTime? Esto quiere decir que la nave disparará 4 láseres por segundo. Ahora, En el LoadContent, al final del método, proseguimos a cargar la textura del láser, con la siguiente instrucción.

projectileTexture = Content.Load<Texture2D>("laser");

Como cuando trabajamos con el despliegue de enemigos, debemos agregar un método para AddProjectile, para instanciar y agregar nuevos proyectiles en la lista, inicializándolos con el viewport, su textura y su posición inicial.

private void AddProjectile(Vector2 position)
{
	Projectile projectile = new Projectile(); 
	projectile.Initialize(GraphicsDevice.Viewport,
	projectileTexture,position); 
	projectiles.Add(projectile);
}

Ahora debemos buscar donde invocar este método. Ya que el jugador es el único elemento que estará disparando rayos láser, tiene sentido ir al método UpdatePlayer, y agregar las siguientes instrucciones al final:

// Disparar bajo el valor de fireTime
if (gameTime.TotalGameTime - previousFireTime > fireTime)
{
	// Reiniciamos nuestro tiempo actual
	previousFireTime = gameTime.TotalGameTime;
	// Agregamos el proyectil al frente de la nave
	AddProjectile(player.Position + new Vector2(player.Width / 2, 0));
}

Aquí estamos agregando nuevos proyectiles en la cola, y disparando a la tasa del valor de fireTime. Ahora necesitamos actualizar las posiciones de cada proyectil en la escena. Como ya hicimos antes con los enemigos, debemos agregar un nuevo método llamado UpdateProjectiles y se verá de la siguiente manera:

private void UpdateProjectiles()
{
	// Actualizar los Proyectiles
	for (int i = projectiles.Count - 1; i >= 0; i--) 
	{
		projectiles[i].Update();
		if (projectiles[i].Active == false)
		{
			projectiles.RemoveAt(i);
		} 
	}
}

Observa que se invoca el método Update por cada proyectil en la escena, donde estos se desplazarán de izquierda a derecha a una velocidad uniforme, y cuando Active se encuentre en false, son removidos de la lista de proyectiles. En cuanto al método Update, proseguimos a invocar UpdateProjectiles al final:

// Actualizar proyectiles
UpdateProjectiles();

Hasta este punto los proyectiles se dibujan, se mueven, pero no colisionan con los enemigos. Dirígete al método UpdateCollision y agrega las siguientes intrucciones al final:

// Colision entre Proyectiles vs Enemigos
for (int i = 0; i < projectiles.Count; i++)
{
	for (int j = 0; j < enemies.Count; j++)
	{
		// Creamos los rectangulos para validar si hay colision
		rectangle1 = new Rectangle(
			(int)projectiles[i].Position.X - 
			projectiles[i].Width / 2,
			(int)projectiles[i].Position.Y - 
			projectiles[i].Height / 2,
			projectiles[i].Width, 
			projectiles[i].Height);

		rectangle2 = new Rectangle(
			(int)enemies[j].Position.X - 
			enemies[j].Width / 2,
			(int)enemies[j].Position.Y - 
			enemies[j].Height / 2,
			enemies[j].Width, 
			enemies[j].Height);

		// Determinamos si se intersectan
		if (rectangle1.Intersects(rectangle2))
		{
			enemies[j].Health -= projectiles[i].Damage;
			projectiles[i].Active = false;
		}
	}
}

Entiendes la lógica? Simplemente se está creando un rectángulo por cada proyectil, y se crea un rectángulo por cada enemigo visible en esa iteración (para capturar su posición en ese instante de tiempo). Luego se verifica si algún rectángulo enemigo se intersecta con el rectángulo del proyectil, y en caso afirmativo, se le resta vida al enemigo colisionado, y se desactiva el proyectil para que sea removido de la lista.

Finalmente, implementamos la logica para desplegar los proyectiles en escena. Vamos al metodo Draw, y debajo de Jugador.Draw(), inserta las siguientes instrucciones:

for (int i = 0; i < projectiles.Count; i++)
{
	projectiles[i].Draw(spriteBatch);
}

Al ejecutar nuestro juego se vera algo así:

Observa la colisión de los proyectiles contra los enemigos, pero veras que solo desaparecen cuando son destruidos. En la siguiente parte de este juego ejemplo haremos que las minas muestren un efecto de explosión cuando son destruidas por el jugador. Si estas teniendo problemas armando la solución, puedes descargarte el proyecto haciendo clic en el siguiente enlace:

Proyecto Shooter Parte 8

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

Ejemplo XNA parte 4 – Animación del elemento Jugador

Hasta ahora tenemos una aeronave que se puede mover a lo largo de la pantalla, pero sin ninguna animación, no se ve interesante ni llamativo para el usuario. Eso lo vamos arreglar ahora. Utilizaremos la clase “Animacion” que importamos desde la parte 1 de este tutorial. En caso que no hiciste el tutorial de animación en este blog, es recomendable que lo leas para que entiendas el mecanismo utilizado para desplegar objetos animados en la pantalla. Accede dicho tutorial dando click en el siguiente enlace: Despliegue de sprites animados 2D.

En la clase jugador

En esta clase Jugador, lo primero que haremos es reemplazar la variable

public Textured2D PlayerTexture

Por esta variable:

public Animation PlayerAnimation;

Esto es necesario ya que no trabajaremos desplegando una Textura estatica de la nave. Trabajaremos con la clase animacion para trabajar con el mecanismo de animacion de sprites. Debido a este cambio de atributo, debemos modificar unas cuantas instrucciones en otros metodos. Ve al metodo Initialize, y reemplazalo por el siguiente:

public void Initialize(Animation animation, Vector2 position)
{
	PlayerAnimation = animation;
	Position = position;
	// Set the player to be active
	Active = true;
	// Set the player health
	Health = 100;
}

Aquí lo que hicimos fue asignar el atributo Animacion que nos fue pasado por parámetro, al igual que la posición. Hacemos true al atributo active para que sea desplegado por defecto, y le inicializamos la vida de la nave a 100. También debemos modificar los atributos Width y Height en la clase jugador. Reemplázalas con lo siguiente:

// Get the width of the player ship
public int Width
{
	get { return PlayerAnimation.FrameWidth; }
}
// Get the height of the player ship
public int Height
{
	get { return PlayerAnimation.FrameHeight; }
}

Ya que no nos interesa saber el alto ni ancho de la imagen Texture2d que teníamos antes, sino el ancho y alto de una imagen en nuestra clase Animacion. Con respecto a Updatedebemos actualizar la posición e invocar el método Update de PlayerAnimation para desplazar el sprite a lo largo de la pantalla con el campo Position, e invocamos Update para el efecto de animación. El método Update queda de la siguiente manera:

public void Update(GameTime gameTime)
{
	PlayerAnimation.Position = Position;
	PlayerAnimation.Update(gameTime);
}

Finalmente, reemplazamos el método Draw que nos desplegaba la Textura2D estática, llamando al método Draw de PlayerAnimation para que despliegue el sprite animado. El método Draw quedaría de la siguiente manera:

public void Draw(SpriteBatch spriteBatch)
{
	PlayerAnimation.Draw(spriteBatch);
}

En la clase Game1

Por último nos queda modificar la clase principal, que involucra la carga de las imágenes de animación, y mandarlas al objeto Jugador por el método Initialize. Dirígete a LoadContent, y reemplaza el método completo por este:

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

	Animation playerAnimation = new Animation();
	Texture2D playerTexture = Content.Load<Texture2D>("Imagenes/animacionNave");
	playerAnimation.Initialize(playerTexture, 
		Vector2.Zero, 115, 69, 8, 30, Color.White, 1f, true);

	Vector2 playerPosition = new Vector2(
		GraphicsDevice.Viewport.TitleSafeArea.X +
		GraphicsDevice.Viewport.TitleSafeArea.Width / 2,
		GraphicsDevice.Viewport.TitleSafeArea.Y +
		GraphicsDevice.Viewport.TitleSafeArea.Height / 2);
	jugador.Initialize(playerAnimation, playerPosition);
}

Nada de otro mundo, simplemente instanciamos un objeto local de la clase Animacion, cargamos la tira de imágenes de la nave en playerTexture, e invocamos el método Initialize de playerAnimation, enviándole por parámetros:

  • playerTexture, el cual es la tira completa de imágenes de la nave
  • Vector2.Zero para la inicializar la posición
  • 115, que es el ancho de una sola imagen expresados en pixeles
  • 69, es el alto de una sola imagen expresados en pixeles
  • 8, representa el numero de imágenes que componen la animación
  • 30, tiempo de despliegue por imagen expresados en milisegundos
  • Color.White, para desplegar la imagen en sus colores reales
  • 1f, para el valor de escala del elemento
  • true, para especificar si queremos que se repita la animación múltiples veces

Se instancia un Vector2 para la posición, para desplegar la nave en el centro de la pantalla, y finalmente se invoca el Initialize de jugador, enviandole playerAnimation playerPosition. Ahora busca el método UpdatePlayer, y al comienzo del método, agrega la siguiente línea:

jugador.Update(gameTime);

Esto invoca el Update de jugador, que este a su vez hace Update a su atributo playerAnimation para refrescar las imágenes de despliegue para dar el efecto de animación de nuestra nave. Ahora no queda más sino armar la solución y ejecutar la aplicación. Ahora veras la nave animada en el centro de la pantalla, con un efecto de movimiento en las alas y el propulsor en la parte trasera. También observa que lo puedes controlar con las teclas de dirección del teclado.

Hasta este punto de nuestro juego de ejemplo, hemos logrado desplegar una nave animada, e interactiva con el usuario. De la misma manera como animamos el sprite del jugador, se harán con los demás elementos animados, como los enemigos. Puede descargar nuestro proyecto de ejemplo hasta este punto dando clic en el siguiente enlace:

Proyecto Shooter Parte 4

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

Ejemplo XNA parte 2 – Creación de la Clase Jugador

Haz un clic derecho sobre el proyecto y agrega una nueva clase llamada «Jugador», y agrega los métodos Initialize, Update y Draw. Deberás importar las librerías de XNA.

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

namespace Shooter
{
	class Jugador
	{
		public void Initialize()
		{
		}

		public void Update()
		{
		}

		public void Draw()
		{
		}
	}
}

Esta es la estructura básica de nuestra clase Jugador, que funcionará para desplegar el elemento que controlará el usuario, que en nuestro caso es la aeronave. Ahora agrega los siguientes atributos (observa la importancia de cada uno que se encuentran comentados)

// Animacion representando al jugador
public Texture2D PlayerTexture;

// Posicion del jugador
public Vector2 Position;

// Estado del jugador
public bool Active;

// Cantidad de vida del jugador
public int Health;

// Obtener el ancho del jugador
public int Width
{
	get { return PlayerTexture.Width; }
}

// Obtener el alto del jugador
public int Height
{
	get { return PlayerTexture.Height; }
}

Estos son algunos atributos fundamentales para nuestra clase jugador, pero obviamente se pueden agregar mas dependiendo del control que queramos tener sobre este elemento. Ahora reemplaza el método Initialize (de la clase Jugador) con lo siguiente:

public void Initialize(Texture2D texture, Vector2 position)
{
	PlayerTexture = texture; 
	// Asignar la posicion del jugador
	Position = position;
	// Activar el jugador
	Active = true;
	// Inicializar la vida del jugador
	Health = 100;
}

Como se puede ver claramente, lo que hace el metodo Initialize es establecer las propiedades iniciales de nuestra clase Jugador. Ahora reemplaza el metodo Draw con el siguiente:

public void Draw(SpriteBatch spriteBatch)
{ 
	spriteBatch.Draw(PlayerTexture, Position, null, 
		Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0f);
}

Ya con la clase Jugador con sus atributos y funciones listas, pasaremos a la clase principal para instanciar un objeto Jugador.

En la clase Game1.cs

Ya que tenemos todo preparada la clase Jugador, debemos instanciarla, inicializarla y por último, invocar su método Draw desde esta clase principal para desplegar la nave en la pantalla. Ve al inicio de esta clase, y debajo de la declaración del atributo SpriteBatch spriteBatch; agrega nuestra variable tipo Jugador.

Jugador jugador;

Y ahora en el método Initialize, instanciamos nuestra clase:

jugador = new Jugador();

Siguiente, debemos cargar la textura de nuestra nave que se encuentra en el Content de nuestro proyecto. En el método LoadContent, agrega las siguientes líneas:

// Load the player resources 
Vector2 playerPosition = new Vector2(
	GraphicsDevice.Viewport.TitleSafeArea.X,
	GraphicsDevice.Viewport.TitleSafeArea.Y + 
	GraphicsDevice.Viewport.TitleSafeArea.Height / 2);
jugador.Initialize(Content.Load<Texture2D>("Imagenes/nave"), playerPosition);

Ya que tenemos nuestro objeto Jugador instacianciado, y cargado, ve al metodo Draw y agrega las siguientes instrucciones:

spriteBatch.Begin();
jugador.Draw(spriteBatch);
spriteBatch.End();

Ahora finalmente compila el proyecto, y ejecútalo. Si todo funciona correctamente, verás algo como esto:

Si tu proyecto presenta errores y no encuentra como solucionarlos, no entres en pánico, puedes descargar el proyecto de acá en su estado final de este tutorial, simplemente da clic al siguiente enlace:

Proyecto Shooter Parte 2

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