Kapitel 3
DirectInput
3.1 Objekthierarchie 254
3.2 Cooperative Level 257
3.3 Datenformat 258
3.4 Beispiel: Tastatur 260
3.5 Beispiel: Joystick 262
3.6 Beispiel: Maus 268
3.7 Beispiel: DirectInput und Direct3D 272
3.8 Bewegung im 3D-Raum 279
Objekthierarchie 254
DirectInput (DI) ist eine API für Eingabegeräte. Durch die Bereitstellung allge- meiner Device Interfaces bietet DirectInput die Sicherheit, auch in der Zukunft auf eine Vielzahl von Geräten zugreifen zu können, deren technische Möglichkei- ten heute noch nicht definiert sind. DirectInput unterscheidet drei Typen von I/O- Geräten.
• Standard-System-Tastatur
• Maus oder mausähnliche Geräte (Maus, Trackball, Touchpad ...)
• Joysticks bzw alle übrigen Geräte (Joystick, Gamepad, Lenkräder...)
Die normale Windows API bietet eine Reihe von unterstüzenden Methoden zur Steuerung von I/O-Geräten, dennoch werden diese durch DirectInput ersetzt. Dies hat zum einen den Grund, dass mit DI eine freie (für die Zukunft offene) Schnitt- stelle geschaffen wurde, zum anderen wurde die Geschwindigkeit der bestehen- den Technik problematisch. Der Grundkonzeption von DirectX entsprechend, wurde auch bei den Eingabegeräten Wert auf hohe Performance gelegt. Natürlich finden wir auch bei DI einen direkten Zugriff auf die Hardware. Dabei wird das übliche Nachrichtensystem von Windows ignoriert. Dies garantiert quasi eine schnelle Reaktionszeit. Das Verhalten von DirectInput ist vom Programmierer konfigurierbar. Je nach Einsatzzweck wird er unterschiedliche Konfigurationen festlegen.
3.1 Objekthierarchie
• DirectInput Interface
Das DirectInput Interface stellt die erste Verbindung zum I/O Gerät her. Es dient zur Konfiguration und Initialisierung.
• Devices
Mit DirectInput Device werden die Ein- / Ausgabegeräte bezeichnet. Diese Objekte (Maus, Tastatur, Joystick...) bestehen aus verschiedenen Objekten.
• Objekte
Objekte sind die Bestandteile eines Devices. Objekte können Tasten, Knöpfe, Achsen, Drehräder ... sein.
• Effekte
Neben den Objekten existieren noch die Effekte. Effekte finden vor allem bei neueren Geräten Verwendung. Insbesondere sei der Force Feedback Joystick genannt.
Die verschiedenen Bestandteile der DirectInput Architekture erfassen im Regel- fall Daten. Dabei ist es ohne Bedeutung, wie die Daten erfasst wurden. Ob eine Maus die Koordinaten mechanisch per Kugel und Zahnrad oder optisch an Direc- tInput weiterleitet spielt keine Rolle. Betrachten wir die Architektur an einem Beispiel, so würden wir eine einfache Maus folgendermaßen skizzieren.
Device → Maus
Objekte → zwei Tasten
zwei Achsen (X- und Y-Achse)
Effekte → keine
Auflisten der verfügbaren Geräte und Objekte
Anhand eines Beispielprogramms werden wir demonstrieren, wie Sie Informatio- nen über die angeschlossene Inputhardware sammeln. Diese Informationen bilden die Grundlage für alle späteren Aktionen. Anders als bei Direct3D oder Direct- Draw können die Inputgeräte von DirectX nicht simuliert werden.
Unser Beispielprogramm erkennt alle eingetragenen I/O-Geräte. Die Geräte müs- sen nicht angeschlossen sein, es reicht wenn die Geräte von Windows als I/O- Geräte registriert wurden. So können Sie z.B. über die Systemsteuerung/Game- controller weitere Joysticks eintragen. Diese werden dann ebenfalls mit aufgelis- tet.
Beginnen wir mit den Dimensionierungen.
Dim dx As New DirectX7 Dim di As DirectInput
Dim diDEV As DirectInputDevice
Dim diEnumDev As DirectInputEnumDevices Dim diEnumOBJ As DirectInputEnumDeviceObjects
Um auf die Geräteliste sowie auf die Objektliste zugreifen zu können, benötigen wir die Variablen vom Typ DirectInputEnumDevices und DirectInputEnum DeviceObjects. Diese werden die gewünschten Informationen beinhalten.
Wir beginnen mit der Form_Load()-Subroutine.
Bild 3.1: Objekthierarchie
Objekthierarchie 256
Private Sub Form_Load()
Set di = dx.DirectInputCreate()
Zuerst benötigen wir das DirectInput-Objekt. Dieses erzeugen wir mit der DirectX7.DirectInputCreate() Methode. Diese Methode besitzt keine Parameter.
Set diEnumDev = di.GetDIEnumDevices(0 , DIEDFL_INCLUDEPHANTOMS)
Mit der DirectInput.GetDiEnumDevices() Methode weisen wir das DirectInput EnumDevices Objekt zu. Der erste Parameter ist vom Typ CONST_DIDEVICE TYPE, der zweite Parameter ist ein Flag aus der CONST_DIENUMDEVICES FLAGS Konstantenliste. Indem wir den ersten Parameter einfach auf 0 setzten, werden uns später alle Geräte (Maus, Tastatur, Joystick ...) aufgelistet werden.
Das Flag DIEDFL_INCLUDEPHANTOMS des zweiten Parameters sorgt dafür, dass auch die Geräte aufglistet werden, welche zurzeit nicht angeschlossen sind.
Bild 3.2: Geräteliste
DeviceCount = diEnumDev.GetCount() For i = 1 To DeviceCount
Set diDEV = _
di.CreateDevice(diEnumDev.GetItem(i).GetGuidInstance) Combo1.AddItem (diEnumDev.GetItem(i).GetProductName)
Der Reihe nach werden alle gefundenen Geräte mit der DirectInput.Create Device()-Methode erzeugt und die Namen der Geräte ausgelesen.
Next i
Combo1.Text = Combo1.List(0) Listen 1
End Sub
Mit der Subroutine Listen() aktualisieren wir die Listboxen auf Form1. Wie- derum zusätzlich zu dem Device erzeugen wir noch das DirectInput-Objekt. In diesem Objekt finden wir Informationen über das Gerät selber. Diese übertragen wir an die jeweiligen Listboxen.
Public Sub Listen(Index As Integer) List1.Clear
List2.Clear Set diDEV = _
di.CreateDevice(diEnumDev.GetItem(Index).GetGuidInstance) DeviceCount = diEnumDev.GetCount()
List1.AddItem "ProductName :" & _ (diEnumDev.GetItem(Index).GetProductName) List1.AddItem "InstanceName :" & _
(diEnumDev.GetItem(Index).GetInstanceName) List1.AddItem "GuidInstance :" & _
(diEnumDev.GetItem(Index).GetGuidInstance) List1.AddItem "GuidProduct :" & _
(diEnumDev.GetItem(Index).GetGuidProduct)
Set diEnumOBJ = diDEV.GetDeviceObjectsEnum(DIDFT_ALL) ObjectCount = diEnumOBJ.GetCount()
For u = 1 To ObjectCount
List2.AddItem (diEnumOBJ.GetItem(u).GetName) Next u
End Sub
3.2 Cooperative Level
Der Cooperative Level bestimmt, wie ein Gerät mit anderen Anwendungen oder dem Windowssystem geteilt wird. Für den Zugriff auf einzelnen I/O-Geräte ste- hen eine gewisse Zahl von Cooperative Levels zur Wahl. Die korrekte Festlegung des Cooperative Levels kann einige Probleme beim Umgang mit DirectX-Anwen- dungen in Windows vermeiden helfen.
Datenformat 258
Werden z.B. die I/O-Geräte nur abgefragt, wenn sich die Anwendung im Vorder- grund befindet, kann es passieren (falls die Anwendung den Fokus verliert), dass eine gestartete Aktion beim Loslassen einer Taste nicht beendet wird.
Die möglichen Cooperative Levels sind:
Die folgende Tabelle gibt an, welche Kombinationen für welche I/O-Geräte zuläs- sig sind.
Wenn eine Anwendung den exklusiven Zugriff auf die Tastatur erhält, unterdrückt DirectInput alle Tastaturnachrichten außer [Strg]+[Alt]+[Del] sowie [Alt)+ [ÿ].
3.3 Datenformat
Um gegenüber neuen Entwicklungen offen zu sein, bietet DirectInput eine sehr allgemeine Möglichkeit um auf die Hardware zuzugreifen. So existieren weitrei- chende Möglichkeiten zur Erstellung selbstdefinierter Datenformate. Für die nor- malen I/O-Geräte existieren bereits vordefinierte Datenformate. Diese können Sie der folgenden Tabelle entnehmen.
Cooperativ Level Kurzbeschreibung
DISCL_NONEXCLUSIVE Die Anwendung erhält den Zugriff auf das Gerät nicht exklusiv (zwingend für die Tastatur).
DISCL_EXCLUSIVE Andere Anwendungen können nicht exklusiv auf das Gerät zugreifen (zwingend für Force Feedback).
DISCL_FORDERGROUND Die Anwendung erhält nur Daten, wenn sie sich im Vor- dergrund befindet.
DISCL_BACKGROUND Die Anwendung erhält Daten im Vordergund und Hin- tergrund.
Cooperative Level Kurzbeschreibung
DISCL_NONEXCLUSIVE DISCL_BACKGROUND
alle Geräte ohne Force Feedback
→ Default DISCL_NONEXCLUSIVE
DISCL_FORDERGROUND
alle Geräte ohne Force Feedback
DISCL_EXCLUSIVE DISCL_FORDERGROUND
alle außer Tastatur
DISCL_EXCLUSIVE DISCL_BACKGROUND
Joysticks sowie Geräte mit Force Feedback
→ nicht zulässig für Tastatur und Maus
Die DirectInputDevice.SetCommonDataFormat()- und DirectInputDevice.Set- DateFormat()-Methoden teilen DirectInput mit, welche Objekte benutzt und wie die Daten empfangen werden.
3.3.1 Standard-Datenformat
Mit der DirectInputDevice.SetCommonDataFormat()-Methode können wir die Standard Datenformate festlegen. Die Methode beinhaltet nur einen Parameter.
Der Parameter format ist vom Typ CONST_DICOMMONDATAFORMATS.
Enum CONST_DICOMMONDATAFORMATS DIFORMAT_JOYSTICK = 3 DIFORMAT_JOYSTICK2 = 4 DIFORMAT_KEYBOARD = 1 DIFORMAT_MOUSE = 2 End Enum
Um das Datenformat z.B. für die Tastatur zu setzen, wählen Sie folgende Syntax:
DirectInputDevice.SetCommonDataFormat DIFORMAT_KEYBOARD
Bevor Sie auf ein Gerät zugreifen können, muss das Datenformat festgelegt wer- den. Während Sie auf das Gerät zugreifen, können Sie das Datenformat nicht ändern.
3.3.2 Benutzerdefiniertes Datenformat
Mit der DirectInputDevice.SetDataFormat()-Methode können Sie ein benutzer- definiertes Datenformat festlegen. Diese Methode beinhaltet zwei Parameter. Der erste Parameter format empfängt Daten über des Gerät selber. Der zweite Para- meter formatArray() ist für die Objekte des Gerätes reserviert.
Bevor wir die Methode aufrufen dürfen, müssen wir die Eigenschaften für die bei- den Parameter bestimmen. Das folgende Listing definiert das Datenformat für eine Maus mit zwei Achsen, allerdings ohne Knöpfe.
Datenformat Beschreibung
DIFORMAT_KEYBOARD Tastatur: Für jede Taste ein Byte. Wird eine Taste momentan gedrückt, so ist das oberste Bit des entspre- chenden Bytes gesetzt.
DIFORMAT_MOUSE Maus: vier Tasten, drei Achsen
DIFORMAT_JOYSTICK Joystick: <= 32 Knöpfe, <= 6 Achsen, <= 2 Schie- beregler, <= 4 Richtungsschalter
DIFORMAT_JOYSTICK2 Datenformat für erweiterte Joysticks
Beispiel: Tastatur 260
Dim dx As New DirectX7 Dim di As DirectInput
Dim diDev As DirectInputDevice Dim DataFormat As DIDATAFORMAT
Dim ObjektFormat(1) As DIOBJECTDATAFORMAT Private Sub Form_Load()
Set di = dx.DirectInputCreate()
Set did = di.CreateDevice("GUID_SysMouse") ObjektFormat (0).lFlags = DIDOI_POLLED ObjektFormat (0).lOfs = 0
ObjektFormat (0).lType = DIDFT_RELAXIS ObjektFormat (0).strGuid = "GUID_XAxis"
ObjektFormat (1).lFlags = DIDOI_POLLED ObjektFormat (1).lOfs = 4
ObjektFormat (1).lType = DIDFT_RELAXIS ObjektFormat (1).strGuid = "GUID_YAxis"
DataFormat.dataSize = 8
DataFormat.lFlags = DIDF_RELAXIS DataFormat.lObjSize = 4
DataFormat.numObjs = 2
diDev.SetDataFormat DataFormat, fDA() End Sub
3.4 Beispiel: Tastatur
Für die Eingabe über die Tastatur erstellen wir zuerst das DirectInput-Objekt.
Anschließend wird das DirectInput-Device erstellt, welches die Tastatur repräsen- tiert. Es bestimmt das Verhalten der Tastatur und empfängt die Daten, welche von der Tastatur gesendet werden.
Unser Beispielprogramm zeigt eine Listbox. In dieser werden die gedrückten Tas- ten angezeigt.
Schritt 1: Erstellen des DirectInput-Objektes und des Tastaturgerätes Schritt 2: Tastaturparameter setzen
Schritt 3: Verbindung zur Tastatur herstellen Schritt 4: Daten von der Tastatur empfangen
Für unser Beispielprogramm dimensionieren wir folgende Variablen.
Dim dx As New DirectX7 Dim di As DirectInput
Dim diDev As DirectInputDevice Dim TastaturStatus As DIKEYBOARDSTATE Dim Tasten(255) As String
Private Sub Form_Load()
Schritt 1: Erstellen des DirectInput-Objektes und des Tastaturgerätes Set di = dx.DirectInputCreate()
Set diDev = di.CreateDevice("GUID_SysKeyboard")
Schritt 2: Tastaturparameter setzen
Für das Standardgerät Tastatur können wir auf ein vordefiniertes Datenformat zugreifen. Deshalb benutzen wir die SetCommonDataFormat()-Methode mit dem Parameter DIFORMAT_KEYBOARD. Für Cooperative Level brauchen wir keinen exklusiven Zugriff (ist für die Tastatur auch nicht erlaubt), aber gleichzei- tig möchten wir die Tastaturdaten empfangen, auch wenn die Anwendung den Focus verliert.
diDev.SetCommonDataFormat DIFORMAT_KEYBOARD
diDev.SetCooperativeLevel Me.hWnd, DISCL_NONEXCLUSIVE Or _ DISCL_BACKGROUND
Schritt 3: Verbindung zur Tastatur herstellen
Die Methode Acquire() besitzt keine Parameter und erstellt die Verbindung zum Eingabegerät. Zu welchem Eingabegerät die Verbindung hergestellt werden soll, wird durch die DirectInput.CreateDevice()-Methode festgelegt.
diDev.Acquire
Timer1.Enabled = True End Sub
Bild 3.3: Bildschirmfoto Tastatur
Beispiel: Joystick 262
Schritt 4: Daten von der Tastatur empfangen
Über das Timerobjekt fragen wir die Tastaturdaten ab. Die DirectInput.Get DeviceStateKeyboard()-Methode empfängt unverzüglich die gewünschten Daten.
Private Sub Timer1_Timer() List1.Clear
diDev.GetDeviceStateKeyboard TastaturStatus For i = 0 To 255
If TastaturStatus.Key(i) <> 0 Then List1.AddItem KeyNames(i)
Die Funktion KeyNames() interpertiert den in TastaturStatus.Key() gespeicherten Rückgabewert. So erhalten wir eine aussagefähige Information auf dem Bild- schirm angezeigt.
End If Next End Sub
Interpretation der empfangenen Tastaturdaten:
Function KeyNames(ByVal iNum As Integer) As String Tasten(1) = "DIK_ESCAPE"
Tasten(2) = "DIK_1 On main keyboard"
Tasten(3) = "DIK_2 On main keyboard"
. . .
Tasten(220) = "DIK_RWIN Right Windows key"
Tasten(221) = "DIK_APPS Application key"
Tasten(116) = "DIK_PAUSE"
KeyNames = Tasten(iNum) End Function
3.5 Beispiel: Joystick
Bei dem Joystickdemo werden wir zuerst die angeschlossenen Joysticks abfragen.
Anschließend werden die Joystickfähigkeiten ermittelt und die notwendigen Para- meter gesetzt. Zum Abschluss werden die Joystickdaten empfangen.
Bei dem Tastaturdemo konnten wir voraussetzen, dass eine Tastatur am Computer angeschlossen ist. Die Annahme, das ein Joystick angeschlossen ist, können wir nicht treffen. Es ist deshalb notwendig abzufragen, welcher Joystick zur Zeit ver- fügbar ist. Gleichzeitig ist es wichtig, dass wir genaue Informationen über die Fähigkeiten des Eingabegerätes erhalten. Zu diesem Zeitpunkt wäre es verfrüht, wenn wir auf die vielfältigen Möglichkeiten des Force Feedback Joysticks einge- hen würden, deshalb werden wir diese etwas vernachlässigen.
Die Abfrage der Joystickdaten gestaltet sich komplizierter als bei dem Tastatur- demo. Die eintreffenden Daten per Timer-Objekt abzufragen wäre viel zu lang- sam. Der übliche Weg zum Empfangen von Joystickdaten ist es, eine Callback- Funktion einzurichten. Diese wird immer dann aufgerufen wenn neue Daten ein- treffen. Der Joystick liefert allerdings permanent Daten.
Schritt 1: Auflisten der verfügbaren Joysticks und Erzeugen des Joystick-Objek- tes
Schritt 2: Abfragen der Joystickfähigkeiten Schritt 3: Setzen der Joystickeigenschaften
Schritt 4: Direktes Empfangen der Joystickdaten per Callback-Funktion Für unser Beispielprogramm dimensionieren wir folgende Variablen:
Implements DirectXEvent
Da wir die Joystickdaten per Callback abrufen wollen, treffen wir mit der Imple- ments-Anweisung die Vorbereitung. DirectXEvent ist eine eigene Klasse. Sie beinhaltet nur eine Methode: DirectXEvent.DXCallback()
Dim dx As New DirectX7 Dim di As DirectInput
Dim diDev As DirectInputDevice
Dim diDevEnum As DirectInputEnumDevices Dim EventHandle As Long
Dim joyCaps As DIDEVCAPS Dim js As DIJOYSTATE Dim Achsen(8) As Boolean Bild 3.4: Bildschirmfoto Joystick
Beispiel: Joystick 264
Dim Knöpfe(32) As Boolean Dim Pov(4) As Boolean Dim JoyGuid(16) As String Dim Fd As DIDATAFORMAT
Dim fda(1) As DIOBJECTDATAFORMAT Dim Running As Boolean
Dim JoyAngeschlossen As Boolean
Schritt 1: Auflisten der verfügbaren Joysticks und Erzeugen des Joystick- Objektes
Mit diesem Beispielprogramm führen wir die Subroutine InitDirectInput() ein.
Sie wird jetzt und in Zukunft die Initialisierungsaufgaben übernehmen. Gleichzei- tig werden wir dieser Routine diverse Prüfungsaufgen anvertrauen. In dem jetzi- gem Beispielprogramm prüfen wir das System auf alle registrierten Joysticks.
Wird ein gültiger Joystick gefunden, so können wir das notwendige Gerät erstel- len. Wir aber kein Joystick gefunden, so beenden wir das Programm. Das Ergeb- nis der Prüfung übergeben wir an die Combobox1 auf Form1 unserer Anwen- dung.
Sub InitDirectInput()
Set di = dx.DirectInputCreate()
Set diDevEnum = di.GetDIEnumDevices(DIDEVTYPE_JOYSTICK, _ DIEDFL_INCLUDEPHANTOMS)
Die DirectInput.GetDIEnumDevices()-Methode füllt das Typen-Arrays diDev Enum. Die Parameter der DirectInput.GetDIEnumDevices()-Methode bestim- men, dass wir Informationen über Joysticks erhalten möchten. Die Konstante DIEDFL_INCLUDEPHANTOMS im zweiten Parameter informiert DirectInput, dass Informationen über alle vom System registrierten Joysticks gewünscht sind.
Es wird nicht unterschieden, ob der Joystick angeschlossen ist oder nicht.
If diDevEnum.GetCount = 0 Then
MsgBox "Es wurde kein Joystick gefunden."
Unload Me End If
Die Prüfung, ob überhaupt ein Joystick verfügbar ist, können wir mit DirectInput EnumDevices.GetCount()-Methode durchführen. Liefert diese Methode eine 0 zurück, so wissen wir, das kein Joystick angeschlossen ist. Ansonsten wird die Methode die Anzahl der angeschlossenen Joysticks bekannt geben.
Dim i As Integer
For i = 1 To diDevEnum.GetCount
Combo1.AddItem diDevEnum.GetItem(i).GetInstanceName JoyGuid(i) = diDevEnum.GetItem(i).GetGuidInstance Next i
Nacheinander lassen wir alle Joystickbezeichnungen in die Combobox eintragen.
Zusätzlich merken wir uns den Guid der gefundenen Joysticks, dieser wird beim Erstellen des Gerätes benötigt. Über diese Box können wir dann einen der geliste- ten Joysticks wählen und testen.
EventHandle = dx.CreateEvent(Me)
Die DirectX7.CreateEvent() liefert das Handle des Event-Objektes, welches in der Form eingebunden ist. Für den vollautomatischen Callback ist das Handle unverzichtbar.
End Sub
Schritt 2: Abfragen der Joystickfähigkeiten
Wenn das Combo1_Click()-Ereignis ausgelöst wird, ermitteln wir die Fähigkeiten des selektierten Joysticks. Ebenso ermitten wir, ob der selektierte Joystick mit dem Computer verbunden ist.
Set diDevEnum = di.GetDIEnumDevices(DIDEVTYPE_JOYSTICK, _ DIEDFL_ATTACHEDONLY)
For EunmCount = 1 To diDevEnum.GetCount If JoyGuid(Index + 1) = _
diDevEnum.GetItem(EunmCount).GetGuidInstance Then Label3 = "Joystick ist mit dem Computer verbunden"
Else
Label3 = _
"Joystick ist --> N I C H T <-- mit dem Computer verbunden"
End If
Next EunmCount
If diDevEnum.GetCount = 0 Then Label3 = "Joystick ist _ --> N I C H T <-- mit dem Computer verbunden"
Entsprechend der Prüfung ob der gewählte Joystick mit dem System verbunden ist, verändern wir den Infotext von Label3. Nur von den angeschlossenen Joy- sticks werden wir später die Daten empfangen und auf dem Bildschirm anzeigen.
Die eigentliche Prüfung der Joystickfähigkeiten übernimmt die CheckCaps()- Subroutine. Diese haben wir in drei Abschnitte unterteilt. Für die Abfrage der Achsen, Knöpfe und Coolie Steuerungen (Richtungsschalter) nutzen wir die DirectInputDevice.GetDeviceObjectsEnum()-Methode. Sie verlangt den Para- meter flags As CONST_DIDFTFLAGS. Er bestimmt welche Fähigkeit des Joy- sticks abgefragt werden soll.
Set didoEnum = diDev.GetDeviceObjectsEnum(DIDFT_AXIS)
Mit der For...Next-Schleife überprüfen wir die gefundnen Joystickfähigkeiten.
Zum Vergleich stellt uns DirectInput die CONST_DIJOYSTICKOFS-Konstan- tenliste zur Verfügung.
Beispiel: Joystick 266
For i = 1 To didoEnum.GetCount Set dido = didoEnum.GetItem(i) Select Case dido.GetOfs Case DIJOFS_X
Achsen(1) = True Case DIJOFS_Y Achsen(2) = True Case DIJOFS_Z Achsen(3) = True Case DIJOFS_RY Achsen(4) = True Case DIJOFS_RX Achsen(5) = True Case DIJOFS_RY Achsen(6) = True Case DIJOFS_SLIDER0 Achsen(7) = True Case DIJOFS_SLIDER1 Achsen(8) = True End Select
Next
In unserm Beispiel übertragen eine eine positive Prüfung der möglichen Achsen in das Array Achsen(). Die Auswertung für die Bildschirmanzeige ist gänzlich unkompliziert und muss nicht erläutert werden.
For i = 1 To 8
If Achsen(i) = True Then Check1(i – 1).Value = 1 Check1(i – 1).Enabled = True Text1(i – 1).Visible = True Else
Check1(i – 1).Value = 0 Check1(i – 1).Enabled = False Text1(i – 1).Visible = False End If
Next i
Schritt 3: Setzen der Joystickeigenschaften
Unterschiedliche Joysticks liefern unterschiedliche Wertebereiche bei der Ach- senabfrage. Da Sie als Programmierer nicht wissen können, welchen Joystick der Anwender einmal nutzen wird, legen wir den gültigen Wertebereich einfach fest.
Die Joystick werden die selbstdefinierten Grenzen beachten, sodass wir eine ein- heitliche Interpretation der empfangenen Daten erstellen können. Bevor wir uns um die Interpretation kümmern, setzen wir nun die Joystickeigenschaften. Hierzu nutzen wir die SetProperties()-Subroutine.
Public Sub SetProperties() Dim DiProp_Range As DIPROPRANGE With DiProp_Range
.lHow = DIPH_DEVICE
Da wir den Wertebereich für alle Achsen begrenzen möchten, wählen wir die Ein- stellung DIPH_DEVICE.
.lSize = Len(DiProp_Range) .lMin = 0
.lMax = 10000
Durch die Parameter lMin und lMax wird der Wertebereich der Achsen begrenzt.
Diese Berenzung wirkt sich auf alle Joysticks aus. Programmtechnisch ist solch eine Einschränkung von Nutzen, denn dadurch wird es möglich, über eine einheit- liche Routine die Daten unterschiedliche Joysticks auszuwerten. Ohne solch eine Einschränkung müsste man für verschieden Joysticks auch unterschiedliche Aus- wertungen erstellen.
End With
diDev.SetProperty "DIPROP_RANGE", DiProp_Range
Die folgenden Geräteeigenschaften definieren die Deadzone. Die Deadzone ist ein Wertebereich in dem der Joystick nicht reagieren soll. Die Mitte des Wertebe- reichs ist die Nullstellung (wenn Sie den Joystick nicht bewegen).
Dim DiProp_Dead As DIPROPLONG With DiProp_Dead
.lData = 1000
Die Deadzone wird in unserem Beispiel mit 1000 (10% der Maximalwerte) fest- gelegt. Setzen Sie diesen Wert doch mal auf 1, Sie werden beobachten, dass die Daten des Joysticks immer etwas schwanken, obwohl er sich in Nullstellung befindet.
.lSize = Len(DiProp_Dead) .lHow = DIPH_BYOFFSET .lObj = DIJOFS_X
diDev.SetProperty "DIPROP_DEADZONE", DiProp_Dead
Zuerst legen wir die Deadzone für die X Achse fest. Anschließend folgt die Defi- nition für die Y Achse.
.lObj = DIJOFS_Y
diDev.SetProperty "DIPROP_DEADZONE", DiProp_Dead End With
End Sub
Beispiel: Maus 268
Schritt 4: Direktes Empfangen der Joystickdaten per Callback-Funktion Die Joystickdaten werden über die DirectXEvent_DXCallback()-Subroutine emp- fangen. Bevor wir an die Daten gelangen, müssen wir das Gerät erstellen, das Da- tenformat festlegen, den Cooperativ Level festlegen, die Callbackbenachrichtigung einschalten und die Verbindung zu dem Gerät schaffen. Dies wird von uns im letz- ten Abschnitt der Combo1_Click()-Subroutine erledigt.
Private Sub Combo1_Click() .
. .
Set diDev = di.CreateDevice(JoyGuid(Index + 1)) diDev.SetCommonDataFormat DIFORMAT_JOYSTICK
diDev.SetCooperativeLevel Me.hWnd, DISCL_BACKGROUND Or _ DISCL_NONEXCLUSIVE
Call diDev.SetEventNotification(EventHandle) SetProperties
diDev.Acquire
Do While Running = True diDev.Poll
Die DirectInputDevice.Poll()-Methode stellt die Joystickdaten zur Verfügung.
Der Joystick wird ohne Unterbrechung Daten senden, deshalb wird auch unter- brechungsfrei die Callback-Routine aufgerufen.
DoEvents Loop
3.6 Beispiel: Maus
Der Zugriff auf die Systemmaus als Eingabegerät ist vergleichbar mit der einge- setzten Joysticktechnik. In unserem Beispiel gehen wir davon aus, dass nur eine Maus angeschlossen ist. Es entfällt somit die Auflistung der verfügbaren Einga- begeräte und wir konzentrieren uns aus die Systemmaus.
Je nachdem welche Maus der Anwender angeschlossen hat, besitzt die Maus unterschiedliche Fähigkeiten. DirectInput unterstützt folgende Objekte:
• 3 Achsen (X-Achse, Y-Achse und Z-Achse)
• 4 Knöpfe
Per Callback-Funktion fragen wir die Joystickdaten ab. Im Gegensatz zum Joy- stick sendet die Maus nicht permanent Daten, sondern nur dann, wenn Sie die Maus bewegen oder eine Taste drücken.
Schritt 1: Erzeugen des Maus-Objektes Schritt 2: Abfragen der Mausfähigkeiten Schritt 3: Setzen der Mauseigenschaften
Schritt 4: Direktes Empfangen der Mausdaten per Callback-Funktion
Neben den benötigten DirectInput-Methoden müssen wir noch einige API-Funk- tionen deklarieren. Diese sind für die Abfrage der Mausinformationen nicht not- wendig, werden aber für eine ordentliche Auswertung benötigt. In einer DirectX- Anwendung, in der Sie nicht unmittelbar mit der Windowsoberfläche arbeiten, können Sie auf diese getrost verzichten.
Implements DirectXEvent
Private Declare Function GetCursorPos Lib "user32" _ (lpPoint As POINTAPI) As Long
Private Declare Function SetCursorPos Lib "user32" _ (ByVal X As Long, ByVal Y As Long) As Long
Private Declare Function ScreenToClient Lib "user32" _ (ByVal hWnd As Long, lpPoint As POINTAPI) As Long Private Type POINTAPI
X As Long Y As Long End Type
Dim dx As New DirectX7 Dim di As DirectInput
Dim diDev As DirectInputDevice
Dim diDevEnum As DirectInputEnumDevices Dim Achsen(8) As Boolean
Bild 3.5: Bildschirmfoto Maus
Beispiel: Maus 270
Dim Knöpfe(32) As Boolean Dim EventHandle As Long Dim MausCaps As DIDEVCAPS Dim js As DIMOUSESTATE Dim Running As Boolean
Dim JoyAngeschlossen As Boolean Dim MouseX As Long
Dim MouseY As Long Dim CursorPos As POINTAPI Dim ClientWindowsLeft As Long Dim ClientWindowsTop As Long Dim ZeichenModus As Boolean Dim Speed As Long
Schritt 1: Erzeugen des Maus-Objektes Sub InitDirectInput()
Set di = dx.DirectInputCreate()
Set diDevEnum = di.GetDIEnumDevices(DIDEVTYPE_MOUSE, _ DIEDFL_ATTACHEDONLY)
If diDevEnum.GetCount = 0 Then
MsgBox "Es wurde kein Mouse gefunden."
End End If
Sollte keine Maus gefunden werden, so beenden wir das Programm. Diesmal ist es nur notwendig, das System auf angeschlossene Geräte zu prüfen. Von den an- geschlossenen Geräten interessiert uns auch nur die Maus. Für den ersten Parame- ter der DirectInput.GetDIEnumDevices()-Methode wählen wir DIDEVTYPE_
MOUSE. Dieser besagt, das wir nur nach einem Gerät vom Typ Maus suchen. Der zweite Parameter DIEDFL_ATTACHEDONLY besagt, dass wir nur angeschlos- sene Geräte suchen.
EventHandle = dx.CreateEvent(Me)
Set diDev = di.CreateDevice("guid_SysMouse")
Nachdem eine Maus gefunden wurde, können wir das Gerät erstellen. Zum Erstellen einer Systemmaus verwenden wir die Konstante guid_SysMouse.
CheckCaps
Die Prüfung der Mausfähigkeiten übernimmt die CheckCaps()-Subroutine. Diese betrachten wir im Anschluss.
diDev.SetCommonDataFormat DIFORMAT_MOUSE
diDev.SetCooperativeLevel Me.hWnd, DISCL_BACKGROUND Or ISCL_NONEXCLUSIVE
Call diDev.SetEventNotification(EventHandle) SetProperties
Nachdem wir das Gerät erstellt haben und bevor wir die Verbindung zum Gerät herstellen, können wir die Eigenschaften des Geräts festlegen. Dies übernimmt die SetProperties()-Subroutine.
diDev.Acquire Running = True
Do While Running = True DoEvents
Loop End Sub
Schritt 2: Abfragen der Mausfähigkeiten Public Sub CheckCaps()
diDev.GetCapabilities MausCaps
Wir übergeben die Mausfähigkeiten an MausCaps As DIDEVCAPS. Aus Maus- Caps benötigen wir eigentlich nur die Anzahl der Mausknöpfe. Programmtech- nisch können Sie das auch anders lösen, dies würde aber der DirectX Philosophie etwas widersprechen.
Dim didoEnum As DirectInputEnumDeviceObjects Dim dido As DirectInputDeviceObjectInstance .
. .
Prüfung der Achsen.
Set didoEnum = diDev.GetDeviceObjectsEnum(DIDFT_AXIS) For i = 1 To didoEnum.GetCount
Set dido = didoEnum.GetItem(i) Select Case dido.GetOfs Case DIMOFS_X Achsen(1) = True Case DIMOFS_Y Achsen(2) = True Case DIMOFS_Z Achsen(3) = True End Select
Next
Prüfung der Knöpfe.
Set didoEnum = diDev.GetDeviceObjectsEnum(DIDFT_BUTTON) For i = 1 To didoEnum.GetCount
Set dido = didoEnum.GetItem(i) a = dido.GetOfs
Select Case dido.GetOfs
Beispiel: DirectInput und Direct3D 272
Case DIMOFS_BUTTON0 Knöpfe(1) = True Case DIMOFS_BUTTON1 Knöpfe(2) = True Case DIMOFS_BUTTON2 Knöpfe(3) = True Case DIMOFS_BUTTON3 Knöpfe(4) = True End Select
Next . . . End Sub
Schritt 3: Setzen der Mauseigenschaften Public Sub SetProperties()
Dim DiProp_Range As DIPROPRANGE Dim diProp As DIPROPLONG
diProp.lHow = DIPH_DEVICE
diProp.lData = DIPROPAXISMODE_REL
Die Konstante DIPROPAXISMODE_REL besagt, dass die von der Maus gesen- deten Daten relative Daten sind. Dem Programmierer werden nur die Änderungen von einer Abfrage zur darauf folgenden Abfrage mitgeteilt. Sie können auch die Konstante DIPROPAXISMODE_ABS verwenden, dann erhalten Sie absolute Werte. In unserem Beispiel sind relative Daten besser auszuwerten.
diProp.lSize = Len(diProp)
Call diDev.SetProperty("DIPROP_AXISMODE", diProp) End Sub
3.7 Beispiel: DirectInput und Direct3D
Mit diesem Beispielprogramm demonstrieren wir die Integration von DirectInput in einer Direct3D-Anwendung. Nach dem Starten des Programms erhalten Sie ein Auswahlfenster. Hier können Sie zwischen einer Steuerung per Tastatur oder per Joystick wählen. Es werden Ihnen nur Joysticks, welche mit dem System verbun- den sind, angezeigt. Benutzen Sie einen analogen Joystick, so wird die Laufge- schwindigkeit entsprechend der Joystickintensität angepasst. Zusätzlich haben wir eine einfache Technik einer Kollisionserkennung eingebunden.
Die Tastatur wir diesmal per Callback abgefragt. In unserem ersten Beispiel wurde die Abfrage noch per Timerobjekt ausgeführt. Diese Lösung wäre für die- ses Beispiel nicht so geeignet. Da wir für den Joystick eine Callback-Funktion einrichten, bietet es sich an, die Tastaturabfrage in der gleichen Subroutine durch- zuführen.
Zuerst betrachten wir das Auswahlfenster für die verfügbaren Eingabegeräte.
Bevor wir ein Eingabegerät auswählen können, müssen die verfügbaren Geräte angezeigt werden. Wir gehen davon aus, dass eine Tastatur immer angeschlossen ist, sodass wir diese nicht testen und auflisten müssen. Für die Auswahl eines Joy- sticks sieht das ganz anders aus.
Private Sub Form_Load()
Set di = dx.DirectInputCreate()
Set diEnumDev = di.GetDIEnumDevices(DIDEVTYPE_JOYSTICK, _ DIEDFL_ATTACHEDONLY)
devicecount = diEnumDev.GetCount() .
. .
For i = 1 To devicecount Set diDev = _
di.CreateDevice(diEnumDev.GetItem(i).GetGuidInstance) Combo1.AddItem (diEnumDev.GetItem(i).GetProductName) Bild 3.6: Bildschirmfoto Bewegung
Beispiel: DirectInput und Direct3D 274
Alle angeschlossenen Joysticks werden in die Comb1-Combobox eingetragen.
Diese Prozedur ist Ihnen bereits bekannt und kann im Beispielprogramm »Auflis- ten der verfügbaren Geräte und Objekte« nachgelesen werden.
Next i
Combo1.Text = Combo1.List(0) Listen 1
Die Subroutine Listen() zeigt die Joystickfähigkeiten an. Auch diese ist Ihnen bekannt und wird jetzt nicht näher erklärt.
End Sub
Nachdem Sie das Eingabegerät gewählt haben, klicken Sie auf die Schaltfläche Weiter.
Private Sub Command1_Click()
If Label5 = "N/A" Or Label6 = "N/A" Then MsgBox "Sie haben _ keine Eingabegerät gewählt!": Exit Sub
Sollten Sie kein Eingabegerät gewählt haben, verlassen wir die Subroutine direkt.
Bild 3.7: Bildschirmfoto Bewegung (Login)
InputName = Label5 InputGuid = Label6
Die Variablen InputName und InputGuid nutzen wir, um später das Eingabegerät zu erzeugen. InputName dient hauptsächlich dazu, um die beiden Eingabegeräte (Tastatur oder Joystick) zu unterscheiden.
Unload Me Load Form1 End Sub
Weiter geht es mit der Subroutine Form_Load() von Form1.
Private Sub Form_Load() InitDDraw
InitD3D InitInput Licht RenderState MakeVertex .
. . End Sub
Die Subroutine InitInput() richtet das Eingabegerät ein. Bevor wir auf weitere Elemente der Form_Load()-Subroutine eingehen, schauen wir uns die InitIn- put()-Subroutine genauer an.
Public Sub InitInput() Select Case Form2.InputName Case "Tastatur"
Set di = g_dx.DirectInputCreate() EventHandle = g_dx.CreateEvent(Me)
Set diDev = di.CreateDevice("GUID_SysKeyboard")
Beim Einrichten der Tastatur sind wir auf eine individuelle Guid nicht angewie- sen. Wir können die feste Kennung GUID_SysKeyboard verwenden.
diDev.SetCommonDataFormat DIFORMAT_KEYBOARD
diDev.SetCooperativeLevel Me.hWnd, DISCL_BACKGROUND Or _ DISCL_NONEXCLUSIVE
Call diDev.SetEventNotification(EventHandle) diDev.Acquire
Case Else
Set di = g_dx.DirectInputCreate() EventHandle = g_dx.CreateEvent(Me)
Set diDev = di.CreateDevice(Form2.InputGuid)
Beispiel: DirectInput und Direct3D 276
Für das Erzeugen des Joysticks benötigen wir eine individuelle Guid. Diese wurde bei der Joystickauswahl ermittelt und in der Variablen InputGuid gespei- chert.
diDev.SetCommonDataFormat DIFORMAT_JOYSTICK
diDev.SetCooperativeLevel Me.hWnd, DISCL_BACKGROUND Or _ DISCL_NONEXCLUSIVE
Call diDev.SetEventNotification(EventHandle) SetProperties
diDev.Acquire End Select End Sub
Um die Eingabedaten zu empfangen, haben wir uns bei beiden Eingabemöglich- keiten für eine Callback-Funktion entschieden.
Private Sub DirectXEvent_DXCallback(ByVal eventid As Long) On Local Error Resume Next
If InputFrei = False Then Exit Sub InputFrei = False
If diDev Is Nothing Then Exit Sub If Form2.InputName <> "Tastatur" Then diDev.GetDeviceStateJoystick js SpeedY = (5000 – js.y) / 5000 SpeedX = ((5000 – js.x) / 500) * -1
Als wir den Joystick erzeugt haben, wurden auch die Joystickeigenschaften neu definiert. Die Subroutine SetProperties() beschränkt den Wertebereich der Joy- stickachsen auf 0 bis 10000. Die Mittelstellung liegt also bei 5000. Wir werden also von allen Joysticks Daten in einem gemeinsamen Wertebereich erhalten, somit sind wir in der Lage, die Daten programmtechnisch auszuwerten. Die Daten übergeben wir an die globalen Variablen SpeedY und SpeedX.
Else
diDev.GetDeviceStateKeyboard ks
If ks.Key(200) And OldButton <> 200 Then SpeedY = 0.3
OldButton = 200 End If
If ks.Key(208) And OldButton <> 208 Then SpeedY = -0.3
OldButton = 208 End If
If ks.Key(203) And OldButton <> 203 Then SpeedX = -4
OldButton = 203 End If
If ks.Key(205) And OldButton <> 205 Then
SpeedX = 4 OldButton = 205 End If
If ks.Key(200) = 0 And ks.Key(208) = 0 Then SpeedY = 0
OldButton = 0 End If
If ks.Key(203) = 0 And ks.Key(205) = 0 Then SpeedX = 0
OldButton = 0 End If
If ks.Key(200) <> 0 And ks.Key(208) <> 0 Then SpeedY = 0
OldButton = 0 End If
If ks.Key(203) <> 0 And ks.Key(205) <> 0 Then SpeedX = 0
OldButton = 0 End If
Für die Datenauswertung der Tastatur mussten wir die Tastatureigenschaften nicht neu definieren. Schließlich gibt es für eine Taste nur zwei Zustände (ge- drückt oder nicht gedrückt). Anders als beim Joystick werden die empfangenen Daten auch nicht weiter interpretiert. Drücken Sie z.B. die Vorwärtstaste, so wird die Geschwindigkeit direkt auf einen festen Wert gesetzt. In unserem Beispiel auf 0.3. Analoge Zwischenwerte existieren nicht.
End Sub
Nachdem wir die Eingabegeräte erzeugt haben und in der Lage sind, die Eingabe- daten zu empfangen, werden die Informationen in der Form_Load()-Subroutine von Form1 weiter ausgewertet.
Private Sub Form_Load() .
. . Do
If g_dx.TickCount > TimerZeit + ZeitInterval Then TimerZeit = g_dx.TickCount
tempVector.x = BetrachterPos.x tempVector.z = BetrachterPos.z
Wir haben eine kleine Kollisionserkennung in das Programm eingebunden. Diese soll folgende Aufgabe lösen: »Erkennen einer Kollision mit einem Baum zwei Einheiten vor unserer aktuellen Laufrichtung«. Es ist wichtig, dass Sie eine mög- liche Kollision vor Ihrer aktuellen Position prüfen. Wir haben uns für zwei Ein- heiten vor uns entschieden. Wenn wir den Abstand zu unserer Position zu klein
Beispiel: DirectInput und Direct3D 278
wählen, treten unschöne Effekte (der Betrachter steht halb in dem Baum, der Betrachter kann durch den Baum sehen ...) auf.
If SpeedY > 0 Then
If speed > 2 Then xx = speed Else xx = 2 tempVector2.x = BetrachterPos.x + (xx * _ Sin(BetrachterWinkel / 57.296))
tempVector2.z = BetrachterPos.z + (xx * _ Cos(BetrachterWinkel / 57.296))
Else
If speed < 2 Then xx = speed * -1 Else xx = 2 tempVector2.x = BetrachterPos.x – (xx * _ Sin(BetrachterWinkel / 57.296))
tempVector2.z = BetrachterPos.z – (xx * _ Cos(BetrachterWinkel / 57.296))
End If
Beim Erzeugen der Bäume in der MakeVertex()-Subroutine haben wir das Typen- Arrays Kollision mit den Baumkoordinaten gefüllt. Diese Daten können wir nun für die Kollisionserkennung nutzen. Dazu fragen wir eine rechteckigen Bereich um den Baum herum ab. Befinden wir uns in diesem Bereich, dann wird eine Kol- lision erkannt.
For i = 1 To 60
If tempVector2.x > Kollision(i).PosX – 0.5 And _ tempVector2.x < Kollision(i).PosX + 0.5 And _ tempVector2.z > Kollision(i).PosZ – 0.5 And _ tempVector2.z < Kollision(i).PosZ + 0.5 Then BetrachterPos.x = tempVector.x
BetrachterPos.z = tempVector.z kolli = 1
Exit For
Wenn eine Kollision erkannt wurde, wird die Betrachterposition nicht verändert.
Der Betrachter verharrt an einer Stelle, ohne sich in die angestrebte Richtung bewegen zu können.
Else
kolli = 0 End If Next i
If kolli = 0 Then
BetrachterPos.x = BetrachterPos.x + _ (SpeedY * Sin(BetrachterWinkel / 57.296)) BetrachterPos.z = BetrachterPos.z + _ (SpeedY * Cos(BetrachterWinkel / 57.296))
Wurde keine Kollision erkannt, so darf der Betrachter die neue Position einneh- men.
End If
BetrachterWinkel = BetrachterWinkel + SpeedX BetrachterSicht.x = BetrachterPos.x + _ 100 * Sin(BetrachterWinkel / 57.296) BetrachterSicht.z = BetrachterPos.z + _ 100 * Cos(BetrachterWinkel / 57.296) BetrachterPos.y = 1.5
BetrachterSicht.y = 1.5
Call g_dx.ViewMatrix(matView, BetrachterPos,_
BetrachterSicht, MakeVector(0, 1, 0), 0)
g_d3dDevice.SetTransform D3DTRANSFORMSTATE_VIEW, matView
Durch die Transformation der ViewMatrix werden die Veränderungen von Betrachterposition und Betrachterblickwinkel für den Anwender sichtbar.
. . . End Sub
3.8 Bewegung im 3D-Raum
Position, Ausrichtung und Blickrichtung des Betrachters oder der Spielfigur wird im Allgemeinen über die Viewmatrix berechnet. Diese Parameter werden über verschiedene Vektoren festgelegt. Als Erstes haben wir die absolute Position im dreidimensionalen Raum. Diese Werte splitten sich in die drei Vektorkoordinaten x, y und z auf. Als Nächstes benötigen wir xyz Koordinaten für den Punkt auf den wir blicken, weiterhin können wir die Kopfneigung und Drehung festlegen. In unserem Beispiel beschränken wir uns zunächst auf die ersten beiden Parameter.
Das Problem:
Über den ersten 3D-Vektor sind wir generell in der Lage uns durch einen beliebi- gen Raum zu bewegen. Allerdings würden wir immer auf einen bestimmten Punkt (0,0,0) im Raum schauen. Würden wir zum Beispiel ein Objekt umkrei- sen, wäre diese Technik angebracht. Die Position des Betrachters würde sich stets verändern und der Blick fiele immer auf das umkreiste Objekt.
Diese Vorhergehensweise macht bei einsetzbaren Anwendungen wenig Sinn.
Vielmehr versucht man die Realität nachzuahmen, getreu dem Motto » Ich gehe, wohin ich sehe.« Viele Programme benutzen hierzu eine bewährte Steuerungs- technik. Betätigt man die Links-Rechts-Cursortasten, so dreht sich der Betrachter um die eigene Achse. Drückt man nun die Vor-Zurück-Cursortasten, bewegt man sich in die Richtung, in die man gerade schaut.
Bewegung im 3D-Raum 280
Um diese Eigenart zu simulieren, benötigen wir einen Blickrichtungsvektor.
Stellen wir uns vor, wir könnten genau 5 Meter weit sehen und wir würden uns einmal um unsere eigene Achse drehen. So würde das Ende unseres Blickfeldes einen Kreis mit einem Radius von 5 Metern um uns ziehen.
Das hieße also, die Blickrichtung liegt auf einem Kreisbogen um uns herum und unsere jetzige Standposition ist der Mittelpunkt dieses Kreises.
Bild 3.8: Umkreisen eines Objekts im 3D-Raum
Bild 3.9: Blickrichtung
Anhand der Grafik sehen wir, dass der Blickpunkt über einfache geometrische Kreisberechnungen ermittelt werden kann. Berücksichtigt werden muss nur noch, dass wir weiter sehen können, als wir mit einem Schritt gehen können. Hierzu berechnen wir einfach einen neuen Standort auf einem kleineren Kreisbogen, den wir dann der alten Position hinzuaddieren können.
So viel zur Theorie. Was nun folgt, ist die Praxis.
Unsere aktuellen Standortvariablen benennen wir (AktX,AktZ). Als Weiteres benötigen wir die aktuelle Blickrichtung in Grad. Nennen wir diese AktBR.
Um nun eine Drehung auszuführen, müssen wir AktBR in einem Bereich von 0-359 Grad verändern.
Für die Tastaturabfrage eignet sich der API-Aufruf GetAsyncKeyState, den wir in einer DoEvents-Endlosschleife auf einen vorhandenen Tastendruck abfragen.
Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer
Dim AktX as Single Dim AktZ as Single Dim AktBR as Single Private Sub Schleife() Do
Drehung links um die Y-Achse
If GetAsyncKeyState(vbKeyLeft) <> 0 then AktBR = AktBR -1
If AktBR <0 then AktBR=359 End If
Bild 3.10: Berechnung des neuen Standorts auf einen neuen Blickpunkt hin
Bewegung im 3D-Raum 282
Drehung rechts um die Y-Achse
If GetAsyncKeyState(vbKeyRight) <> 0 then AktBR = AktBR+1
If AktBR >359 then AktBR=0 end if
Berechnung des Positionsabgleichs Vorwärtsbewegung.
If GetAsyncKeyState(vbKeyUp) <> 0 then px = 0.5 * Cos( AktBR / 180 / Pi ) pz = 0.5 * Sin( AktBR / 180 / Pi ) AktX = AktX + px
AktZ = AktZ + pz End If
Berechnung des neuen Blickpunktes.
BlickX = 100 * Cos (AktBR / 180 / Pi ) BlickY = 100 * Sin (AktBR / 180 / Pi ) Call g_dx.ViewMatrix(matView, _
MakeVector(AktX,0,AktZ), MakeVector(BlickX, _ 0, BlickY), MakeVector(0, 1, 0), 0)
Loop End Sub
Private Function MakeVector( a As Double, b As Double, _ c As Double ) as D3DVECTOR
Dim vecOut as D3DVECTOR vecOut.x = a
vecOut.y = b vecOut.z = c MakeVector = vecOut End Function
Bild 3.11: Berechnung der neuen Position im Koordinatensystem