Hallo liebe Community,
Ich habe mir in Eigenregie eine Möglichkeit gesucht, meinen Xbox-Controller für den PC auszulesen, ohne XNA nutzen zu müssen. Nach einigen Stolperfallen und Sackgassen kann ich nun mit einer funktionstüchtigen Klasse aufwarten, die die gewünschte Funktionalität beinhaltet. Ohne da noch viel Text hinzuklatschen möge das Klassendiagramm einen ersten Eindruck vermitteln:
Klassendiagramm:
Funktionsweise:
Die Klasse nutzt hierbei Funktionen der Winmm.dll und je nach System die XInput1_4.dll(Windows 8.X) oder die XInput9_1_0.dll(Windows Vista). Nach der Instanzierung, in der das Handle für das Behandlen der Message-Verarbeitung und den Rang des Xbox-Controllers(falls mehrere angeschlossen sind) übergeben wird, kann mit der
Code:
Nun kommen wir ans Eingemachte:
Spoiler anzeigen
Zu beachten:
Weiteres:
Ich bin für alle Korrekturen und Verbesserungsvorschläge offen und hoffe auf Unterstützung seitens der erfahreneren Mitglieder. Ich habe noch nicht viel Erfahrung mit
Eine ältere Version wird in der ControllerDLL von @xd-franky-5 verwendet.
Beispielanwendung:
XBoxController.rar
Vorschau:
Ich habe mir in Eigenregie eine Möglichkeit gesucht, meinen Xbox-Controller für den PC auszulesen, ohne XNA nutzen zu müssen. Nach einigen Stolperfallen und Sackgassen kann ich nun mit einer funktionstüchtigen Klasse aufwarten, die die gewünschte Funktionalität beinhaltet. Ohne da noch viel Text hinzuklatschen möge das Klassendiagramm einen ersten Eindruck vermitteln:
Klassendiagramm:
Funktionsweise:
Die Klasse nutzt hierbei Funktionen der Winmm.dll und je nach System die XInput1_4.dll(Windows 8.X) oder die XInput9_1_0.dll(Windows Vista). Nach der Instanzierung, in der das Handle für das Behandlen der Message-Verarbeitung und den Rang des Xbox-Controllers(falls mehrere angeschlossen sind) übergeben wird, kann mit der
Poll()
-Methode die aktuellsten Daten des Controllers abgerufen werden, welche dann über die Properties des Typs erreichbar sind. Über die StartCapturing(onlySendMsgWhenChanged As Boolean)
-Funktion wird über Winmm der Typ verlinkt, sodass dieser alle Meldungen bekommt. Sind controllerspezifische Meldungen vorhanden, so wird über XInput der Controller vollständig ausgelesen(die mitgelieferten Daten sind unvollständig), indem wiederum die Poll()
-Methode aufgerufen wird. Um Meldungen an den Controller zu sende, stehen zwei Funktionen für das Senden von Vibrationen bereit - Vibrate(leftMotor As UShort, rightMotor As UShort)
und StartVibration(leftMotor As UShort, rightMotor As UShort, length As TimeSpan)
. Das Capturing wird über die Methode StopCapturing()
gestoppt, wobei der Typ sich aus der Schlange der WindowsMessage-Empfängern ausklinkt.Code:
Nun kommen wir ans Eingemachte:
Quellcode
- Imports System.Runtime.InteropServices
- Imports System.Windows
- 'Hierfür ein Verweis auf "WindowsBase" aus dem Framework hinzufügen.
- Namespace Controller
- ''' <summary>
- ''' Eine Klasse, die dazu dient, Eingaben von Xbox-Controllern auszulesen.
- ''' </summary>
- ''' <remarks></remarks>
- Public Class XboxController
- Inherits NativeWindow
- #Region "Native"
- <DllImport("winmm", CallingConvention:=CallingConvention.Winapi, EntryPoint:="joySetCapture", SetLastError:=True)> _
- Private Shared Function JoySetCapture(ByVal hwnd As IntPtr, ByVal uJoyID As UInteger, ByVal uPeriod As Integer, <MarshalAs(UnmanagedType.Bool)> ByVal changed As Boolean) As JoyError
- End Function
- <DllImport("winmm", CallingConvention:=CallingConvention.Winapi, EntryPoint:="joyReleaseCapture", SetLastError:=True)> _
- Private Shared Function JoyReleaseCapture(ByVal uJoyID As UInteger) As JoyError
- End Function
- <DllImport("XInput1_4", EntryPoint:="XInputGetState", setlasterror:=True)> _
- Private Shared Function XInputGetState_1_4(ByVal dwUserIndex As UInteger, ByRef pState As XINPUT_STATE) As UInteger
- End Function
- <DllImport("XInput9_1_0", EntryPoint:="XInputGetState", setlasterror:=True)> _
- Private Shared Function XInputGetState_9_1_0(ByVal dwUserIndex As UInteger, ByRef pState As XINPUT_STATE) As UInteger
- End Function
- <DllImport("XInput1_4", EntryPoint:="XInputSetState")> _
- Private Shared Function XInputSetState_1_4(ByVal dwUserIndex As UInteger, ByRef pVibration As XINPUT_VIBRATION) As UInteger
- End Function
- <DllImport("XInput9_1_0", EntryPoint:="XInputSetState", setlasterror:=True)> _
- Private Shared Function XInputSetState_9_1_0(ByVal dwUserIndex As UInteger, ByRef pVibration As XINPUT_VIBRATION) As UInteger
- End Function
- #End Region
- #Region "Variables"
- Private _Controller As UInteger
- Private _Win8 As Boolean
- Private _Initialized As Boolean
- Private _LSDead As UShort = 7849
- Private _RSDead As UShort = 8689
- Private _TDead As Byte = 30
- Private _StickMaxRadius As UShort = Short.MaxValue
- Private _TrimStickLength As Boolean = False
- Private _LastState As XINPUT_GAMEPAD
- #End Region
- #Region "Properties"
- ''' <summary>
- ''' Gibt die Rangnummer des angeschlossenen Controllers an.
- ''' </summary>
- ''' <returns>Einen Wert von 0 bis 3</returns>
- Public ReadOnly Property ControllerNumber As UInteger
- Get
- Return _Controller
- End Get
- End Property
- ''' <summary>
- ''' Gibt den Radius der Deadzone für den linken Stick an, oder legt diese fest.
- ''' </summary>
- ''' <returns>Den Radius der Deadzone für den linken Stick zwischen 0 und <seealso cref="Short.MaxValue"/></returns>
- Public Property LeftStickDeadzoneRadius As UShort
- Get
- Return _LSDead
- End Get
- Set(value As UShort)
- _LSDead = value
- End Set
- End Property
- ''' <summary>
- ''' Gibt den Radius der Deadzone für den rechten Stick an, oder legt diese fest.
- ''' </summary>
- ''' <returns>Den Radius der Deadzone für den rechten Stick zwischen 0 und <seealso cref="Short.MaxValue"/></returns>
- Public Property RightStickDeadzoneRadius As UShort
- Get
- Return _RSDead
- End Get
- Set(value As UShort)
- _RSDead = value
- End Set
- End Property
- ''' <summary>
- ''' Gibt den Wert für die Deadzone der Trigger an, oder legt diese fest.
- ''' </summary>
- ''' <returns>Einen Wert zwischen 0 und <seealso cref="Byte.MaxValue"/></returns>
- Public Property TriggerDeadzone As Byte
- Get
- Return _TDead
- End Get
- Set(value As Byte)
- _TDead = value
- End Set
- End Property
- ''' <summary>
- ''' Gibt den Radius für das Triggern des Maximalwertes bei den Sticks an, oder legt diesen fest.
- ''' </summary>
- ''' <returns>Einen Wert zwischen 0 und <seealso cref="Short.MaxValue"/></returns>
- ''' <remarks>Ist redundant, wenn <see cref="TrimStickLength"/> = True ist.</remarks>
- Public Property StickMaximumRadius As UShort
- Get
- Return _StickMaxRadius
- End Get
- Set(value As UShort)
- _StickMaxRadius = value
- End Set
- End Property
- ''' <summary>
- ''' Gibt an, ob die Länge des Vektors der Sticks so getrimmt werden soll, dass sie <see cref="Short.MyxValue"/> entspricht und somit einen vollständigen Kreis beschreibt, oder nicht.
- ''' </summary>
- Public Property TrimStickLength As Boolean
- Get
- Return _TrimStickLength
- End Get
- Set(value As Boolean)
- _TrimStickLength = value
- End Set
- End Property
- ''' <summary>
- ''' Gibt die aus dem letzen Poll gedrückten Buttons zurück.
- ''' </summary>
- ''' <returns>Gibt einen <seealso cref="Short"/>-Wert zurück, der als Falg-Enumeration die Informationen über die gedrückten Buttons enthält.</returns>
- Public ReadOnly Property PressedButtons As Buttons
- Get
- Return CType(_LastState.wButtons, Buttons)
- End Get
- End Property
- ''' <summary>
- ''' Gibt den Vektor des linken Sticks aus dem letzten Poll zurück.
- ''' </summary>
- ''' <returns>Vektor des Typs <see cref="Vector"/></returns>
- Public ReadOnly Property LeftStickLocation As Vector
- Get
- Return New Vector(_LastState.sThumbLX, _LastState.sThumbLY)
- End Get
- End Property
- ''' <summary>
- ''' Gibt die Richtung an, in die der linke Stick aus dem letzten Poll zeigt.
- ''' </summary>
- ''' <returns>Gibt einen <seealso cref="Byte"/>-Wert zurück, der als Flag-Enumeration die Informationen über die Richtungen des Sticks beinhaltet.</returns>
- Public ReadOnly Property LeftStickDirection As StickDirection
- Get
- Return StickChangedEventArgs.GetDirection(LeftStickLocation)
- End Get
- End Property
- ''' <summary>
- ''' Gibt den Vektor des rechten Sticks aus dem letzten Poll zurück.
- ''' </summary>
- ''' <returns>Vektor des Typs <see cref="Vector"/></returns>
- Public ReadOnly Property RightStickLocation As Vector
- Get
- Return New Vector(_LastState.sThumbRX, _LastState.sThumbRY)
- End Get
- End Property
- ''' <summary>
- ''' Gibt die Richtung an, in die der rechte Stick aus dem letzten Poll zeigt.
- ''' </summary>
- ''' <returns>Gibt einen <seealso cref="Byte"/>-Wert zurück, der als Flag-Enumeration die Informationen über die Richtungen des Sticks beinhaltet.</returns>
- Public ReadOnly Property RightStickDirection As StickDirection
- Get
- Return StickChangedEventArgs.GetDirection(RightStickLocation)
- End Get
- End Property
- ''' <summary>
- ''' Gibt den Wert aus dem letzten Poll für den linken Trigger zurück.
- ''' </summary>
- ''' <returns>Einen Wert zwischen der Grenze der <see cref="TriggerDeadzone">Deadzone</see> und <seealso cref="Byte.MaxValue"/> zurück.</returns>
- Public ReadOnly Property LeftTriggerValue As Byte
- Get
- Return _LastState.bLeftTrigger
- End Get
- End Property
- ''' <summary>
- ''' Gibt den Wert aus dem letzten Poll für den rechten Trigger zurück.
- ''' </summary>
- ''' <returns>Einen Wert zwischen der Grenze der <see cref="TriggerDeadzone">Deadzone</see> und <seealso cref="Byte.MaxValue"/> zurück.</returns>
- Public ReadOnly Property RightTriggerValue As Byte
- Get
- Return _LastState.bRightTrigger
- End Get
- End Property
- #End Region
- #Region "Events"
- ''' <summary>
- ''' Tritt ein, wenn ein Button seit dem letzten Poll gedrückt wurde.
- ''' </summary>
- Public Event ButtonPressed(sender As Object, e As ButtonChangeEventArgs)
- ''' <summary>
- ''' Tritt ein, wenn ein Button seit dem letzten Poll losgelassen wurde.
- ''' </summary>
- Public Event ButtonReleased(sender As Object, e As ButtonChangeEventArgs)
- ''' <summary>
- ''' Tritt ein, wenn der linke Stick seit dem letzten Poll eine Änderung erfuhr.
- ''' </summary>
- ''' <remarks>Tritt in jedem Fall ein.</remarks>
- Public Event LeftStickChanged(sender As Object, e As StickChangedEventArgs)
- ''' <summary>
- ''' Tritt ein, wenn der linke Stick seit dem letzten Poll aus der Deadzone ausbrach.
- ''' </summary>
- Public Event LeftStickStarted(sender As Object, e As StickChangedEventArgs)
- ''' <summary>
- ''' Tritt ein, wenn der linke Stick seit dem letzten Poll den Maximalwert erreichte, oder immernoch diesem entspricht.
- ''' </summary>
- Public Event LeftStickReachedMaximum(sender As Object, e As StickChangedEventArgs)
- ''' <summary>
- ''' Tritt ein, wenn der linke Stick seit dem letzten Poll in die Deadzone fiel.
- ''' </summary>
- Public Event LeftStickStopped(sender As Object, e As EventArgs)
- ''' <summary>
- ''' Tritt ein, wenn der rechte Stick seit dem letzten Poll eine Änderung erfuhr.
- ''' </summary>
- ''' <remarks>Tritt in jedem Fall ein.</remarks>
- Public Event RightStickChanged(sender As Object, e As StickChangedEventArgs)
- ''' <summary>
- ''' Tritt ein, wenn der rechte Stick seit dem letzten Poll aus der Deadzone ausbrach.
- ''' </summary>
- Public Event RightStickStarted(sender As Object, e As StickChangedEventArgs)
- ''' <summary>
- ''' Tritt ein, wenn der rechte Stick seit dem letzten Poll den Maximalwert erreichte, oder immernoch diesem entspricht.
- ''' </summary>
- Public Event RightStickReachedMaximum(sender As Object, e As StickChangedEventArgs)
- ''' <summary>
- ''' Tritt ein, wenn der rechte Stick seit dem letzten Poll in die Deadzone fiel.
- ''' </summary>
- Public Event RightStickStopped(sender As Object, e As EventArgs)
- ''' <summary>
- ''' Tritt ein, wenn der linke Trigger seit dem letzten Poll eine Änderung erfuhr.
- ''' </summary>
- ''' <remarks>Tritt in jedem Fall ein.</remarks>
- Public Event LeftTriggerChanged(sender As Object, e As TriggerChangedEventArgs)
- ''' <summary>
- ''' Tritt ein, wenn der linke Trigger seit dem letzten Poll aus der Deadzone ausbrach.
- ''' </summary>
- Public Event LeftTriggerStarted(sender As Object, e As EventArgs)
- ''' <summary>
- ''' Tritt ein, wenn der linke Trigger seit dem letzten Poll den Maximalwert erreichte, oder immernoch diesem entspricht.
- ''' </summary>
- Public Event LeftTriggerReachedMaximum(sender As Object, e As EventArgs)
- ''' <summary>
- ''' Tritt ein, wenn der linke Trigger seit dem letzten Poll in die Deadzone fiel.
- ''' </summary>
- Public Event LeftTriggerStopped(sender As Object, e As EventArgs)
- ''' <summary>
- ''' Tritt ein, wenn der rechte Trigger seit dem letzten Poll eine Änderung erfuhr.
- ''' </summary>
- ''' <remarks>Tritt in jedem Fall ein.</remarks>
- Public Event RightTriggerChanged(sender As Object, e As TriggerChangedEventArgs)
- ''' <summary>
- ''' Tritt ein, wenn der rechte Trigger seit dem letzten Poll aus der Deadzone ausbrach.
- ''' </summary>
- Public Event RightTriggerStarted(sender As Object, e As EventArgs)
- ''' <summary>
- ''' Tritt ein, wenn der rechte Trigger seit dem letzten Poll den Maximalwert erreichte, oder immernoch diesem entspricht.
- ''' </summary>
- Public Event RightTriggerReachedMaximum(sender As Object, e As EventArgs)
- ''' <summary>
- ''' Tritt ein, wenn der rechte Trigger seit dem letzten Poll in die Deadzone fiel.
- ''' </summary>
- Public Event RightTriggerStopped(sender As Object, e As EventArgs)
- #End Region
- #Region "Preset"
- 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
- #End Region
- ''' <summary>
- ''' Initialisiert die Klasse und bereitet das Capturing vor.
- ''' </summary>
- ''' <param name="parent">Eine Form, über deren Handle das Capturing erfolgt.</param>
- ''' <param name="controllerNumber">Rangnummer des angeschlossenen Xbox-Controllers. Muss einschliesslich zwischen 0 und 3 liegen.</param>
- Public Sub New(parent As Form, controllerNumber As UInteger)
- If controllerNumber > 3 Then Throw New ArgumentOutOfRangeException("controllerNumber", "variable must be an element out of 0, 1, 2, 3.")
- _Controller = controllerNumber
- With Environment.OSVersion.Version
- If .Major <> 6 Then Throw New Exception("The system does not match the requirements.")
- _Win8 = .Minor > 1
- End With
- AddHandler parent.HandleCreated, AddressOf Me.OnHandleCreated
- AddHandler parent.HandleDestroyed, AddressOf Me.OnHandleDestroyed
- AssignHandle(parent.Handle)
- End Sub
- ''' <summary>
- ''' Startet das Capturing.
- ''' </summary>
- ''' <param name="onlySendMsgWhenChanged">Legt fest, ob nur dann die Eingabe verarbeitet werden soll, wenn eine erfolgt. Wenn True ergeben sich eventuell Probleme beim Capturing von Buttonclicks.</param>
- ''' <returns>True, wenn das Einhängen geklappt hat, andernfalls False.</returns>
- Public Function StartCapturing(onlySendMsgWhenChanged As Boolean) As Boolean
- If _Initialized Then Return False
- Select Case JoySetCapture(Me.Handle, _Controller, 1, onlySendMsgWhenChanged)
- Case JoyError.JOYERR_NOERROR
- _Initialized = True
- Case Else
- 'Console.WriteLine(Result.ToString)
- End Select
- Return _Initialized
- End Function
- ''' <summary>
- ''' Stoppt das Capturing
- ''' </summary>
- ''' <returns>True, wenn das Lösen erfolgreich war, andernfalls False.</returns>
- Public Function StopCapturing() As Boolean
- If Not _Initialized Then Return False
- Select Case JoyReleaseCapture(_Controller)
- Case JoyError.JOYERR_NOERROR
- _Initialized = False
- Case Else
- 'Console.WriteLine(Result.ToString)
- End Select
- Me.ReleaseHandle()
- Return Not _Initialized
- End Function
- ''' <summary>
- ''' Sendet Vibration an den Controller.
- ''' </summary>
- ''' <param name="leftMotor">Wert für die Vibrationsintensität des linken Motors. Wert muss einschliesslich zwischen 0 und <seealso cref="UShort.MaxValue"/> liegen.</param>
- ''' <param name="rightMotor">Wert für die Vibrationsintensität des rechten Motors. Wert muss einschliesslich zwischen 0 und <seealso cref="UShort.MaxValue"/> liegen.</param>
- ''' <returns>True, wenn das Senden erfolgreich war, andernfalls False.</returns>
- Public Function Vibrate(leftMotor As UShort, rightMotor As UShort) As Boolean
- Dim Vibration As New XINPUT_VIBRATION(leftMotor, rightMotor)
- If _Win8 Then
- Return XInputSetState_1_4(0, Vibration) = 0
- Else
- Return XInputSetState_9_1_0(0, Vibration) = 0
- End If
- End Function
- ''' <summary>
- ''' Sendet Vibration asynchron an den Controller.
- ''' </summary>
- ''' <param name="leftMotor">Wert für die Vibrationsintensität des linken Motors. Wert muss einschliesslich zwischen 0 und <seealso cref="UShort.MaxValue"/> liegen.</param>
- ''' <param name="rightMotor">Wert für die Vibrationsintensität des rechten Motors. Wert muss einschliesslich zwischen 0 und <seealso cref="UShort.MaxValue"/> liegen.</param>
- ''' <param name="length">Wert für die Dauer, der Vibration.</param>
- ''' <returns>True, wenn das Senden erfolgreich war, andernfalls False.</returns>
- Public Async Function StartVibration(leftMotor As UShort, rightMotor As UShort, length As TimeSpan) As Task(Of Boolean)
- Dim Success1 = Vibrate(leftMotor, rightMotor)
- Await Task.Delay(length)
- Dim Success2 = Vibrate(0, 0)
- Return Success1 And Success2
- End Function
- Protected Overrides Sub WndProc(ByRef m As Message)
- Select Case CType(m.Msg, MMRESULT)
- Case Is = MMRESULT.MM_JOY1MOVE, MMRESULT.MM_JOY1ZMOVE, MMRESULT.MM_JOY1BUTTONDOWN, MMRESULT.MM_JOY1BUTTONUP
- Poll()
- Case Else
- 'do nothing
- End Select
- MyBase.WndProc(m)
- End Sub
- ''' <summary>
- ''' Holt die aktuellsten Daten zum Xbox-Controller.
- ''' </summary>
- ''' <remarks>Wird beim Capturing automatisch aufgerufen.</remarks>
- Public Sub Poll()
- Dim State As New XINPUT_STATE(_Controller, New XINPUT_GAMEPAD)
- Dim Result As UInteger
- If _Win8 Then
- Result = XInputGetState_1_4(0, State)
- Else
- Result = XInputGetState_9_1_0(0, State)
- End If
- If Result <> 0 Then Throw New Exception("Could not get states")
- With State.Gamepad
- 'Buttons:
- If Not _LastState.wButtons = .wButtons Then
- Dim OldBtns = _LastState.wButtons
- Dim NewBtns = .wButtons
- 'Elaborating Changes by using bitwise operations:
- Dim ChangedBtns = (_LastState.wButtons Or NewBtns)
- Dim NewPressed = ChangedBtns - OldBtns
- Dim NewReleased = ChangedBtns - NewBtns
- If Not NewPressed = 0 Then
- RaiseEvent ButtonPressed(Me, New ButtonChangeEventArgs(CType(NewPressed, Buttons)))
- End If
- If Not NewReleased = 0 Then
- RaiseEvent ButtonReleased(Me, New ButtonChangeEventArgs(CType(NewReleased, Buttons)))
- End If
- End If
- 'LeftStick:
- If .sThumbLX <> _LastState.sThumbLX OrElse .sThumbLY <> _LastState.sThumbLY Then
- Dim LS As New Vector(.sThumbLX, .sThumbLY)
- Dim LPressed = CType(.wButtons, Buttons).HasFlag(Buttons.LEFT_STICK)
- If LS.Length < _LSDead Then
- .sThumbLX = 0
- .sThumbLY = 0
- If _LastState.sThumbLX <> 0 AndAlso _LastState.sThumbLY <> 0 Then
- 'Stop Moving
- RaiseEvent LeftStickChanged(Me, New StickChangedEventArgs(New Vector(0, 0), LPressed))
- RaiseEvent LeftStickStopped(Me, EventArgs.Empty)
- End If
- Else
- If _LastState.sThumbLX = 0 AndAlso _LastState.sThumbLY = 0 Then
- 'Start Moving
- RaiseEvent LeftStickChanged(Me, New StickChangedEventArgs(LS, LPressed))
- RaiseEvent LeftStickStarted(Me, New StickChangedEventArgs(LS, LPressed))
- Else
- If _TrimStickLength Then
- If LS.Length > Short.MaxValue Then
- LS.Normalize()
- LS *= Short.MaxValue
- End If
- If LS.Length >= Short.MaxValue Then
- 'Maximum reached
- RaiseEvent LeftStickChanged(Me, New StickChangedEventArgs(LS, LPressed))
- RaiseEvent LeftStickReachedMaximum(Me, New StickChangedEventArgs(LS, LPressed))
- Else
- 'Is Moving
- RaiseEvent LeftStickChanged(Me, New StickChangedEventArgs(LS, LPressed))
- End If
- ElseIf .sThumbLX = Short.MaxValue OrElse
- .sThumbLX = Short.MinValue OrElse
- .sThumbLY = Short.MaxValue OrElse
- .sThumbLY = Short.MinValue OrElse
- LS.Length >= _StickMaxRadius Then
- 'Maximum reached
- RaiseEvent LeftStickChanged(Me, New StickChangedEventArgs(LS, LPressed))
- RaiseEvent LeftStickReachedMaximum(Me, New StickChangedEventArgs(LS, LPressed))
- Else
- 'Is Moving
- RaiseEvent LeftStickChanged(Me, New StickChangedEventArgs(LS, LPressed))
- End If
- End If
- End If
- End If
- 'RightStick:
- If .sThumbRX <> _LastState.sThumbRX OrElse .sThumbRY <> _LastState.sThumbRY Then
- Dim RS As New Vector(.sThumbRX, .sThumbRY)
- Dim RPressed = CType(.wButtons, Buttons).HasFlag(Buttons.RIGHT_STICK)
- If RS.Length < _LSDead Then
- .sThumbRX = 0
- .sThumbRY = 0
- If _LastState.sThumbRX <> 0 AndAlso _LastState.sThumbRY <> 0 Then
- 'Stop Moving
- RaiseEvent RightStickChanged(Me, New StickChangedEventArgs(New Vector(0, 0), RPressed))
- RaiseEvent RightStickStopped(Me, EventArgs.Empty)
- End If
- Else
- If _LastState.sThumbRX = 0 AndAlso _LastState.sThumbRY = 0 Then
- 'Start Moving
- RaiseEvent RightStickChanged(Me, New StickChangedEventArgs(RS, RPressed))
- RaiseEvent RightStickStarted(Me, New StickChangedEventArgs(RS, RPressed))
- Else
- If _TrimStickLength Then
- If RS.Length > Short.MaxValue Then
- RS.Normalize()
- RS *= Short.MaxValue
- End If
- If RS.Length >= Short.MaxValue Then
- 'Maximum reached
- RaiseEvent RightStickChanged(Me, New StickChangedEventArgs(RS, RPressed))
- RaiseEvent RightStickReachedMaximum(Me, New StickChangedEventArgs(RS, RPressed))
- Else
- 'Is Moving
- RaiseEvent RightStickChanged(Me, New StickChangedEventArgs(RS, RPressed))
- End If
- ElseIf .sThumbRX = Short.MaxValue OrElse
- .sThumbRX = Short.MinValue OrElse
- .sThumbRY = Short.MaxValue OrElse
- .sThumbRY = Short.MinValue OrElse
- RS.Length >= _StickMaxRadius Then
- 'Maximum reached
- RaiseEvent RightStickChanged(Me, New StickChangedEventArgs(RS, RPressed))
- RaiseEvent RightStickReachedMaximum(Me, New StickChangedEventArgs(RS, RPressed))
- Else
- 'Is Moving
- RaiseEvent RightStickChanged(Me, New StickChangedEventArgs(RS, RPressed))
- End If
- End If
- End If
- End If
- 'LT:
- Dim LT = .bLeftTrigger
- If LT <> _LastState.bLeftTrigger Then
- If LT < _TDead Then
- .bLeftTrigger = 0
- If _LastState.bLeftTrigger <> 0 Then
- 'Stop Moving
- RaiseEvent LeftTriggerChanged(Me, New TriggerChangedEventArgs(0))
- RaiseEvent LeftTriggerStopped(Me, EventArgs.Empty)
- End If
- Else
- If _LastState.bLeftTrigger = 0 Then
- 'Start Moving
- RaiseEvent LeftTriggerChanged(Me, New TriggerChangedEventArgs(LT))
- RaiseEvent LeftTriggerStarted(Me, EventArgs.Empty)
- Else
- If LT = Byte.MaxValue Then
- 'Maximum reached
- RaiseEvent LeftTriggerChanged(Me, New TriggerChangedEventArgs(LT))
- RaiseEvent LeftTriggerReachedMaximum(Me, EventArgs.Empty)
- Else
- 'Is Moving
- RaiseEvent LeftTriggerChanged(Me, New TriggerChangedEventArgs(LT))
- End If
- End If
- End If
- End If
- 'RT:
- Dim RT = .bRightTrigger
- If RT <> _LastState.bRightTrigger Then
- If RT < _TDead Then
- .bRightTrigger = 0
- If _LastState.bRightTrigger <> 0 Then
- 'Stop Moving
- RaiseEvent RightTriggerChanged(Me, New TriggerChangedEventArgs(0))
- RaiseEvent RightTriggerStopped(Me, EventArgs.Empty)
- End If
- Else
- If _LastState.bRightTrigger = 0 Then
- 'Start Moving
- RaiseEvent RightTriggerChanged(Me, New TriggerChangedEventArgs(RT))
- RaiseEvent RightTriggerStarted(Me, EventArgs.Empty)
- Else
- If RT = Byte.MaxValue Then
- 'Maximum reached
- RaiseEvent RightTriggerChanged(Me, New TriggerChangedEventArgs(RT))
- RaiseEvent RightTriggerReachedMaximum(Me, EventArgs.Empty)
- Else
- 'Is Moving
- RaiseEvent RightTriggerChanged(Me, New TriggerChangedEventArgs(RT))
- End If
- End If
- End If
- End If
- End With
- _LastState = State.Gamepad
- End Sub
- End Class
- ''' <summary>
- ''' Stellt Daten für das <see cref="XboxController.ButtonPressed"/>- und das <see cref="XboxController.ButtonReleased"/>-Event bereit.
- ''' </summary>
- Public NotInheritable Class ButtonChangeEventArgs
- Inherits EventArgs
- Private _Buttons As Buttons
- Friend Sub New(newPressed As Buttons)
- _Buttons = newPressed
- End Sub
- ''' <summary>
- ''' Gibt die Buttons zurück, die eine Änderung erfuhren.
- ''' </summary>
- Public ReadOnly Property ChangedButtons As Buttons
- Get
- Return _Buttons
- End Get
- End Property
- End Class
- ''' <summary>
- ''' Stellt Daten für das <see cref="XboxController.LeftStickChanged"/>-, das <see cref="XboxController.RightStickChanged"/>-,
- ''' das <see cref="XboxController.LeftStickReachedMaximum"/>-, das <see cref="XboxController.RightStickReachedMaximum"/>-,
- ''' das <see cref="XboxController.LeftStickStarted"/>- und das <see cref="XboxController.RightStickStarted"/>-Event bereit.
- ''' </summary>
- Public NotInheritable Class StickChangedEventArgs
- Inherits EventArgs
- Private _Vec As Vector
- Private _Pressed As Boolean
- Friend Sub New(vec As Vector, pressed As Boolean)
- _Vec = vec
- _Pressed = pressed
- End Sub
- ''' <summary>
- ''' Gibt den Vektor zurück, die die Verschiebung eines Sticks beschreibt.
- ''' </summary>
- ''' <returns>Einen Vektor des Typs <see cref="Windows.Vector"/></returns>
- Public ReadOnly Property Vector As Windows.Vector
- Get
- Return _Vec
- End Get
- End Property
- ''' <summary>
- ''' Gibt die Stärke an, mit der der Stick zum Maximum gedrückt wurde.
- ''' </summary>
- Public ReadOnly Property Value As Double
- Get
- Return _Vec.Length
- End Get
- End Property
- ''' <summary>
- ''' Gibt die Richtungen zurück, in die der Stick gedrückt wurde.
- ''' </summary>
- Public ReadOnly Property Direction As StickDirection
- Get
- Return GetDirection(_Vec)
- End Get
- End Property
- ''' <summary>
- ''' Gibt an, ob der Stick gedrückt wurde, oder nicht.
- ''' </summary>
- Public ReadOnly Property IsPressed As Boolean
- Get
- Return _Pressed
- End Get
- End Property
- Friend Shared Function GetDirection(vec As Vector) As StickDirection
- If vec.Length <= Double.Epsilon Then Return 0
- Dim Angle = Math.Atan2(vec.Y, vec.X)
- Select Case Angle
- Case Is > Math.Atan2(1, -2), Is < Math.Atan2(-1, -2)
- Return StickDirection.Left
- Case Is > Math.Atan2(2, -1)
- Return StickDirection.Left Or StickDirection.Up
- Case Is < Math.Atan2(-2, -1)
- Return StickDirection.Left Or StickDirection.Down
- Case Is > Math.Atan2(2, 1)
- Return StickDirection.Up
- Case Is < Math.Atan2(-2, 1)
- Return StickDirection.Down
- Case Is > Math.Atan2(1, 2)
- Return StickDirection.Up Or StickDirection.Right
- Case Is < Math.Atan2(-1, 2)
- Return StickDirection.Down Or StickDirection.Right
- Case Else
- Return StickDirection.Right
- End Select
- End Function
- End Class
- ''' <summary>
- ''' Stellt Daten für das <see cref="XboxController.LeftTriggerChanged"/>-, das <see cref="XboxController.RightTriggerChanged"/>-,
- ''' das <see cref="XboxController.LeftTriggerStarted"/>- und das <see cref="XboxController.RightTriggerStarted"/>-Event bereit.
- ''' </summary>
- Public NotInheritable Class TriggerChangedEventArgs
- Inherits EventArgs
- Private _Val As Byte
- Friend Sub New(value As Byte)
- _Val = value
- End Sub
- ''' <summary>
- ''' Gibt den Wert des Triggers zurück.
- ''' </summary>
- ''' <returns>Gibt einen Wert zurück, der sich zwischen <see cref="XboxController.TriggerDeadzone"/> und dem <see cref="Byte.MaxValue"/> liegt.</returns>
- Public ReadOnly Property Value As Byte
- Get
- Return _Val
- End Get
- End Property
- End Class
- ''' <summary>
- ''' Bezeichnet die Richtungen, in die der jeweilige Stick gedrückt wurde an.
- ''' </summary>
- <Flags()> _
- Public Enum StickDirection As Byte
- Left = 1
- Right = 2
- Up = 4
- Down = 8
- End Enum
- ''' <summary>
- ''' Bezeichnet die Buttons, die eine Änderung erfuhren.
- ''' </summary>
- <Flags()> _
- Public Enum Buttons As Short
- DPAD_UP = &H1
- DPAD_DOWN = &H2
- DPAD_LEFT = &H4
- DPAD_RIGHT = &H8
- START = &H10
- BACK = &H20
- LEFT_STICK = &H40
- RIGHT_STICK = &H80
- LEFT_BUTTON = &H100
- RIGHT_BUTTON = &H200
- A = &H1000
- B = &H2000
- X = &H4000
- Y = -&H8000
- End Enum
- Friend Enum MMRESULT As UInteger
- MM_JOY1MOVE = &H3A0
- MM_JOY2MOVE = &H3A1
- MM_JOY1ZMOVE = &H3A2
- MM_JOY2ZMOVE = &H3A3
- MM_JOY1BUTTONDOWN = &H3B5
- MM_JOY2BUTTONDOWN = &H3B6
- MM_JOY1BUTTONUP = &H3B7
- MM_JOY2BUTTONUP = &H3B8
- End Enum
- Friend Enum JoyError As UInteger
- JOYERR_NOERROR = 0
- JOYERR_PARMS = 165
- JOYERR_NOCANDO = 166
- JOYERR_UNPLUGGED = 167
- End Enum
- <StructLayout(LayoutKind.Explicit)> _
- Friend Structure XINPUT_STATE
- <FieldOffset(0)> Public dwPacketNumber As UInteger
- <FieldOffset(4)> Public Gamepad As XINPUT_GAMEPAD
- Public Sub New(pNumber As UInteger, gPad As XINPUT_GAMEPAD)
- dwPacketNumber = pNumber
- Gamepad = gPad
- End Sub
- End Structure
- Friend Structure XINPUT_VIBRATION
- Public wLeftMotorSpeed As UShort
- Public wRightMotorSpeed As UShort
- Public Sub New(lMot As UShort, rMot As UShort)
- wLeftMotorSpeed = lMot
- wRightMotorSpeed = rMot
- End Sub
- End Structure
- <StructLayout(LayoutKind.Explicit)> _
- Friend Structure XINPUT_GAMEPAD
- <FieldOffset(0)> Public wButtons As Short
- <FieldOffset(2)> Public bLeftTrigger As Byte
- <FieldOffset(3)> Public bRightTrigger As Byte
- <FieldOffset(4)> Public sThumbLX As Short
- <FieldOffset(6)> Public sThumbLY As Short
- <FieldOffset(8)> Public sThumbRX As Short
- <FieldOffset(10)> Public sThumbRY As Short
- Public Overrides Function ToString() As String
- Return String.Format("wButtons={0}, bLeftTrigger={1}, bRightTrigger={2}, sThumbLX={3}, sThumbLY={4}, sThumbRX={5}, sThumbRY={6}",
- wButtons,
- bLeftTrigger,
- bRightTrigger,
- sThumbLX,
- sThumbLY,
- sThumbRX,
- sThumbRY)
- End Function
- End Structure
- End Namespace
Zu beachten:
- Es ist vor der Benützung noch ein Verweis auf "WindowsBase" unter
Assambly
->Framework
zu setzen, da der TypVector
von dort bezogen wird. - Wird ein anderes System als
Windows Vista
,Windows 7
,Windows 8
,Windows 8.1
verwendet, wird der Code nicht funktionieren, da nur diese die erforderlichen XInput-Bibliotheken haben. Code wurde auf Windows 8.1 getestet. - Wird ein tieferes Framework angesprochen als
.NET Framework 4.5
so ergeben sich ein paar Komplikationen. Für.NET Framework 4.0
ist die asynchroneStartVibration
-Funktion mit Threads zu modifizieren. Bei tieferen Frameworks ist die Methode zu löschen.
Weiteres:
Ich bin für alle Korrekturen und Verbesserungsvorschläge offen und hoffe auf Unterstützung seitens der erfahreneren Mitglieder. Ich habe noch nicht viel Erfahrung mit
NativeWindow
-Typen und scheine da etwas verbockt zu haben, denn nach dem ersten Aufruf von StopCapturing
lässt sich kein erneutes Capturing iniziieren.Eine ältere Version wird in der ControllerDLL von @xd-franky-5 verwendet.
Beispielanwendung:
XBoxController.rar
Vorschau:
Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Higlav“ ()