Freigeben über


XNA from Silverlight on Windows Phone 7 - Gestures

Another example of where you might want to access XNA APIs from a WP7 Silverlight application; touch gesture support. Although Silverlight offers manipulation support, it has no direct support for the higher level concept of “gestures” (eg tap, flick). You could certainly build that yourself in Silverlight OR you may be able to use the gesture support from the XNA Framework. I say “may” as I haven’t tested this exhaustively but my initial efforts suggest it seems to work just fine.

To access the XNA gesture support you’ll need to add a reference to the Microsoft.Xna.Framework.Input.Touch XNA assembly. I’ve also added a reference to Microsoft.Xna.Framework as that contains the definition for the Vector2 struct that I use to give some feedback of the gesture position / scope for the purposes of the demo. You may not need it in your app.

In the Microsoft.Xna.Framework.Input.Touch namespace is a class called TouchPanel that is the entry point for gesture support in XNA. It allows us to enable gestures, specify what types of gestures we want to register an interest in, check if a gesture is “available”, read available gestures etc.

An XNA application operates in a “game loop”. In other words, rather than being event-driven (the user has clicked on a button), it’s time based and polls for input at specific intervals (typically 30 times / second on a WP7 device). As a result there is no way for the TouchPanel to notify us that a gesture has occurred. We can’t subscribe to a GestureReady event or anything of that nature. However, Silverlight does expose touch related events (manipulations) so, by hooking into these (in my case I just used ManipulationCompleted) I know that some touch event has occurred and I can query the TouchPanel to see if a gesture is available.

Here’s the app UI (not much to it in this case):

image

I’ve wired up the ManipulationCompleted event to the LayoutRoot element (Grid).

 <Grid x:Name="LayoutRoot" 
        Background="Transparent" 
        ManipulationCompleted="ManipCompleted">

 

And here’s the code:

 using System.Threading;
using System.Windows.Input;
using Microsoft.Phone.Controls;
using Microsoft.Xna.Framework.Input.Touch;

namespace Gestures
{
    public partial class MainPage : PhoneApplicationPage
    {
        public MainPage()
        {
            InitializeComponent();

            // Enable gestures and subscribe to flick and tap
            TouchPanel.EnabledGestures =
                GestureType.Flick | GestureType.Tap;
        }

        // Silverlight can notify us that a touch manipulation has completed
        // Seems like a good time to check if we have a gesture
        private void ManipCompleted(
            object sender, ManipulationCompletedEventArgs e)
        {
            // The IsGestureAvailable property will tell us if a gesture
            // has been recognised. It's possible there's more than one
            while (TouchPanel.IsGestureAvailable)
            {
                // Read the current gesture
                GestureSample gesture = TouchPanel.ReadGesture();

                // Temporarily update page title to indicate type of gesture
                // and its properties (for the purpose of the demo).
                if (gesture.GestureType == GestureType.Flick)
                {
                    PageTitle.Text = string.Format(
                        "flick {0}, {1}", gesture.Delta.X, gesture.Delta.Y);
                    RestoreTitleDelayed(1500);
                }
                else
                {
                    PageTitle.Text = string.Format(
                        "tap {0}, {1}", gesture.Position.X, gesture.Position.Y);
                    RestoreTitleDelayed(1500);
                }
            }
        }

        // Queue a work item on a separate thread (so we don't lock the UI)
        // to restore the page title after a specified delay
        private void RestoreTitleDelayed(int delay)
        {
            ThreadPool.QueueUserWorkItem((object o) =>
                {
                    Thread.Sleep(delay);
                    Dispatcher.BeginInvoke(() =>
                    {
                        PageTitle.Text = "waiting";
                    });
                });
        }
    }
}

Now when I either tap or flick on the screen, the gesture is recognised and reported (briefly) in the page title. Here’s a quick video of the demo app in action: