With the advent of the fifth console generation comes the Nintendo Wii, loved for its low retail price, endearing exclusive franchises, and most importantly, its unique input device: a controller with Infrared pointing capabilities, an accessory slot for attachments, and accelerometers that measure rotation along 3 axes. This controller has opened up a whole new world of control beyond what the fourth console generation offered us, and now independent developers everywhere are scrambling to get a bite of this technology for their own projects.
While the rest of the world waits to see if Nintendo actually makes good on their uncharacteristic promises of openness to 3rd party developers with “small budgets and big ideas,” the impatient independent community has already started tearing the Wii console and remote apart trying to figure it all out, and their efforts have yielded plenty of libraries that allow us to use the Wii Remote as an input device in our PC software.
This tutorial will explain how to use one such library, WiimoteLib, to interpret the signals sent from the Wii remote via Bluetooth.
I learned everything I know about this subject from personal experience and this tutorial by Brian Peek. If you don’t think I’m cool enough for your tutorial needs, I welcome you to go check out his tutorial instead. ;D
This is quite possibly the only difficult part of the whole tutorial, simply because it involves taking a lot of independent hardware made by different manufacturers and trying to make them all talk to each other. The unfortunate truth is that depending on your computer and what kind of Bluetooth adapter you’re using, this process is probably going to be slightly different for you. Regardless, however, most of the basic steps are the same no matter what tools you’re using, so try to follow along as best as you can, and if it just isn’t working, try referring to your Bluetooth adapter’s instruction manual.
If you’ve already got a Bluetooth adapter installed on your computer (or if you’re one of the lucky ones who has Bluetooth communications built right in), then this step should be as easy as plugging in the USB dongle and letting it start up. If this is your first time, however, follow the instructions included with your adapter and make sure you install it correctly before proceeding.
This step varies depending on your bluetooth adapter, but the process should look something like this.
Pressing the 1 and 2 buttons will make the Wiimote enter the Bluetooth Discoverable mode, which is indicated by the flashing LED lights at the bottom of the Wiimote. Make sure you keep the 1 and 2 buttons held down while establishing the connection. If you let go, the Wiimote will go back to sleep faster and you might not be able to finish establishing the connection.
The Wiimote should be listed as Nintendo RVL-CNT-01. Depending on how your software works, select the Wiimote and establish a connection with it. If you are asked for a PIN number at any time, leave the field blank and/or skip that step. If you are asked which service you want the Wiimote to use, select something to the tune of “Input / Keyboard & Mouse / Human Interface Device (HID)”.
Once the connection has been established, the Wiimote might keep flashing its LED lights, which can be confusing. To test whether or not the connection has been successfully made, try running this application. If the connection has been made, the program will display the Wiimote’s current button and joystick states, accelerometer readings, IR info, and attachment info for the Nunchuk or Classic Controller.
Once you’ve downloaded WiimoteLib, just grab WiimoteLib.DLL and reference it in your project. To get started, all you need is some initialization and destruction code:
Imports WiimoteLib Private myWiimote as New Wiimote Private Sub Initialize() myWiimote.Connect() myWiimote.SetReportType(Wiimote.InputReport.IRExtensionAccel, False) '<---Use this code if you have an accessory attached myWiimote.SetReportType(Wiimote.InputReport.IRAccel, False) '<---Use this code if you don't 'In the next section, we'll see how to automatically 'choose/change the report type when an attachment is 'plugged in or unplugged. End Sub Private Sub Kill() myWiimote.SetRumble(False) myWiimote.SetLEDs(False, False, False, False) myWiimote.Disconnect() End Sub
And now, the magical world of the Wiimote is at your fingertips! You can access the Wiimote’s different states using myWiimote.WiimoteState, like so:
Dim wiimoteRotation as TV_3DVECTOR wiimoteRotation.x = myWiimote.WiimoteState.AccelState.X wiimoteRotation.y = myWiimote.WiimoteState.AccelState.Y wiimoteRotation.z = myWiimote.WiimoteState.AccelState.Z Dim buttonPressed_A as Boolean Dim buttonPressed_1 as Boolean Dim buttonPressed_2 as Boolean buttonPressed_A = myWiimote.WiimoteState.ButtonState.A buttonPressed_1 = myWiimote.WiimoteState.ButtonState.One buttonPressed_2 = myWiimote.WiimoteState.ButtonState.Two Dim batteryStrengthPercentage as String batteryStrengthPercentage = "Wiimote Battery Level: Roughly " & Int(myWiimote.WiimoteState.Battery / 2) & "%" Dim nunchukJoystickPosition as TV_2DVECTOR nunchukJoystickPosition.x = myWiimote.WiimoteState.NunchukState.X nunchukJoystickPosition.y = myWiimote.WiimoteState.NunchukState.Y
In order to handle the addition/removal of accessories, you can create event handlers that run whenever an accessory is plugged in or unplugged.
Imports WiimoteLib Private myWiimote as New Wiimote Private Sub Initialize() AddHandler myWiimote.OnWiimoteExtensionChanged, AddressOf myWiimote_ExtensionChanged myWiimote.Connect() myWiimote.SetReportType(Wiimote.InputReport.IRAccel, False) End Sub Private Sub myWiimote_ExtensionChanged(ByVal sender As Object, ByVal args As WiimoteExtensionChangedEventArgs) If args.Inserted Then myWiimote.SetReportType(Wiimote.InputReport.IRExtensionAccel, True) Else myWiimote.SetReportType(Wiimote.InputReport.IRAccel, True) End If End Sub Private Sub Kill() myWiimote.SetRumble(False) myWiimote.SetLEDs(False, False, False, False) myWiimote.Disconnect() End Sub
One problem I quickly noticed is that there is a small ping delay when changing myWiimote.WiimoteState.Rumble which causes choppy gameplay every time the rumble turns on or off. In order to get around this, I created a simple rumble manager that utilizes a BackgroundWorker object.
Imports WiimoteLib Private myWiimote as New Wiimote Private WithEvents wiimoteBackgroundWorker As New System.ComponentModel.BackgroundWorker Private rumbleTime As Single = -1 Private rumbling As Boolean Private Sub Initialize() AddHandler myWiimote.OnWiimoteExtensionChanged, AddressOf myWiimote_ExtensionChanged myWiimote.Connect() myWiimote.SetReportType(Wiimote.InputReport.IRAccel, False) wiimoteBackgroundWorker.RunWorkerAsync() End Sub Private Sub wiimoteBackgroundWorker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles wiimoteBackgroundWorker.DoWork Do If rumbleTime >= 0 Then If Not rumbling Then orTV.WIIMOTE.SetRumble(True) rumbling = True End If rumbleTime -= fpsAdj 'fpsAdj is a global adjustment value I use to control my game's speed regardless of framerate. 'As my framerate goes up, fpsAdj goes down, and vice versa. You could use a flat number 'here if you want, instead of a dynamic value: "rumbleTime -= 1", for instance. ElseIf rumbling Then orTV.WIIMOTE.SetRumble(False) rumbling = False End If Application.DoEvents() Loop End Sub Private Sub myWiimote_ExtensionChanged(ByVal sender As Object, ByVal args As WiimoteExtensionChangedEventArgs) If args.Inserted Then myWiimote.SetReportType(Wiimote.InputReport.IRExtensionAccel, True) Else myWiimote.SetReportType(Wiimote.InputReport.IRAccel, True) End If End Sub Private Sub Kill() myWiimote.SetRumble(False) myWiimote.SetLEDs(False, False, False, False) myWiimote.Disconnect() End Sub
With this code in place, you can set the number of frames you want the Wiimote to rumble like this:
Private Sub RumbleForAWhile() rumbleTime = 100 End Sub Private Sub RumbleForAWhileLonger() rumbleTime += 50 End Sub Private Sub StopRumblingImmediately() rumbleTime = 0 End Sub
The Wiimote has a powerful Infrared feature that detects four infrared light sources, then interprets those two (up to four) IR “dots” to X and Y values which are sent to the Wii. The fact that the Wiimote picks up four separated IR dots allows us to tell when the Wiimote is moving closer and farther away from the screen, in the exact same way that having two eyes allows humans to see with depth perception.
That silver bar that comes with your Wii and is placed in front of your TV screen is nothing more than a couple of infrared light emitters attached to a plastic spacer. They emit small, focused, and bright infrared “dots” for the Wiimote to easily see and trace, but the Wiimote can also pick up on other infrared light, like nearby lamps and other electronic equipment. If you’ve ever been playing a Wii game when your Wiimote suddenly glitched and became seemingly uncontrollable, there’s a good chance that you pointed the Wiimote too far away from the center of the screen, and when the Wiimote lost focus on one end of the sensor bar, it picked up another nearby IR source, such as a lamp next to your TV. Turning off nearby lights or moving them behind you usually does the trick, if you run into this problem frequently. Additionally, sitting a little bit farther away from the sensor bar makes it harder for the Wiimote to lose track of the sensor bar.
The problem with the Wii’s sensor bar is that it isn’t wireless, like the Wiimote. Tethered to the Wii like a chained dog, it’s rather useless to us. So what are we supposed to do? Sure, we don’t need to utilize the Wiimote’s IR capabilities to come up with some pretty awesome games, but let’s face it: every good shooter deserves IR pointing- and so do a lot of other cool games.
I’ve always envisioned a pair of USB plugin IR emitters that you can clip, stick, or mount onto the top or sides of your monitor, but I haven’t been able to find any such thing for sale anywhere. And let’s be honest, who wants to pay money for a unitasking tool when there’s a cheap, simple, and- frankly- somewhat medieval solution sitting right under your nose? That’s right: candles. Candles yield heat, heat yields infrared light, and as long as we keep a small and focused flame going, we can definitely get a couple of IR “dots” strong enough for our Wiimote to pick up on.
Now, once you have your candles set up, you can use the following two lines of code to see exactly what the Wiimote sees: two infrared “dots”.
tvScreen2dImmediate.Draw_Circle(myWiimote.WiimoteState.IRState.X1 * screenWidth, screenHeight - myWiimote.WiimoteState.IRState.Y1 * screenHeight, 8, 12, orTV.GLOBALS.RGBA(1, 0, 0, 1)) tvScreen2dImmediate.Draw_Circle(myWiimote.WiimoteState.IRState.X2 * screenWidth, screenHeight - myWiimote.WiimoteState.IRState.Y2 * screenHeight, 8, 12, orTV.GLOBALS.RGBA(1, 0, 0, 1))
Using the IR coordinates we get from the Wiimote, we can make a simple pointer out of the Wiimote, like so:
tvScreen2dImmediate.Draw_Circle(screenWidth - (myWiimote.WiimoteState.IRState.X1 + myWiimote.WiimoteState.IRState.X2) / 2 * screenWidth, (myWiimote.WiimoteState.IRState.Y1 + myWiimote.WiimoteState.IRState.Y2) / 2 * screenHeight, Math.Abs(myWiimote.WiimoteState.IRState.X2 - myWiimote.WiimoteState.IRState.X1) * 64, 12, orTV.GLOBALS.RGBA(1, 1, 1, 1))
Now, that simple line of code will render a dot where you’re pointing the Wiimote, and as you move the Wiimote closer to the screen, the dot will grow bigger. This simple code has many weaknesses, however. For instance:
With a little creativity, you can easily overcome these problems. I also plan to write a more failproof cursor handler sometime soon, and when I do so, I’ll be sure to upload it. :D