user736893
user736893

Reputation:

vb.net gamepad support, partially working

I found the following class code on a forum. It works great for the gamepad (up, down, left, right) however all the code for the buttons is missing. Can anyone fill in the blanks?

This works:

Private Sub joystick1_Up() Handles joystick1.Up
    moveUp()
End Sub

This does not:

Private Sub joystick1_buttonPressed() Handles joystick1.buttonPressed
    MsgBox(joystick1.btnValue)
End Sub

because there is no "buttonPressed" event and I have no idea how to write it.

And here's the class:

Imports System.ComponentModel
Imports System.Runtime.InteropServices


Public Class joystick
    Inherits NativeWindow

    Private parent As Form
    Private Const MM_JOY1MOVE As Integer = &H3A0

    ' Public Event Move(ByVal joystickPosition As Point)
    Public btnValue As String
    Public Event Up()
    Public Event Down()
    Public Event Left()
    Public Event Right()

    <StructLayout(LayoutKind.Explicit)> _
    Private Structure JoyPosition
        <FieldOffset(0)> _
        Public Raw As IntPtr
        <FieldOffset(0)> _
        Public XPos As UShort
        <FieldOffset(2)> _
        Public YPos As UShort
    End Structure

    Private Class NativeMethods

        Private Sub New()
        End Sub

        ' This is a "Stub" function - it has no code in its body.
        ' There is a similarly named function inside a dll that comes with windows called
        ' winmm.dll. 
        ' The .Net framework will route calls to this function, through to the dll file.
        <DllImport("winmm", CallingConvention:=CallingConvention.Winapi, EntryPoint:="joySetCapture", SetLastError:=True)> _
        Public Shared Function JoySetCapture(ByVal hwnd As IntPtr, ByVal uJoyID As Integer, ByVal uPeriod As Integer, <MarshalAs(UnmanagedType.Bool)> ByVal changed As Boolean) As Integer
        End Function

    End Class

    Public Sub New(ByVal parent As Form, ByVal joyId As Integer)
        AddHandler parent.HandleCreated, AddressOf Me.OnHandleCreated
        AddHandler parent.HandleDestroyed, AddressOf Me.OnHandleDestroyed
        AssignHandle(parent.Handle)
        Me.parent = parent
        Dim result As Integer = NativeMethods.JoySetCapture(Me.Handle, joyId, 100, True)
    End Sub

    Private Sub OnHandleCreated(ByVal sender As Object, ByVal e As EventArgs)
        AssignHandle(DirectCast(sender, Form).Handle)
    End Sub

    Private Sub OnHandleDestroyed(ByVal sender As Object, ByVal e As EventArgs)
        ReleaseHandle()
    End Sub

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        If m.Msg = MM_JOY1MOVE Then
            ' Joystick co-ords.
            ' (0,0) (32768,0) (65535, 0) 
            '
            '
            '
            ' (0, 32768) (32768, 32768) (65535, 32768)
            '
            '
            '
            '
            ' (0, 65535) (32768, 65535) (65535, 65535)
            '

            Dim p As JoyPosition
            p.Raw = m.LParam
            ' RaiseEvent Move(New Point(p.XPos, p.YPos))
            If p.XPos > 16384 AndAlso p.XPos < 49152 Then
                ' X is near the centre line.
                If p.YPos < 6000 Then
                    ' Y is near the top.
                    RaiseEvent Up()
                ElseIf p.YPos > 59536 Then
                    ' Y is near the bottom.
                    RaiseEvent Down()
                End If
            Else
                If p.YPos > 16384 AndAlso p.YPos < 49152 Then
                    ' Y is near the centre line
                    If p.XPos < 6000 Then
                        ' X is near the left.
                        RaiseEvent Left()
                    ElseIf p.XPos > 59536 Then
                        ' X is near the right
                        RaiseEvent Right()
                    End If
                End If
            End If
        End If
        If btnValue <> m.WParam.ToString Then
            btnValue = m.WParam.ToString
        End If
        MyBase.WndProc(m)
    End Sub

End Class

Upvotes: 1

Views: 1812

Answers (1)

Jason Olson
Jason Olson

Reputation: 3696

Instead of using the older winmm, I would use XInput instead (or if you can, use XNA).

There are a couple of ways you could go to do this. One step up is to use the XInput dlls directly as outlined by this question on the MSDN forums. That's still fairly ugly though. Probably the "easier" way to do this is using a wrapper library that exists out there like SlimDX or SharpDX.

One of the advantages of using XInput via SlimDX or SharpDX is that it will also work within a Windows Store app for Windows 8 :).

Here's a snippet from the GamePad sample in SharpDX:

C#

var controllers = new[] { new Controller(UserIndex.One), new Controller(UserIndex.Two), new Controller(UserIndex.Three), new Controller(UserIndex.Four) };

// Get 1st controller available 
Controller controller = null; 
foreach (var selectControler in controllers) 
{ 
    if (selectControler.IsConnected) 
    { 
        controller = selectControler; 
        break; 
    }
}

VB

Dim controllers As New List(Of Controller)
controllers.Add(New Controller(UserIndex.One))
controllers.Add(New Controller(UserIndex.Two))
controllers.Add(New Controller(UserIndex.Three))
controllers.Add(New Controller(UserIndex.Four))

Dim controller as Controller = Nothing; 
For Each selectController In controllers
    If selectController.IsConnected Then
        controller = selectController
        Exit For
    End If
Next

Then you can get the state to work with using:

var state = controller.GetState();

You will notice that XInput uses more of a polling model, so you will need to occasionally check for a button press to detect this. If you need to poll continuously, you can probably spin up a new Task to do this on.

Upvotes: 2

Related Questions