Utilizar el SDK de Kinect con XNA

El sensor de Kinect fue desarrollado especialmente para mejorar la experiencia de usuario en los juegos de la consola Xbox360. Con el lanzamiento del SDK de Kinect para Windows y la posibilidad de aprovechar las características de este dispositivo desde nuestro entorno Windows podemos aplicarla al desarrollo de nuestros propios videojuegos programándolos en XNA.

Vamos a ver cómo integrar el SDK de Kinect en un videojuego en XNA para poder utilizar nuestro propio cuerpo como controlador.

Descargas necesarias:

SDK de Kinect para Windows. SDK necesario para utilizar las capacidades del sensor Kinect en nuestras aplicaciones. Descárgalo aquí.

Instalar las herramientas de Windows Phone que incluye XNA.
Descargar un juego XNA para ir incorporando capacidades del sensor de Kinect.

 

Primeros pasos

Lo primero es descargarse e instalarse la SDK de Kinect para Windows. Después conectaremos el sensor de Kinect al USB y comprobaremos que se han instalado los drivers correctamente y está el LED verde del sensor parpadeando.

Más información sobre cómo empezar con Kinect en el Reto SDK de Kinect: Desarrolla con Kinect.

Para utilizar el sensor en un juego nos descargaremos el juego del tutorial de XNA. Así nos centraremos en cómo programar con el sensor y no en cómo programar con XNA.

Una vez descargado abriremos el proyecto y añadiremos una referencia a la librería del sensor Kinect( Microsoft.Research.Kinect).

Capturar datos desde el sensor

Dentro de la solución XNA que ya tenemos abierta nos crearemos un nuevo proyecto WPF. Agregaremos una referencia a la librería de Kinect ( Microsoft.Research.Kinect) para poder utilizarla y otra al proyecto de XNA (Shooter).

Este proyecto WPF será el encargado de crear e inicializar la clase necesaria para utilizar el sensor y también ejecutará el videojuego. Esta aplicación capturará los datos y se los pasará al proyecto de XNA para que se realicen las acciones oportunas.

Primero agregamos los using necesarios.

using Shooter;

using Microsoft.Research.Kinect.Nui;

Tendremos que crearnos una instancia del juego para lanzarlo y poder pasarles los datos, así que nos creamos una propiedad Game1.

Game1 game;

Crearemos una función Load y dentro de esa función crearemos nuestra instancia del sensor y añadiremos los manejadores para capturar los datos y enviárselos al videojuego.

private void Window_Loaded(object sender, RoutedEventArgs e)

{

Runtime kinectRuntime = new Runtime();

kinectRuntime.Initialize(RuntimeOptions.UseDepthAndPlayerIndex|RuntimeOptions.UseSkeletalTracking);

kinectRuntime.DepthStream.Open(ImageStreamType.Depth, 2, ImageResolution.Resolution320x240, ImageType.DepthAndPlayerIndex);

kinectRuntime.DepthFrameReady += kinectRuntime_DepthFrameReady;

kinectRuntime.SkeletonFrameReady += kinectRuntime_SkeletonFrameReady;

Inicializamos el sensor con las opciones de UseDepthAndPlayerIndex y de UseSkeletalTracking para poder utilizar la cámara de profundidad y los datos del esqueleto. Después conectamos la cámara con Open y creamos los manejadores para capturar los Frames que devuelve la cámara.

Dentro de la función Loaded también lanzaremos el juego.

using (game = new Game1())

{

  game.Exiting += game_Exiting;

  game.Run();

}

kinectRuntime.Uninitialize();

}

Lo siguiente es crearnos las funciones de los manejadores que obtendrán los datos y se pasarán al juego. También el evento Exit del juego para que cuando salgamos del juego se acabe la aplicación.

void kinectRuntime_DepthFrameReady(object sender, ImageFrameReadyEventArgs e)

{

   game.OnDepthFrameReady(e);

}

void kinectRuntime_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)

{

   game.OnSkeletonFrameReady(e);

}

void game_Exiting(object sender, System.EventArgs e)

{

   Close();

}

Con esto ya hemos acabado dentro de nuestro WPF. Hemos inicializado el sensor y le hemos pasado los datos al juego. Ahora codificaremos las líneas necesarias dentro del proyecto de XNA para realizar acciones con los datos recibidos del sensor.

Viéndote dentro del juego

Abriremos la clase Game1 del proyecto de XNA (Shooter) y añadiremos 2 propiedades. Una textura que utilizaremos para mostrar la imagen de la cámara de profundidad y un array que contendrá los datos de esa imagen.

Texture2D depthTexture;

readonly byte[] depthFrame32 = new byte[320 * 240 * 4];

El array lo utilizamos porque no vamos a mostrar directamente la imagen que nos da la cámara sino que vamos a utilizar una función para pasar esos datos a una intensidad de 8-bit y de paso poner a cada jugador de una intensidad distinta para poder diferenciarlos (este juego es de un sólo jugador).

void ConvertDepthFrame(byte[] depthFrame16)

{

    for (int i16 = 0, i32 = 0; i16 < depthFrame16.Length && i32 < depthFrame32.Length; i16 += 2, i32 += 4)

    {

        int player = depthFrame16[i16] & 0x07;

        int realDepth = (depthFrame16[i16 + 1] << 5) | (depthFrame16[i16] >> 3);

        // transform 13-bit depth information into an 8-bit intensity appropriate

        // for display (we disregard information in most significant bit)

        byte intensity = (byte)(255 - (255 * realDepth / 0x0fff));

 

        depthFrame32[i32] = 0;

        depthFrame32[i32 + 1] = 0;

        depthFrame32[i32 + 2] = 0;

 

       switch (player)

        {

            case 0: // no one

                depthFrame32[i32] = (byte)(intensity / 2);

                depthFrame32[i32 + 1] = (byte)(intensity / 2);

                depthFrame32[i32 + 2] = (byte)(intensity / 2);

              break;

            default:

                depthFrame32[i32] = intensity;

                break;

        }

    }

}

En el método OnDepthFrameReady crearemos la textura con las dimensiones de la imagen y después por medio de la función OnDepthFrameReady actualizaremos el array con los nuevos datos, para más tarde asignarlos a nuestra textura recién creada.

public void OnDepthFrameReady(ImageFrameReadyEventArgs e)

{