Virtual Joystick
In this chapter, we take a look at the basics of building a utility for simulating a joystick. The basic concepts behind the joystick are to eliminate the need for a real joystick to be connected to the Tablet PC to play games. In theory, this sounds great, but realistically, the usefulness of this application is probably very limited depending on the game. With that said, you can use this same technique for controlling your own game projects (we'll do something similar in Chapter 26, Pong Game), and with some tweaking, this could be an interesting way to input information into any application without typing.
Note |
The source code for the projects are located on the CD-ROM in the PROJECTS folder. You can either type them in as you go or you can copy the projects from the CD-ROM to your hard drive for editing. |
Key Concepts
As you already know, the general idea behind this application is to simulate a joystick. Most games that support joysticks also offer the user the ability to use a keyboard. Obviously, there might be times that a Tablet PC user may not have his keyboard with him, but might want to play a game. The obvious approach to playing the game is to use the Input Panel, but this obstructs so much of the screen's real estate that it might be difficult to see the game you are playing.
Our approach is to develop an application based around the SendKeys method. By using SendKeys, we have the ability to send keystrokes to other applications. We can assign keys for the various directions and buttons that a joystick would normally use (see Figure 25.1). The keys correspond to the keys the game is set up to use. We'll store the keys in the Registry and retrieve them as needed.
Figure 25.1: The joystick will look like this.
Getting Started
Begin by creating a new VB Window Forms application. As you can see in Figure 25.1, the GUI is quite simple. We use a background image on a form to simulate the appearance of a joystick. The image is available on the CD-ROM that is included with the book in the Chapter 25 project folder. After assigning the background to the image, resize the form to approximate its dimensions. The next step is to place label controls to represent the keys.
The following Label controls need to be created with the following names and placed in the correct locations:
- lblUp
- lbl45
- lblRight
- lbl135
- lblDown
- lbl225
- lblLeft
- lbl315
The previous labels handle the directional elements for the joystick. There are a few remaining labels that we need to add. First, we need to add labels called lblReturnPos and lblDelay, which help to control how long the application waits between virtual keystrokes. The last two steps are to add a Timer control, a StatusBar control, and an InkEdit control to the form. The InkEdit control will be used to change the values of the directions as the user will not have access to his keyboard. You can leave their names as the default. The form should now look like Figure 25.2 (so that you can see them better, the labels have had values assigned to them).
Figure 25.2: The final GUI.
Writing Code
We begin with importing the appropriate namespaces. Add the following code to the Code Editor:
Imports Microsoft.Win32 Imports Microsoft.Ink Imports System.Windows.Forms.Screen
We are now going to create the variables for our application, which include values for the directions, the screen dimensions for both x and y directions, a Boolean value for joystick enabled, and the Registry key value.
Here is the code:
Dim DUp, DDown, DLeft, DRight, D45, D135, D225, D315, DReturnPos, DDelay As Object Dim X As Integer = PrimaryScreen.Bounds.Width / 2 Dim Y As Integer = PrimaryScreen.Bounds.Height / 2 Dim JEnabled As Boolean Dim pRegKey As RegistryKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\VirtualJoystick")
The Registry
The Windows Registry is a central database for application configuration settings and other information required by the applications. In fact, this is its only purpose. We are going to manually add some values to the Registry so that we can see how this information is stored. For this application, you could enter values into the Registry manually, or you can simply run the Chapter25.Reg file included on the CD-ROM in the Chapter 25 folder. This adds the values to the Registry automatically for you.
Note |
Making changes to the Registry can cause serious problems with your system. Refer to an appropriate text before making changes that you don't already understand. With this in mind, it is recommended that you enter the Registry values with the Chapter25.reg file. |
After the values are entered, you can run the Registry Editor by choosing Run from the Start menu. Use the filename regedit, and then click OK to open it (see Figure 25.3). Depending on your previous use of the editor, your Registry may look very different from the one displayed in Figure 25.3. You can see from the figure that the Registry is hierarchical data storage for the settings and has five main keys listed under My Computer. You can look up the 'VirtualJoystick' application if you want. Either way, now that you have some idea of the values stored, you can close the Registry Editor.
Figure 25.3: The Windows Registry Editor.
The .NET Framework provides two classes (Registry and RegistryKey) to work with the Registry. These classes are defined in the Microsoft.Win32 namespace, which is why we added a reference to it earlier.
The Registry Class
The Registry class is the first of the two classes we look at and contains members that provide access to Registry keys. We can define Registry keys in the following order:
CurrentUser: Stores information about user preferences
LocalMachine: Stores configuration information for the local machine
ClassesRoot: Stores information about types (and classes) and their properties
Users: Stores information about the default user configuration
PerformanceData: Stores performance information for software components
CurrentConfig: Stores non-user-specific hardware information
DynData: Stores dynamic data
As you noticed with the code we added, our data will be stored in LocalMachine under 'VirtualJoystick.' The Registry class has a field corresponding to each of these key types. The Registry class members are described in the following list:
ClassesRoot: Provides access to HKEY_CLASSES_ROOT key
CurrentConfig: Provides access to HKEY_CURRENT_CONFIG key
CurrentUser: Provides access to HKEY_CURRENT_USER key
DynData: Provides access to HKEY_DYN_DATA key
LocalMachine: Provides access to HKEY_LOCAL_MACHINE key
PerformanceData: Provides access to HKEY_PERFORMANCE_DATA key
Users: Provides access to HKEY_USERS key
As you can see from our code, if you want to access the HKEY_LOCAL_MACHINE key, you need to call the Registry.LocalMachine member, which returns a RegistryKey type.
The RegistryKey Class
The RegistryKey class contains members that allow us to add, remove, replace, and read Registry data. Some of its common properties are detailed in the following list:
Name: Represents the name of the key
SubKeyCount: Represents the count of subkeys at the base level, for current key
ValueCount: Represents the count of values in the key
Some of the common methods of the RegistryKey class are detailed in the following list:
Close: Closes the key
CreateSubKey: Creates a new subkey if doesn't exist; otherwise, opens an existing subkey
DeleteSubKey: Deletes the specified subkey
DeleteSubKeyTree: Deletes a subkey and any children
DeleteValue: Deletes the specified value from a key
GetSubKeyNames: Returns an array of strings that contains all the subkey names
GetValue: Returns the specified value
GetValueNames: Retrieves an array of strings that contains all the value names associated with this key
OpenSubKey: Opens a subkey
SetValue: Sets the specified value
Initializing Values
In the Form_Load event, we call the Init procedure, which we are about to create. The Init procedure gets values from the Registry. We use the GetValue method, which returns the value of a subkey in the form of Object. We then initialize the values displayed by the labels so that the user can visually see the values. Initializing the values for the labels calls the InitLabels procedure.
Here are the two procedures:
Private Sub Init() DUp = pRegKey.GetValue("Up") DDown = pRegKey.GetValue("Down") DLeft = pRegKey.GetValue("Left") DRight = pRegKey.GetValue("Right") D45 = pRegKey.GetValue("45") D135 = pRegKey.GetValue("135") D225 = pRegKey.GetValue("225") D315 = pRegKey.GetValue("315") DReturnPos = pRegKey.GetValue("ReturnPos") ' Setup delay & return position DDelay = pRegKey.GetValue("Delay") pRegKey.Close() JEnabled = False Timer1.Interval = Int(DDelay) InitLabels(DUp, lblUp) InitLabels(DDown, lblDown) InitLabels(DLeft, lblLeft) InitLabels(DRight, lblRight) InitLabels(D45, lbl45) InitLabels(D135, lbl135) InitLabels(D225, lbl225) InitLabels(D315, lbl315) InitLabels(DReturnPos, lblReturnPos) InitLabels(DDelay, lblDelay) InkEdit1.Text = "" End Sub Private Sub InitLabels(ByVal name As Object, ByVal lbl As Label) lbl.BackColor = Color.Transparent lbl.ForeColor = Color.Red lbl.TextAlign = ContentAlignment.MiddleCenter lbl.Text = name End Sub
You may have noticed that we set the Timer1 Interval property equal to DDelay. This is the value we retrieved from the Registry. By default, this value is equal to 2000 milliseconds. It's now time to write the code that sends the keys to other applications using the Timer1_Elapsed event.
We begin by testing to see if the application is enabled. If so, we then use a series of If...Then and Case statements to determine where the mouse is positioned on the screen. Depending on where our pen position is on the screen, we send the appropriate keys. For example, if our pen is positioned in the middle of the screen, our joystick would be centered and would not send a key. If we were to move down, the application would then send the value of down.
Here is the code:
Private Sub Timer1_Elapsed(ByVal sender As System.Object, ByVal e As System.Timers.ElapsedEventArgs) Handles Timer1.Elapsed StatusBar1.Text = Control.MousePosition.ToString If JEnabled Then If Control.MousePosition.X > X + 50 And Control.MousePosition.Y < Y - 50 Then SendKeys.Send(D45) Exit Sub ElseIf Control.MousePosition.X > X + 50 And Control.MousePosition.Y > Y + 50 Then SendKeys.Send(D135) Exit Sub ElseIf Control.MousePosition.X < X - 50 And Control.MousePosition.Y > Y + 50 Then SendKeys.Send(D225) Exit Sub ElseIf Control.MousePosition.X < X - 50 And Control.MousePosition.Y < Y - 50 Then SendKeys.Send(D315) Exit Sub End If Select Case Control.MousePosition.X Case Is < X - 100 SendKeys.Send(DLeft) Exit Sub Case Is > X + 100 SendKeys.Send(DRight) Exit Sub End Select Select Case Control.MousePosition.Y Case Is < Y - 100 SendKeys.Send(DUp) Exit Sub Case Is > Y + 100 SendKeys.Send(DDown) Exit Sub End Select End If End Sub
There are a couple of things we have left to do. For starters, we need to have a way to instruct the application when it is enabled. An easy way to do this is to use the Form_MouseDown event and test for the right mouse button being pressed. If the right button is pressed, the application will set JEnabled to the opposite of itself, effectively setting it from True to False or False to True.
Here is the code:
Private Sub Form1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseDown If e.Button = MouseButtons.Right Then JEnabled = Not (JEnabled) End If End Sub
The final step in the application is to look at how we can store new values for the various directions. We can use the SetValue method to set these values, but first, we need a way to change them. Each of the labels has a Click event and we can use the Click event for all of them. For this example, we look at only lblUp. This is also where the InkEdit control comes into play.
We begin this procedure by testing to see if the length of InkEdits text is equal to 1. If so, we assume that the value needs to be stored. Otherwise, we can assume the user entered an incorrect value because we only have the ability to store a single character. We now store the value and then set the InkEdit control to an empty string, finishing by closing the Registry.
Here is the code:
Private Sub lblUp_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lblUp.Click Dim regKey As RegistryKey Dim ver As Decimal If InkEdit1.TextLength = 1 Then regKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\VirtualJoystick", True) lblUp.Text = InkEdit1.Text InkEdit1.Text = "" regKey.SetValue("Up", lblUp.Text) regKey.Close() End If End Sub
The application is now finished. You can test it in several ways. An easy way is to open Notepad and then run the application. Next, right-click the application to enable it and then maximize Notepad. Depending on your pen's position, you should see the various keys being input into Notepad, as shown in Figure 25.4.
Figure 25.4: The application is sending keys to Notepad.
Summary
In this chapter, we built a very unique application that emulates a joystick. We used a variety of new concepts, including reading and writing to the Registry. The application could easily be upgraded by adding code to create a taskbar icon for the application. You could also add the ability to alter the strings for each direction and save them to the Registry. Another interesting idea is to turn this application into a full-screen virtual keyboard, which would send keys to an application depending on the position of the mouse. In Chapter 26, we build a pong game that uses some of the concepts we've looked at for controlling movement.