Rick Barraza

Silverlight UX Development

20080111 Friday January 11, 2008

Connecting to the Wii Control with WPF

So, Project Maestro has been getting some nice attention, and people have been asking how it was done. So I’ll take a break from the normal Dirty Dozen posts and put up some code that can hopefully get you started.

If you haven't seen our Project Maestro video, click on the image below to see it in our new Cynergy.Labs screening room


You should also check out Johnny Chung Lee’s great Wii Projects for inspiration. While we’ll focus on using a managed library DLL with WPF and XAML, Johnny Chung Lee has done some amazing things using the Wii and DirectX and I’m a big fan.

Next, You’ll need to get Bryan Peek’s managed Library DLL and instructions. Make sure to read his section ‘Getting Connected’ if you need help having your Bluetooth enabled computer find the Wii control. There is a lot of code there to go over to see how else to use the control, but for this project, just make sure you get the .DLL file and reference it in your project.

So, assuming you’ve downloaded the WiimoteLib DLL and referenced it in your WPF project and have the Wii Remote connected through Bluetooth properly, here’s the basic skeleton code to get you started.

The only XAML you need to get this to work is two visual elements of some sort (I’m using a 2x2 Rectangle object for each) named fingerR and fingerL. These will represent where your fingers are pointing on the screen.

…
using System.Threading;

    public partial class WiiTest : Window
    {
	// the primary object defined in WiimoteLib.dll…
        Wiimote wm;
	// helps in handling some of the messier threading issues…
        Mutex m = new Mutex();
	// One point each to track each finger…
        Point point1 = new Point(0, 0);
        Point point2 = new Point(0, 0);

        public WiiTest()
        {
            InitializeComponent();
	        // First, we instantiate the WiiMote object…
                wm = new Wiimote();
	        // and add an event handler for its message updates…
                wm.WiimoteChanged += new WiimoteChangedEventHandler(wm_OnWiimoteChanged);
	        // We then have the object connect to the device…
                wm.Connect();
                wm.SetReportType(Wiimote.InputReport.IRAccel, true);
	        // and for fun, you can set the LEDs on the device to
                // know your in. (not needed but kinda cool ;)
                wm.SetLEDs(false, true, true, false);
	        // Then, as a WPF animation loop, I hook up to the 
               // Rendering event, which does all the visual updates
            CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
        }

        void wm_OnWiimoteChanged(object sender, WiimoteChangedEventArgs args)
        {

            m.WaitOne();
  	    // We capture the messages from the control
            WiimoteState ws = args.WiimoteState;
	    // and use the Raw values of any IR sources the IR camera detects.
            // A quick calculation converts the Raw value (from a 1024x768 grid) to a 
            // value that maps to our window’s actual width and height. We then
            // store it in the public Point objects we defined earlier.
            point1.X = this.ActualWidth- Convert.ToDouble((ws.IRState.RawX1 * screenWidth)) / 1024;
            point1.Y = this.ActualHeight - Convert.ToDouble((ws.IRState.RawY1 * screenHeight)) / 768;
            point2.X = this.ActualWidth - Convert.ToDouble((ws.IRState.RawX2 * screenWidth)) / 1023;
            point2.Y = this.ActualHeight - Convert.ToDouble((ws.IRState.RawY2 * screenHeight)) / 768;
            m.ReleaseMutex();
        }

        void CompositionTarget_Rendering(object sender, EventArgs e)
        {  
                moveFingers();
        }
        private void moveFingers()
        {
  	    // Now, we could have just set fingerR and fingerL to the values
	    // stored in point1 and point2 directly, but it’s a little more
            // elegant to add some easing equations to make the animation
            // smoother. Call me old fashioned.
            double frX = Convert.ToDouble(fingerR.GetValue(Canvas.LeftProperty));
            double frY = Convert.ToDouble(fingerR.GetValue(Canvas.TopProperty));
            double left = frX + (point1.X - frX) * .4;
            double top = frY + (point1.Y - frY) * .4;
            fingerR.SetValue(Canvas.LeftProperty, left);
            fingerR.SetValue(Canvas.TopProperty, top);

            double flX = Convert.ToDouble(fingerL.GetValue(Canvas.LeftProperty));
            double flY = Convert.ToDouble(fingerL.GetValue(Canvas.TopProperty));
            double left2 = flX + (point2.X - flX) * .4;
            double top2 = flY + (point2.Y - flY) * .4;
            fingerL.SetValue(Canvas.LeftProperty, left2);
            fingerL.SetValue(Canvas.TopProperty, top2);
        }


That’s it.

If you connected the Wii properly and are referencing Brian Peek’s DLL, you should be able to move the two finger visuals around the screen by moving two different IR emitters/reflectors in front of the Wii Control, all in WPF!

Note regarding Emitters:You can test the code with anything that emits a steady beam of Infrared. Television remote controls have a tendency to pulse their IR signal, so their beam isn’t constant and you’ll see the flicker on the screen. Any Radio Shack or similar electronics store will usually carry 1.5V IR LEDs for a couple bucks that you can use with a AA battery for testing if you want to take this further. Have fun!

Posted by rickbarraza | Jan 11 2008, 05:59:08 PM PST
XML