jueves, 16 de diciembre de 2010

[XNA/WP7] Consideraciones sobre el TouchPanel

El uso del TouchPanel es una de las características que siempre se nombran en Windows Phone 7. Y teniendo en cuenta que disponemos únicamente de tres botones físicos en el teléfono, más nos vale usar la pantalla para interactuar con el usuario. Aquí entra el TouchPanel, la pantalla táctil de los terminales del tipo Windows Phone 7.

Para usarlo es sencillo. Simplemente debemos capturar el estado del panel en el momento apropiado. Algo como lo siguiente:

   1: TouchCollection touchCollection = TouchPanel.GetState();
   2: foreach (TouchLocation tl in touchCollection)
   3: {
   4: //Aquí evaluamos cada punto...
   5: }

Capturamos el estado y mediante un bucle, recorremos todos los puntos con los que hemos hecho contacto. A ese punto se le llama un TouchLocation y el estado del TouchPanel no es más que una colección de TouchLocation. Cada TouchLocation tiene un identificador (entero), posición (Point indicando la posición de contacto) y State (para ver si está pulsado o no). Por lo tanto yo podría tener algo así:

   1: TouchCollection touchCollection = TouchPanel.GetState();
   2: foreach (TouchLocation tl in touchCollection)
   3: {
   4:     if (this.intersectsWithPlayer(tl.Position))
   5:     {
   6:         //...
   7:     }
   8: }

La función intersectsWithPlayer(Point p) comprueba si en el punto p hay una intersección con el jugador. La pregunta es, ¿es este planteamiento correcto?

No. No lo es. Y es un fallo simple y común. ¿Dónde está el fallo? El fallo es que no verifico en qué estado está el TouchLocation a analizar. Cada TouchLocation tiene varios estados (enumeración TouchLocationState): Inválido, movido, pulsado, levantado. Debemos comprobar antes de interactuar con los puntos en qué estado se encuentra, porque en ese caso el funcionamiento de la interacción no será el que deseamos. Por ejemplo, partiendo del ejemplo anterior, supongamos que ese código está dentro del Update propio y que el usuario tiene el dedo en la pantalla. En la primera iteración de Update, efectivamente el panel capturaría el estado y reconocería el punto de contacto. A continuación, sigue el ciclo de ejecución y el usuario levanta el dedo de la pantalla. ¿Qué ocurrirá en la iteración posterior de Update a ese evento? Pues volverá a capturar el punto (pero si el usuario no tiene ningún dedo puesto!!) y lo evaluará. ¿Cómo es posible que si no tenemos un punto con la pantalla el sistema diga que tiene uno? Porque evidentemente, lo tiene. Y ese punto es el punto anterior, donde antes estaba pulsado, pero con el estado “Released”. A la siguiente iteración ya no estará el punto capturado, pero previamente lo estaba por la transición de sus estados. Así que por lo tanto para solventar nuestro problemilla la solución es esta:

   1: foreach (TouchLocation tl in touchCollection)
   2: {
   3:     if ((tl.State == TouchLocationState.Pressed)
   4:     {
   5:         if ( this.intersectsWithPlayer(tl.Position))
   6:         {
   7:             //...
   8:         }
   9:     }
  10: }

Lo mismo ocurre con el resto de estados. Es un fallo que ya lo hemos sufrido varios y depurarlo es muy complicado si no tienes en cuenta esas peculiaridades de TouchLocationState.

2 comentarios:

  1. Te falta cerrar un parentesis en el ultimo trozo de codigo. Seria asi:

    foreach (TouchLocation tl in touchCollection)
    {
    if ((tl.State == TouchLocationState.Pressed))
    {
    if ( this.intersectsWithPlayer(tl.Position))
    {
    //...
    }
    }
    }

    UN saludo

    ResponderEliminar