Xbox-Controller auslesen

    • VB.NET
    • .NET (FX) 4.5–4.8

    Es gibt 27 Antworten in diesem Thema. Der letzte Beitrag () ist von Parmaster.

      Xbox-Controller auslesen

      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 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:
      Spoiler anzeigen

      Quellcode

      1. Imports System.Runtime.InteropServices
      2. Imports System.Windows
      3. 'Hierfür ein Verweis auf "WindowsBase" aus dem Framework hinzufügen.
      4. Namespace Controller
      5. ''' <summary>
      6. ''' Eine Klasse, die dazu dient, Eingaben von Xbox-Controllern auszulesen.
      7. ''' </summary>
      8. ''' <remarks></remarks>
      9. Public Class XboxController
      10. Inherits NativeWindow
      11. #Region "Native"
      12. <DllImport("winmm", CallingConvention:=CallingConvention.Winapi, EntryPoint:="joySetCapture", SetLastError:=True)> _
      13. 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
      14. End Function
      15. <DllImport("winmm", CallingConvention:=CallingConvention.Winapi, EntryPoint:="joyReleaseCapture", SetLastError:=True)> _
      16. Private Shared Function JoyReleaseCapture(ByVal uJoyID As UInteger) As JoyError
      17. End Function
      18. <DllImport("XInput1_4", EntryPoint:="XInputGetState", setlasterror:=True)> _
      19. Private Shared Function XInputGetState_1_4(ByVal dwUserIndex As UInteger, ByRef pState As XINPUT_STATE) As UInteger
      20. End Function
      21. <DllImport("XInput9_1_0", EntryPoint:="XInputGetState", setlasterror:=True)> _
      22. Private Shared Function XInputGetState_9_1_0(ByVal dwUserIndex As UInteger, ByRef pState As XINPUT_STATE) As UInteger
      23. End Function
      24. <DllImport("XInput1_4", EntryPoint:="XInputSetState")> _
      25. Private Shared Function XInputSetState_1_4(ByVal dwUserIndex As UInteger, ByRef pVibration As XINPUT_VIBRATION) As UInteger
      26. End Function
      27. <DllImport("XInput9_1_0", EntryPoint:="XInputSetState", setlasterror:=True)> _
      28. Private Shared Function XInputSetState_9_1_0(ByVal dwUserIndex As UInteger, ByRef pVibration As XINPUT_VIBRATION) As UInteger
      29. End Function
      30. #End Region
      31. #Region "Variables"
      32. Private _Controller As UInteger
      33. Private _Win8 As Boolean
      34. Private _Initialized As Boolean
      35. Private _LSDead As UShort = 7849
      36. Private _RSDead As UShort = 8689
      37. Private _TDead As Byte = 30
      38. Private _StickMaxRadius As UShort = Short.MaxValue
      39. Private _TrimStickLength As Boolean = False
      40. Private _LastState As XINPUT_GAMEPAD
      41. #End Region
      42. #Region "Properties"
      43. ''' <summary>
      44. ''' Gibt die Rangnummer des angeschlossenen Controllers an.
      45. ''' </summary>
      46. ''' <returns>Einen Wert von 0 bis 3</returns>
      47. Public ReadOnly Property ControllerNumber As UInteger
      48. Get
      49. Return _Controller
      50. End Get
      51. End Property
      52. ''' <summary>
      53. ''' Gibt den Radius der Deadzone für den linken Stick an, oder legt diese fest.
      54. ''' </summary>
      55. ''' <returns>Den Radius der Deadzone für den linken Stick zwischen 0 und <seealso cref="Short.MaxValue"/></returns>
      56. Public Property LeftStickDeadzoneRadius As UShort
      57. Get
      58. Return _LSDead
      59. End Get
      60. Set(value As UShort)
      61. _LSDead = value
      62. End Set
      63. End Property
      64. ''' <summary>
      65. ''' Gibt den Radius der Deadzone für den rechten Stick an, oder legt diese fest.
      66. ''' </summary>
      67. ''' <returns>Den Radius der Deadzone für den rechten Stick zwischen 0 und <seealso cref="Short.MaxValue"/></returns>
      68. Public Property RightStickDeadzoneRadius As UShort
      69. Get
      70. Return _RSDead
      71. End Get
      72. Set(value As UShort)
      73. _RSDead = value
      74. End Set
      75. End Property
      76. ''' <summary>
      77. ''' Gibt den Wert für die Deadzone der Trigger an, oder legt diese fest.
      78. ''' </summary>
      79. ''' <returns>Einen Wert zwischen 0 und <seealso cref="Byte.MaxValue"/></returns>
      80. Public Property TriggerDeadzone As Byte
      81. Get
      82. Return _TDead
      83. End Get
      84. Set(value As Byte)
      85. _TDead = value
      86. End Set
      87. End Property
      88. ''' <summary>
      89. ''' Gibt den Radius für das Triggern des Maximalwertes bei den Sticks an, oder legt diesen fest.
      90. ''' </summary>
      91. ''' <returns>Einen Wert zwischen 0 und <seealso cref="Short.MaxValue"/></returns>
      92. ''' <remarks>Ist redundant, wenn <see cref="TrimStickLength"/> = True ist.</remarks>
      93. Public Property StickMaximumRadius As UShort
      94. Get
      95. Return _StickMaxRadius
      96. End Get
      97. Set(value As UShort)
      98. _StickMaxRadius = value
      99. End Set
      100. End Property
      101. ''' <summary>
      102. ''' 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.
      103. ''' </summary>
      104. Public Property TrimStickLength As Boolean
      105. Get
      106. Return _TrimStickLength
      107. End Get
      108. Set(value As Boolean)
      109. _TrimStickLength = value
      110. End Set
      111. End Property
      112. ''' <summary>
      113. ''' Gibt die aus dem letzen Poll gedrückten Buttons zurück.
      114. ''' </summary>
      115. ''' <returns>Gibt einen <seealso cref="Short"/>-Wert zurück, der als Falg-Enumeration die Informationen über die gedrückten Buttons enthält.</returns>
      116. Public ReadOnly Property PressedButtons As Buttons
      117. Get
      118. Return CType(_LastState.wButtons, Buttons)
      119. End Get
      120. End Property
      121. ''' <summary>
      122. ''' Gibt den Vektor des linken Sticks aus dem letzten Poll zurück.
      123. ''' </summary>
      124. ''' <returns>Vektor des Typs <see cref="Vector"/></returns>
      125. Public ReadOnly Property LeftStickLocation As Vector
      126. Get
      127. Return New Vector(_LastState.sThumbLX, _LastState.sThumbLY)
      128. End Get
      129. End Property
      130. ''' <summary>
      131. ''' Gibt die Richtung an, in die der linke Stick aus dem letzten Poll zeigt.
      132. ''' </summary>
      133. ''' <returns>Gibt einen <seealso cref="Byte"/>-Wert zurück, der als Flag-Enumeration die Informationen über die Richtungen des Sticks beinhaltet.</returns>
      134. Public ReadOnly Property LeftStickDirection As StickDirection
      135. Get
      136. Return StickChangedEventArgs.GetDirection(LeftStickLocation)
      137. End Get
      138. End Property
      139. ''' <summary>
      140. ''' Gibt den Vektor des rechten Sticks aus dem letzten Poll zurück.
      141. ''' </summary>
      142. ''' <returns>Vektor des Typs <see cref="Vector"/></returns>
      143. Public ReadOnly Property RightStickLocation As Vector
      144. Get
      145. Return New Vector(_LastState.sThumbRX, _LastState.sThumbRY)
      146. End Get
      147. End Property
      148. ''' <summary>
      149. ''' Gibt die Richtung an, in die der rechte Stick aus dem letzten Poll zeigt.
      150. ''' </summary>
      151. ''' <returns>Gibt einen <seealso cref="Byte"/>-Wert zurück, der als Flag-Enumeration die Informationen über die Richtungen des Sticks beinhaltet.</returns>
      152. Public ReadOnly Property RightStickDirection As StickDirection
      153. Get
      154. Return StickChangedEventArgs.GetDirection(RightStickLocation)
      155. End Get
      156. End Property
      157. ''' <summary>
      158. ''' Gibt den Wert aus dem letzten Poll für den linken Trigger zurück.
      159. ''' </summary>
      160. ''' <returns>Einen Wert zwischen der Grenze der <see cref="TriggerDeadzone">Deadzone</see> und <seealso cref="Byte.MaxValue"/> zurück.</returns>
      161. Public ReadOnly Property LeftTriggerValue As Byte
      162. Get
      163. Return _LastState.bLeftTrigger
      164. End Get
      165. End Property
      166. ''' <summary>
      167. ''' Gibt den Wert aus dem letzten Poll für den rechten Trigger zurück.
      168. ''' </summary>
      169. ''' <returns>Einen Wert zwischen der Grenze der <see cref="TriggerDeadzone">Deadzone</see> und <seealso cref="Byte.MaxValue"/> zurück.</returns>
      170. Public ReadOnly Property RightTriggerValue As Byte
      171. Get
      172. Return _LastState.bRightTrigger
      173. End Get
      174. End Property
      175. #End Region
      176. #Region "Events"
      177. ''' <summary>
      178. ''' Tritt ein, wenn ein Button seit dem letzten Poll gedrückt wurde.
      179. ''' </summary>
      180. Public Event ButtonPressed(sender As Object, e As ButtonChangeEventArgs)
      181. ''' <summary>
      182. ''' Tritt ein, wenn ein Button seit dem letzten Poll losgelassen wurde.
      183. ''' </summary>
      184. Public Event ButtonReleased(sender As Object, e As ButtonChangeEventArgs)
      185. ''' <summary>
      186. ''' Tritt ein, wenn der linke Stick seit dem letzten Poll eine Änderung erfuhr.
      187. ''' </summary>
      188. ''' <remarks>Tritt in jedem Fall ein.</remarks>
      189. Public Event LeftStickChanged(sender As Object, e As StickChangedEventArgs)
      190. ''' <summary>
      191. ''' Tritt ein, wenn der linke Stick seit dem letzten Poll aus der Deadzone ausbrach.
      192. ''' </summary>
      193. Public Event LeftStickStarted(sender As Object, e As StickChangedEventArgs)
      194. ''' <summary>
      195. ''' Tritt ein, wenn der linke Stick seit dem letzten Poll den Maximalwert erreichte, oder immernoch diesem entspricht.
      196. ''' </summary>
      197. Public Event LeftStickReachedMaximum(sender As Object, e As StickChangedEventArgs)
      198. ''' <summary>
      199. ''' Tritt ein, wenn der linke Stick seit dem letzten Poll in die Deadzone fiel.
      200. ''' </summary>
      201. Public Event LeftStickStopped(sender As Object, e As EventArgs)
      202. ''' <summary>
      203. ''' Tritt ein, wenn der rechte Stick seit dem letzten Poll eine Änderung erfuhr.
      204. ''' </summary>
      205. ''' <remarks>Tritt in jedem Fall ein.</remarks>
      206. Public Event RightStickChanged(sender As Object, e As StickChangedEventArgs)
      207. ''' <summary>
      208. ''' Tritt ein, wenn der rechte Stick seit dem letzten Poll aus der Deadzone ausbrach.
      209. ''' </summary>
      210. Public Event RightStickStarted(sender As Object, e As StickChangedEventArgs)
      211. ''' <summary>
      212. ''' Tritt ein, wenn der rechte Stick seit dem letzten Poll den Maximalwert erreichte, oder immernoch diesem entspricht.
      213. ''' </summary>
      214. Public Event RightStickReachedMaximum(sender As Object, e As StickChangedEventArgs)
      215. ''' <summary>
      216. ''' Tritt ein, wenn der rechte Stick seit dem letzten Poll in die Deadzone fiel.
      217. ''' </summary>
      218. Public Event RightStickStopped(sender As Object, e As EventArgs)
      219. ''' <summary>
      220. ''' Tritt ein, wenn der linke Trigger seit dem letzten Poll eine Änderung erfuhr.
      221. ''' </summary>
      222. ''' <remarks>Tritt in jedem Fall ein.</remarks>
      223. Public Event LeftTriggerChanged(sender As Object, e As TriggerChangedEventArgs)
      224. ''' <summary>
      225. ''' Tritt ein, wenn der linke Trigger seit dem letzten Poll aus der Deadzone ausbrach.
      226. ''' </summary>
      227. Public Event LeftTriggerStarted(sender As Object, e As EventArgs)
      228. ''' <summary>
      229. ''' Tritt ein, wenn der linke Trigger seit dem letzten Poll den Maximalwert erreichte, oder immernoch diesem entspricht.
      230. ''' </summary>
      231. Public Event LeftTriggerReachedMaximum(sender As Object, e As EventArgs)
      232. ''' <summary>
      233. ''' Tritt ein, wenn der linke Trigger seit dem letzten Poll in die Deadzone fiel.
      234. ''' </summary>
      235. Public Event LeftTriggerStopped(sender As Object, e As EventArgs)
      236. ''' <summary>
      237. ''' Tritt ein, wenn der rechte Trigger seit dem letzten Poll eine Änderung erfuhr.
      238. ''' </summary>
      239. ''' <remarks>Tritt in jedem Fall ein.</remarks>
      240. Public Event RightTriggerChanged(sender As Object, e As TriggerChangedEventArgs)
      241. ''' <summary>
      242. ''' Tritt ein, wenn der rechte Trigger seit dem letzten Poll aus der Deadzone ausbrach.
      243. ''' </summary>
      244. Public Event RightTriggerStarted(sender As Object, e As EventArgs)
      245. ''' <summary>
      246. ''' Tritt ein, wenn der rechte Trigger seit dem letzten Poll den Maximalwert erreichte, oder immernoch diesem entspricht.
      247. ''' </summary>
      248. Public Event RightTriggerReachedMaximum(sender As Object, e As EventArgs)
      249. ''' <summary>
      250. ''' Tritt ein, wenn der rechte Trigger seit dem letzten Poll in die Deadzone fiel.
      251. ''' </summary>
      252. Public Event RightTriggerStopped(sender As Object, e As EventArgs)
      253. #End Region
      254. #Region "Preset"
      255. Private Sub OnHandleCreated(ByVal sender As Object, ByVal e As EventArgs)
      256. AssignHandle(DirectCast(sender, Form).Handle)
      257. End Sub
      258. Private Sub OnHandleDestroyed(ByVal sender As Object, ByVal e As EventArgs)
      259. ReleaseHandle()
      260. End Sub
      261. #End Region
      262. ''' <summary>
      263. ''' Initialisiert die Klasse und bereitet das Capturing vor.
      264. ''' </summary>
      265. ''' <param name="parent">Eine Form, über deren Handle das Capturing erfolgt.</param>
      266. ''' <param name="controllerNumber">Rangnummer des angeschlossenen Xbox-Controllers. Muss einschliesslich zwischen 0 und 3 liegen.</param>
      267. Public Sub New(parent As Form, controllerNumber As UInteger)
      268. If controllerNumber > 3 Then Throw New ArgumentOutOfRangeException("controllerNumber", "variable must be an element out of 0, 1, 2, 3.")
      269. _Controller = controllerNumber
      270. With Environment.OSVersion.Version
      271. If .Major <> 6 Then Throw New Exception("The system does not match the requirements.")
      272. _Win8 = .Minor > 1
      273. End With
      274. AddHandler parent.HandleCreated, AddressOf Me.OnHandleCreated
      275. AddHandler parent.HandleDestroyed, AddressOf Me.OnHandleDestroyed
      276. AssignHandle(parent.Handle)
      277. End Sub
      278. ''' <summary>
      279. ''' Startet das Capturing.
      280. ''' </summary>
      281. ''' <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>
      282. ''' <returns>True, wenn das Einhängen geklappt hat, andernfalls False.</returns>
      283. Public Function StartCapturing(onlySendMsgWhenChanged As Boolean) As Boolean
      284. If _Initialized Then Return False
      285. Select Case JoySetCapture(Me.Handle, _Controller, 1, onlySendMsgWhenChanged)
      286. Case JoyError.JOYERR_NOERROR
      287. _Initialized = True
      288. Case Else
      289. 'Console.WriteLine(Result.ToString)
      290. End Select
      291. Return _Initialized
      292. End Function
      293. ''' <summary>
      294. ''' Stoppt das Capturing
      295. ''' </summary>
      296. ''' <returns>True, wenn das Lösen erfolgreich war, andernfalls False.</returns>
      297. Public Function StopCapturing() As Boolean
      298. If Not _Initialized Then Return False
      299. Select Case JoyReleaseCapture(_Controller)
      300. Case JoyError.JOYERR_NOERROR
      301. _Initialized = False
      302. Case Else
      303. 'Console.WriteLine(Result.ToString)
      304. End Select
      305. Me.ReleaseHandle()
      306. Return Not _Initialized
      307. End Function
      308. ''' <summary>
      309. ''' Sendet Vibration an den Controller.
      310. ''' </summary>
      311. ''' <param name="leftMotor">Wert für die Vibrationsintensität des linken Motors. Wert muss einschliesslich zwischen 0 und <seealso cref="UShort.MaxValue"/> liegen.</param>
      312. ''' <param name="rightMotor">Wert für die Vibrationsintensität des rechten Motors. Wert muss einschliesslich zwischen 0 und <seealso cref="UShort.MaxValue"/> liegen.</param>
      313. ''' <returns>True, wenn das Senden erfolgreich war, andernfalls False.</returns>
      314. Public Function Vibrate(leftMotor As UShort, rightMotor As UShort) As Boolean
      315. Dim Vibration As New XINPUT_VIBRATION(leftMotor, rightMotor)
      316. If _Win8 Then
      317. Return XInputSetState_1_4(0, Vibration) = 0
      318. Else
      319. Return XInputSetState_9_1_0(0, Vibration) = 0
      320. End If
      321. End Function
      322. ''' <summary>
      323. ''' Sendet Vibration asynchron an den Controller.
      324. ''' </summary>
      325. ''' <param name="leftMotor">Wert für die Vibrationsintensität des linken Motors. Wert muss einschliesslich zwischen 0 und <seealso cref="UShort.MaxValue"/> liegen.</param>
      326. ''' <param name="rightMotor">Wert für die Vibrationsintensität des rechten Motors. Wert muss einschliesslich zwischen 0 und <seealso cref="UShort.MaxValue"/> liegen.</param>
      327. ''' <param name="length">Wert für die Dauer, der Vibration.</param>
      328. ''' <returns>True, wenn das Senden erfolgreich war, andernfalls False.</returns>
      329. Public Async Function StartVibration(leftMotor As UShort, rightMotor As UShort, length As TimeSpan) As Task(Of Boolean)
      330. Dim Success1 = Vibrate(leftMotor, rightMotor)
      331. Await Task.Delay(length)
      332. Dim Success2 = Vibrate(0, 0)
      333. Return Success1 And Success2
      334. End Function
      335. Protected Overrides Sub WndProc(ByRef m As Message)
      336. Select Case CType(m.Msg, MMRESULT)
      337. Case Is = MMRESULT.MM_JOY1MOVE, MMRESULT.MM_JOY1ZMOVE, MMRESULT.MM_JOY1BUTTONDOWN, MMRESULT.MM_JOY1BUTTONUP
      338. Poll()
      339. Case Else
      340. 'do nothing
      341. End Select
      342. MyBase.WndProc(m)
      343. End Sub
      344. ''' <summary>
      345. ''' Holt die aktuellsten Daten zum Xbox-Controller.
      346. ''' </summary>
      347. ''' <remarks>Wird beim Capturing automatisch aufgerufen.</remarks>
      348. Public Sub Poll()
      349. Dim State As New XINPUT_STATE(_Controller, New XINPUT_GAMEPAD)
      350. Dim Result As UInteger
      351. If _Win8 Then
      352. Result = XInputGetState_1_4(0, State)
      353. Else
      354. Result = XInputGetState_9_1_0(0, State)
      355. End If
      356. If Result <> 0 Then Throw New Exception("Could not get states")
      357. With State.Gamepad
      358. 'Buttons:
      359. If Not _LastState.wButtons = .wButtons Then
      360. Dim OldBtns = _LastState.wButtons
      361. Dim NewBtns = .wButtons
      362. 'Elaborating Changes by using bitwise operations:
      363. Dim ChangedBtns = (_LastState.wButtons Or NewBtns)
      364. Dim NewPressed = ChangedBtns - OldBtns
      365. Dim NewReleased = ChangedBtns - NewBtns
      366. If Not NewPressed = 0 Then
      367. RaiseEvent ButtonPressed(Me, New ButtonChangeEventArgs(CType(NewPressed, Buttons)))
      368. End If
      369. If Not NewReleased = 0 Then
      370. RaiseEvent ButtonReleased(Me, New ButtonChangeEventArgs(CType(NewReleased, Buttons)))
      371. End If
      372. End If
      373. 'LeftStick:
      374. If .sThumbLX <> _LastState.sThumbLX OrElse .sThumbLY <> _LastState.sThumbLY Then
      375. Dim LS As New Vector(.sThumbLX, .sThumbLY)
      376. Dim LPressed = CType(.wButtons, Buttons).HasFlag(Buttons.LEFT_STICK)
      377. If LS.Length < _LSDead Then
      378. .sThumbLX = 0
      379. .sThumbLY = 0
      380. If _LastState.sThumbLX <> 0 AndAlso _LastState.sThumbLY <> 0 Then
      381. 'Stop Moving
      382. RaiseEvent LeftStickChanged(Me, New StickChangedEventArgs(New Vector(0, 0), LPressed))
      383. RaiseEvent LeftStickStopped(Me, EventArgs.Empty)
      384. End If
      385. Else
      386. If _LastState.sThumbLX = 0 AndAlso _LastState.sThumbLY = 0 Then
      387. 'Start Moving
      388. RaiseEvent LeftStickChanged(Me, New StickChangedEventArgs(LS, LPressed))
      389. RaiseEvent LeftStickStarted(Me, New StickChangedEventArgs(LS, LPressed))
      390. Else
      391. If _TrimStickLength Then
      392. If LS.Length > Short.MaxValue Then
      393. LS.Normalize()
      394. LS *= Short.MaxValue
      395. End If
      396. If LS.Length >= Short.MaxValue Then
      397. 'Maximum reached
      398. RaiseEvent LeftStickChanged(Me, New StickChangedEventArgs(LS, LPressed))
      399. RaiseEvent LeftStickReachedMaximum(Me, New StickChangedEventArgs(LS, LPressed))
      400. Else
      401. 'Is Moving
      402. RaiseEvent LeftStickChanged(Me, New StickChangedEventArgs(LS, LPressed))
      403. End If
      404. ElseIf .sThumbLX = Short.MaxValue OrElse
      405. .sThumbLX = Short.MinValue OrElse
      406. .sThumbLY = Short.MaxValue OrElse
      407. .sThumbLY = Short.MinValue OrElse
      408. LS.Length >= _StickMaxRadius Then
      409. 'Maximum reached
      410. RaiseEvent LeftStickChanged(Me, New StickChangedEventArgs(LS, LPressed))
      411. RaiseEvent LeftStickReachedMaximum(Me, New StickChangedEventArgs(LS, LPressed))
      412. Else
      413. 'Is Moving
      414. RaiseEvent LeftStickChanged(Me, New StickChangedEventArgs(LS, LPressed))
      415. End If
      416. End If
      417. End If
      418. End If
      419. 'RightStick:
      420. If .sThumbRX <> _LastState.sThumbRX OrElse .sThumbRY <> _LastState.sThumbRY Then
      421. Dim RS As New Vector(.sThumbRX, .sThumbRY)
      422. Dim RPressed = CType(.wButtons, Buttons).HasFlag(Buttons.RIGHT_STICK)
      423. If RS.Length < _LSDead Then
      424. .sThumbRX = 0
      425. .sThumbRY = 0
      426. If _LastState.sThumbRX <> 0 AndAlso _LastState.sThumbRY <> 0 Then
      427. 'Stop Moving
      428. RaiseEvent RightStickChanged(Me, New StickChangedEventArgs(New Vector(0, 0), RPressed))
      429. RaiseEvent RightStickStopped(Me, EventArgs.Empty)
      430. End If
      431. Else
      432. If _LastState.sThumbRX = 0 AndAlso _LastState.sThumbRY = 0 Then
      433. 'Start Moving
      434. RaiseEvent RightStickChanged(Me, New StickChangedEventArgs(RS, RPressed))
      435. RaiseEvent RightStickStarted(Me, New StickChangedEventArgs(RS, RPressed))
      436. Else
      437. If _TrimStickLength Then
      438. If RS.Length > Short.MaxValue Then
      439. RS.Normalize()
      440. RS *= Short.MaxValue
      441. End If
      442. If RS.Length >= Short.MaxValue Then
      443. 'Maximum reached
      444. RaiseEvent RightStickChanged(Me, New StickChangedEventArgs(RS, RPressed))
      445. RaiseEvent RightStickReachedMaximum(Me, New StickChangedEventArgs(RS, RPressed))
      446. Else
      447. 'Is Moving
      448. RaiseEvent RightStickChanged(Me, New StickChangedEventArgs(RS, RPressed))
      449. End If
      450. ElseIf .sThumbRX = Short.MaxValue OrElse
      451. .sThumbRX = Short.MinValue OrElse
      452. .sThumbRY = Short.MaxValue OrElse
      453. .sThumbRY = Short.MinValue OrElse
      454. RS.Length >= _StickMaxRadius Then
      455. 'Maximum reached
      456. RaiseEvent RightStickChanged(Me, New StickChangedEventArgs(RS, RPressed))
      457. RaiseEvent RightStickReachedMaximum(Me, New StickChangedEventArgs(RS, RPressed))
      458. Else
      459. 'Is Moving
      460. RaiseEvent RightStickChanged(Me, New StickChangedEventArgs(RS, RPressed))
      461. End If
      462. End If
      463. End If
      464. End If
      465. 'LT:
      466. Dim LT = .bLeftTrigger
      467. If LT <> _LastState.bLeftTrigger Then
      468. If LT < _TDead Then
      469. .bLeftTrigger = 0
      470. If _LastState.bLeftTrigger <> 0 Then
      471. 'Stop Moving
      472. RaiseEvent LeftTriggerChanged(Me, New TriggerChangedEventArgs(0))
      473. RaiseEvent LeftTriggerStopped(Me, EventArgs.Empty)
      474. End If
      475. Else
      476. If _LastState.bLeftTrigger = 0 Then
      477. 'Start Moving
      478. RaiseEvent LeftTriggerChanged(Me, New TriggerChangedEventArgs(LT))
      479. RaiseEvent LeftTriggerStarted(Me, EventArgs.Empty)
      480. Else
      481. If LT = Byte.MaxValue Then
      482. 'Maximum reached
      483. RaiseEvent LeftTriggerChanged(Me, New TriggerChangedEventArgs(LT))
      484. RaiseEvent LeftTriggerReachedMaximum(Me, EventArgs.Empty)
      485. Else
      486. 'Is Moving
      487. RaiseEvent LeftTriggerChanged(Me, New TriggerChangedEventArgs(LT))
      488. End If
      489. End If
      490. End If
      491. End If
      492. 'RT:
      493. Dim RT = .bRightTrigger
      494. If RT <> _LastState.bRightTrigger Then
      495. If RT < _TDead Then
      496. .bRightTrigger = 0
      497. If _LastState.bRightTrigger <> 0 Then
      498. 'Stop Moving
      499. RaiseEvent RightTriggerChanged(Me, New TriggerChangedEventArgs(0))
      500. RaiseEvent RightTriggerStopped(Me, EventArgs.Empty)
      501. End If
      502. Else
      503. If _LastState.bRightTrigger = 0 Then
      504. 'Start Moving
      505. RaiseEvent RightTriggerChanged(Me, New TriggerChangedEventArgs(RT))
      506. RaiseEvent RightTriggerStarted(Me, EventArgs.Empty)
      507. Else
      508. If RT = Byte.MaxValue Then
      509. 'Maximum reached
      510. RaiseEvent RightTriggerChanged(Me, New TriggerChangedEventArgs(RT))
      511. RaiseEvent RightTriggerReachedMaximum(Me, EventArgs.Empty)
      512. Else
      513. 'Is Moving
      514. RaiseEvent RightTriggerChanged(Me, New TriggerChangedEventArgs(RT))
      515. End If
      516. End If
      517. End If
      518. End If
      519. End With
      520. _LastState = State.Gamepad
      521. End Sub
      522. End Class
      523. ''' <summary>
      524. ''' Stellt Daten für das <see cref="XboxController.ButtonPressed"/>- und das <see cref="XboxController.ButtonReleased"/>-Event bereit.
      525. ''' </summary>
      526. Public NotInheritable Class ButtonChangeEventArgs
      527. Inherits EventArgs
      528. Private _Buttons As Buttons
      529. Friend Sub New(newPressed As Buttons)
      530. _Buttons = newPressed
      531. End Sub
      532. ''' <summary>
      533. ''' Gibt die Buttons zurück, die eine Änderung erfuhren.
      534. ''' </summary>
      535. Public ReadOnly Property ChangedButtons As Buttons
      536. Get
      537. Return _Buttons
      538. End Get
      539. End Property
      540. End Class
      541. ''' <summary>
      542. ''' Stellt Daten für das <see cref="XboxController.LeftStickChanged"/>-, das <see cref="XboxController.RightStickChanged"/>-,
      543. ''' das <see cref="XboxController.LeftStickReachedMaximum"/>-, das <see cref="XboxController.RightStickReachedMaximum"/>-,
      544. ''' das <see cref="XboxController.LeftStickStarted"/>- und das <see cref="XboxController.RightStickStarted"/>-Event bereit.
      545. ''' </summary>
      546. Public NotInheritable Class StickChangedEventArgs
      547. Inherits EventArgs
      548. Private _Vec As Vector
      549. Private _Pressed As Boolean
      550. Friend Sub New(vec As Vector, pressed As Boolean)
      551. _Vec = vec
      552. _Pressed = pressed
      553. End Sub
      554. ''' <summary>
      555. ''' Gibt den Vektor zurück, die die Verschiebung eines Sticks beschreibt.
      556. ''' </summary>
      557. ''' <returns>Einen Vektor des Typs <see cref="Windows.Vector"/></returns>
      558. Public ReadOnly Property Vector As Windows.Vector
      559. Get
      560. Return _Vec
      561. End Get
      562. End Property
      563. ''' <summary>
      564. ''' Gibt die Stärke an, mit der der Stick zum Maximum gedrückt wurde.
      565. ''' </summary>
      566. Public ReadOnly Property Value As Double
      567. Get
      568. Return _Vec.Length
      569. End Get
      570. End Property
      571. ''' <summary>
      572. ''' Gibt die Richtungen zurück, in die der Stick gedrückt wurde.
      573. ''' </summary>
      574. Public ReadOnly Property Direction As StickDirection
      575. Get
      576. Return GetDirection(_Vec)
      577. End Get
      578. End Property
      579. ''' <summary>
      580. ''' Gibt an, ob der Stick gedrückt wurde, oder nicht.
      581. ''' </summary>
      582. Public ReadOnly Property IsPressed As Boolean
      583. Get
      584. Return _Pressed
      585. End Get
      586. End Property
      587. Friend Shared Function GetDirection(vec As Vector) As StickDirection
      588. If vec.Length <= Double.Epsilon Then Return 0
      589. Dim Angle = Math.Atan2(vec.Y, vec.X)
      590. Select Case Angle
      591. Case Is > Math.Atan2(1, -2), Is < Math.Atan2(-1, -2)
      592. Return StickDirection.Left
      593. Case Is > Math.Atan2(2, -1)
      594. Return StickDirection.Left Or StickDirection.Up
      595. Case Is < Math.Atan2(-2, -1)
      596. Return StickDirection.Left Or StickDirection.Down
      597. Case Is > Math.Atan2(2, 1)
      598. Return StickDirection.Up
      599. Case Is < Math.Atan2(-2, 1)
      600. Return StickDirection.Down
      601. Case Is > Math.Atan2(1, 2)
      602. Return StickDirection.Up Or StickDirection.Right
      603. Case Is < Math.Atan2(-1, 2)
      604. Return StickDirection.Down Or StickDirection.Right
      605. Case Else
      606. Return StickDirection.Right
      607. End Select
      608. End Function
      609. End Class
      610. ''' <summary>
      611. ''' Stellt Daten für das <see cref="XboxController.LeftTriggerChanged"/>-, das <see cref="XboxController.RightTriggerChanged"/>-,
      612. ''' das <see cref="XboxController.LeftTriggerStarted"/>- und das <see cref="XboxController.RightTriggerStarted"/>-Event bereit.
      613. ''' </summary>
      614. Public NotInheritable Class TriggerChangedEventArgs
      615. Inherits EventArgs
      616. Private _Val As Byte
      617. Friend Sub New(value As Byte)
      618. _Val = value
      619. End Sub
      620. ''' <summary>
      621. ''' Gibt den Wert des Triggers zurück.
      622. ''' </summary>
      623. ''' <returns>Gibt einen Wert zurück, der sich zwischen <see cref="XboxController.TriggerDeadzone"/> und dem <see cref="Byte.MaxValue"/> liegt.</returns>
      624. Public ReadOnly Property Value As Byte
      625. Get
      626. Return _Val
      627. End Get
      628. End Property
      629. End Class
      630. ''' <summary>
      631. ''' Bezeichnet die Richtungen, in die der jeweilige Stick gedrückt wurde an.
      632. ''' </summary>
      633. <Flags()> _
      634. Public Enum StickDirection As Byte
      635. Left = 1
      636. Right = 2
      637. Up = 4
      638. Down = 8
      639. End Enum
      640. ''' <summary>
      641. ''' Bezeichnet die Buttons, die eine Änderung erfuhren.
      642. ''' </summary>
      643. <Flags()> _
      644. Public Enum Buttons As Short
      645. DPAD_UP = &H1
      646. DPAD_DOWN = &H2
      647. DPAD_LEFT = &H4
      648. DPAD_RIGHT = &H8
      649. START = &H10
      650. BACK = &H20
      651. LEFT_STICK = &H40
      652. RIGHT_STICK = &H80
      653. LEFT_BUTTON = &H100
      654. RIGHT_BUTTON = &H200
      655. A = &H1000
      656. B = &H2000
      657. X = &H4000
      658. Y = -&H8000
      659. End Enum
      660. Friend Enum MMRESULT As UInteger
      661. MM_JOY1MOVE = &H3A0
      662. MM_JOY2MOVE = &H3A1
      663. MM_JOY1ZMOVE = &H3A2
      664. MM_JOY2ZMOVE = &H3A3
      665. MM_JOY1BUTTONDOWN = &H3B5
      666. MM_JOY2BUTTONDOWN = &H3B6
      667. MM_JOY1BUTTONUP = &H3B7
      668. MM_JOY2BUTTONUP = &H3B8
      669. End Enum
      670. Friend Enum JoyError As UInteger
      671. JOYERR_NOERROR = 0
      672. JOYERR_PARMS = 165
      673. JOYERR_NOCANDO = 166
      674. JOYERR_UNPLUGGED = 167
      675. End Enum
      676. <StructLayout(LayoutKind.Explicit)> _
      677. Friend Structure XINPUT_STATE
      678. <FieldOffset(0)> Public dwPacketNumber As UInteger
      679. <FieldOffset(4)> Public Gamepad As XINPUT_GAMEPAD
      680. Public Sub New(pNumber As UInteger, gPad As XINPUT_GAMEPAD)
      681. dwPacketNumber = pNumber
      682. Gamepad = gPad
      683. End Sub
      684. End Structure
      685. Friend Structure XINPUT_VIBRATION
      686. Public wLeftMotorSpeed As UShort
      687. Public wRightMotorSpeed As UShort
      688. Public Sub New(lMot As UShort, rMot As UShort)
      689. wLeftMotorSpeed = lMot
      690. wRightMotorSpeed = rMot
      691. End Sub
      692. End Structure
      693. <StructLayout(LayoutKind.Explicit)> _
      694. Friend Structure XINPUT_GAMEPAD
      695. <FieldOffset(0)> Public wButtons As Short
      696. <FieldOffset(2)> Public bLeftTrigger As Byte
      697. <FieldOffset(3)> Public bRightTrigger As Byte
      698. <FieldOffset(4)> Public sThumbLX As Short
      699. <FieldOffset(6)> Public sThumbLY As Short
      700. <FieldOffset(8)> Public sThumbRX As Short
      701. <FieldOffset(10)> Public sThumbRY As Short
      702. Public Overrides Function ToString() As String
      703. Return String.Format("wButtons={0}, bLeftTrigger={1}, bRightTrigger={2}, sThumbLX={3}, sThumbLY={4}, sThumbRX={5}, sThumbRY={6}",
      704. wButtons,
      705. bLeftTrigger,
      706. bRightTrigger,
      707. sThumbLX,
      708. sThumbLY,
      709. sThumbRX,
      710. sThumbRY)
      711. End Function
      712. End Structure
      713. End Namespace



      Zu beachten:
      • Es ist vor der Benützung noch ein Verweis auf "WindowsBase" unter Assambly -> Framework zu setzen, da der Typ Vector 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 asynchrone StartVibration-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“ ()

      @Higlav Cooler Code. Benötige ihn zwar derzeit nicht, kann aber durchaus mal der Fall sein.
      Eine Frage stellt sich mir trotzdem, funktioniert das auch mit den Controllern der XBOX 360 oder nur mit der aktuellen XBOX One.
      (Bei der "ersten" und "besten" BOX kann es ja nicht klappen, da die Controller keine USB Schnittstelle und/oder Bluetooth besitzen)

      v-go schrieb:

      Eine Frage stellt sich mir trotzdem, funktioniert das auch mit den Controllern der XBOX 360 oder nur mit der aktuellen XBOX One.
      (Bei der "ersten" und "besten" BOX kann es ja nicht klappen, da die Controller keine USB Schnittstelle und/oder Bluetooth besitzen)

      Hallo v-go.
      Der Code ist nur für die Xbox-Controller ausgelegt, die für Nutzung am PC ausgelegt sind.
      Gibts denn noch andere Methoden, die Daten von Gamepads auszulesen? Mein Billig-Gamepad vom Saturn kann ich in allen Spielen benutzen, XInput gibt aber ​1167 als Result -> funktioniert so wie ich das sehe net.
      @Parmaster WindowsBase ist der fehlende Verweis, so scheint mir. Den findest du unter Assambly -> Framework. Ich trag's gleich oben mal in den Code ein. Danke für den Hinweis. ^^

      Counterbug schrieb:

      Parmaster schrieb:


      welche Dll?


      Das .Net-Framework besteht nicht nur aus einer Datei, selbst hinter den .Net-Verweisen stecken dlls ;)


      Ja, schon klar, ich dachte du meintest eine extra Dll. Deshalb hatte ich noch mal nachgefragt ob mir da eine fehlt.

      Aber es war, wie Higlav sagte, das fehlende WindowsBase.


      @Higlav

      eine Frage noch:

      wie muss das genau aussehen, um z.B. abzufragen ob der linke Stick nach links oder rechts gedrückt wird?

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Parmaster“ ()

      Parmaster schrieb:

      wie muss das genau aussehen, um z.B. abzufragen ob der linke Stick nach links oder rechts gedrückt wird?

      Die Events Left/Right-Stick-Changed/Started/ReachedMaximum liefern alle ein Objekt vom Typ StickChangedEventArgs, das die Eigenschaft Direction beinhaltet. Es ist eine Instanz der Enumeration StickDirection, die dir sagt, in welche Richtung(en) der Stick gedrückt wurde. Da sich das überschneiden kann, habe ich es so eingebaut, dass auch zwei Werte(zB. Up und Left) mitgeliefert werden können, indem ich die Enum nach dem Flag-Prinzip aufgebaut habe. Um eine spezifische Richtung abzufragen, musst du beispielsweise e.Direction.HasFlag(StickDirection.Left) abfragen, anstatt direkt zu vergleichen.

      Grüsse,

      Higlav