Tuesday, November 23, 2010

Using Joysticks from VB.NET

There are several ways for using joystick on VB.NET: APIs and DirectX. Here I'll show you the DirectX Method.

First, download DirectX SDK from Microsoft:
http://msdn.microsoft.com/en-us/directx/aa937788.aspx
and then install it.

Starts a new project on VB.NET and add a reference to DirectX:
Project -> Add reference.
Select Microsoft.DirectX.DirectInput from list in ".NET" tab:


First thing to do is obtain a device list, but VB.NET will throw an exception (Loader Lock). Deactivate Loader Lock exception from menu Debud -> Exceptions. Explode "Managed Debugging Assistant", find "Loader Lock" and uncheck the box "Generated":



Imports DirectInput name space at top of your program:


Add a timer (timer1) on your form and set "Enabled" property on true (we'll use it for joystick polling).

Add a textbox named txtDir
Add a textbox named txtButtons

The code is the following:

Imports Microsoft.DirectX.DirectInput

Public Class Form1

    Dim joystickDevice As Device

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        ' List of attached joysticks
        Dim gameControllerList As DeviceList = Manager.GetDevices(DeviceClass.GameControl, EnumDevicesFlags.AttachedOnly)

        ' There is one controller at least?
        If (gameControllerList.Count > 0) Then

            ' Select first joystick
            gameControllerList.MoveNext()
            ' Create an object instance
            Dim deviceInstance As DeviceInstance = gameControllerList.Current

            joystickDevice = New Device(deviceInstance.InstanceGuid)
            joystickDevice.SetCooperativeLevel(Me, CooperativeLevelFlags.Background Or CooperativeLevelFlags.NonExclusive)

        End If

        joystickDevice.SetDataFormat(DeviceDataFormat.Joystick)
        joystickDevice.Acquire()

        Timer1.Start()
        

    End Sub

    Private Sub joystickPolling()

        Try
            joystickDevice.Poll()
            Dim state As JoystickState = joystickDevice.CurrentJoystickState

            txtDir.Text = "X=" & state.X & " Y=" & state.Y & " Z=" & state.Z

            txtbuttons.Text = state.GetButtons(0) & " " & state.GetButtons(1)

        Catch ex As Exception

        End Try
    End Sub

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        joystickPolling()

    End Sub
End Class

state object will contain all informations you need. State.X is the X position of joystick (type state followed by dot and see the entire list of controls).

the GetButtons property of state object will give you the state of buttons. GetButtons give an array. 
Example: 

dim button1 as byte = state.GetButtons(0) ' retrieve first button state

if the value is 128, the button is pressed, if the value is 0 the button isn't pressed or doesn't exists 

7 comments:

  1. Hi, congratulations for this code. I used it on a man machine interface I am designing and it works like a charm. I have a question for you. Is it possible to get an event from one of the buttons of the joystick without using the state.getbuttons property, but with a routine similar to a mouse button (for example click or doubleclick)?
    thanks in advance
    nunzio

    ReplyDelete
  2. This is the only post that resolved my problems, thank you!

    ReplyDelete
  3. I am getting a "is not a valid Win32 application" error when I try to debug. I think its because my system is Windows 7 64 bit. How would I correct this?

    Thanks

    ReplyDelete
  4. I am getting a "is not a valid Win32 application" error when I try to debug. It hightlights "joystickPolling()" in the code. I think its because my system is Windows 7 64 bit. How would I correct this?

    Thanks

    ReplyDelete
  5. I am having the same problem as William. Any suggestions?

    Thanks

    ReplyDelete
  6. Who will help me? how do ...

    Private Sub Button1_pressed (......

    'If joystick button number one pressed then my code

    End sub

    The code works fine, but I need to send the command once , and now it still sends me as long as the button is pressed

    Thanks

    ReplyDelete
  7. Hi this is my code...

    Imports System.Runtime.InteropServices

    Public Class Form1

    Declare Function joyGetPosEx Lib "winmm.dll" (ByVal uJoyID As Integer, ByRef pji As JOYINFOEX) As Integer

    _
    Public Structure JOYINFOEX
    Public dwSize As Integer
    Public dwFlags As Integer
    Public dwXpos As Integer
    Public dwYpos As Integer
    Public dwZpos As Integer
    Public dwRpos As Integer
    Public dwUpos As Integer
    Public dwVpos As Integer
    Public dwButtons As Integer
    Public dwButtonNumber As Integer
    Public dwPOV As Integer
    Public dwReserved1 As Integer
    Public dwReserved2 As Integer
    End Structure

    Dim myjoyEX As JOYINFOEX

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick

    ' Get the joystick information
    Call joyGetPosEx(0, myjoyEX)

    With myjoyEX
    Label10.Text = .dwXpos.ToString 'Up to six axis supported
    Label11.Text = .dwYpos.ToString
    Label12.Text = .dwZpos.ToString
    Label13.Text = .dwRpos.ToString
    Label14.Text = .dwUpos.ToString
    Label15.Text = .dwVpos.ToString
    Label16.Text = .dwButtons.ToString("X") 'Print in Hex, so can see the individual bits associated with the buttons
    Label17.Text = .dwButtonNumber.ToString 'number of buttons pressed at the same time
    Label18.Text = (.dwPOV / 100).ToString 'POV hat (in 1/100ths of degrees, so divided by 100 to give degrees)
    End With
    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    myjoyEX.dwSize = 64
    myjoyEX.dwFlags = &HFF ' All information
    Timer1.Interval = 200 'Update at 5 hz
    Timer1.Start()
    End Sub
    End Class

    Its work for one joystick gamepad but if more the two device doesn't work the other one
    What will I do? Thanks in advanced

    ReplyDelete