Tipp 0462 Bewegen im 3D-Raum
Autor/Einsender:
Datum:
  Alexander Csadek
22.09.2005
Entwicklungsumgebung:
DirectX-Version:
  VB 6
DirectX 8
Ein Spiel in 3D bringt nicht viel wenn man sich im 3D-Raum nicht bewegen kann. Solch ein Bewegen im 3D-Raum ist nicht schwer zu realisieren, wenn die Grundlagen beachtet werden mit denen Direct3D arbeitet.
Alles hängt von der Kamera (ViewPort) ab. Hierbei ist es wichtig zu wissen, dass die Kamera aus zwei Positionen im 3D Raum besteht, genauer gesagt, aus zwei Vektoren. Der erste Vektor beschreibt die Position (Eye Point) der Kamera im 3D-Raum und der zweite Vektor das Ziel (LookAt) der Kamera im 3D-Raum. Den Abstand zwischen den beiden Vektoren könnte man als Sichtweite bezeichnen.
Würde man nur die Position (Start-Vektor) der Kamera verändern aber nicht das Ziel, dann würde die Kamera, egal wo sie sich im 3D-Raum gerade befindet, immer auf das gleiche Ziel (End-Vektor) blicken. Und das wäre in einem Spiel nicht gerade der erwünschte Effekt. In den Spielen ist es meist so, ich schaue wohin ich gehe, oder andersrum, ich gehe wohin ich schaue. Um das in einem 3D-Spiel zu realisieren, braucht man einen Blick-Winkel. Also einen Winkel der festlegt, wohin der Spieler im 3D-Raum schaut.
Mit der Position des Spielers (= Kamera), dem Blickwinkel und der Sichtweite, kann die Ausrichtung der Kamera (= die Zielposition der Kamera) berechnet werden. Hierfür gibt es zwei Möglichkeiten. Es werden Start- und Ziel-Position der Kamera berechnet und die Kamera (ViewPort) neu gesetzt. Oder wir verwenden die Matrizen-Funktonen.
Der Tipp zeigt wie die Kamera (ViewPort) im 3D-Raum unter Verwendung der Matrizen-Funktionen bewegt werden kann. Hierbei muss der Blick-Punkt der Spielers nicht manuell errechnet werden.
In Direct3D wird der ViewPort (Kamera) über eine Matrix definiert. Eine Matrix kann man verschieben (translate) und um die X-, Y-, Z-Achse rotieren (rotate) lassen. Wenn dann die Matrix als neuer ViewPort (Kamera) an Direct3D übergeben wird, wird der Blick-punkt automatisch neu berechnet.
Wie sieht das nun also aus?
1. Beim Erstellen vom Direct3D wird der ViewPort (Kamera) auf einen Startpunkt und Zielpunkt gesetzt, dabei wird dem Direct3D gleich die gewünschte Sichtweite mitgeteilt.
2. Der ViewPort (Kamera) wird an die gewünschte Position geschoben (translate), da es ja ziemlich unsinnig ist, wenn der Spieler außerhalb vom Level beginnt.
Im Spiel selbst benötigen wir drei Hilfsmatrizen, mit denen die Bewegungen in der ViewPort-Matrix durchgeführt werden:
1. Dreht sich der Spieler, so wird die neue Blickrichtung (Blickwinkel) und Laufrichtung errechnet.
2. Bewegt sich der Spieler nun vor-, rück- oder seitwärts, so wird die Matrix auf Grund der Laufrichtung verschoben.
3. Die Matrix wird mit dem neuen Blickwinkel um die Y-Achse gedreht.
4. Schaut der Spieler rauf oder runter, wird die Matrix um die X-Achse entsprechend gedreht.
5. Damit alle Veränderungen zusammengeführt werden, müssen die Hilfsmatrizen miteinander multipliziert werden.
6. Das Ergebnis wird dem Direct3D als neue ViewPort-Matrix übergeben.
Code
 
'--- Im Erstellungs-Bereich vom Direct3D ---
  'ViewportMatrix einstellen
  'Dieser wird definiert durch einen Ausgangspunkt (auch
  'Eye Point genannt), einer Orientierung (auch LookAt genannt)
  'und der Angabe wo "oben" ist (Standard ist Y, die Achse, die
  '"oben" ist).
  D3DXMatrixIdentity View3DMatrix
  D3DXMatrixLookAtLH View3DMatrix, vec3(0, 0, 0), _
            vec3(0, 0, 1000), vec3(0, 1, 0)
  gD3DDevice8.SetTransform D3DTS_VIEW, View3DMatrix

  'Neuen Ursprung der Viewport-Matrix einstellen
  gD3DDevice8.GetTransform D3DTS_VIEW, View3DMatrix
  TranslateMatrix View3DMatrix, _
        vec3(PLAYER.X * -1, PLAYER.Y * -1, PLAYER.Z * -1)
  gD3DDevice8.SetTransform D3DTS_VIEW, View3DMatrix

'--- Game-Loop ---
  Do
    PLAYER.TurnHor = 0#
    PLAYER.TurnVer = 0#
    PLAYER.Move = MOVE_NONE

    'Prüfen auf Anwender-Eingabe
    DIModule.CheckInput
    DIModule.CheckMouse
    With PLAYER
      'y-Achse rotieren
      If .TurnHor <> 0 Then
        .AngleY = .AngleY + (.TurnHor / 5)
        .Move = .Move Or MOVE_TURN
        If .AngleY < 0 Then
          .AngleY = 360 + .AngleY
        ElseIf .AngleY > 360 Then
          .AngleY = .AngleY - 360
        End If
      End If

      'x-Achse rotieren
      If .TurnVer <> 0 Then
        .AngleX = .AngleX + (.TurnVer / 10)
        If .AngleX > 50 Then .AngleX = 50
        If .AngleX < -50 Then .AngleX = -50
        .Move = .Move Or MOVE_TURN
      End If
    End With

    'Hat sich der Spieler überhaupt bewegt?
    If PLAYER.Move <> MOVE_NONE Then
      'neue Spielerposition berechnen
      CalculateViewStartPos

      'Matrix-Ursprung auf Spielerposition setzen
      TranslateMatrix myTranslateMatrix, _
            vec3(PLAYER.X * -1, PLAYER.Y * -1, PLAYER.Z * -1)

      'Matrix Y-Rotation
      RotateYMatrix myYMatrix, PLAYER.AngleY * PI / 180

      'Matrix X-Rotation
      RotateXMatrix myXMatrix, PLAYER.AngleX * PI / 180

      'Matrizen zusammenführen
      myNewMatrix = RetMatrixMult(myTranslateMatrix, myYMatrix)
      View3DMatrix = RetMatrixMult(myNewMatrix, myXMatrix)

      'Transformation der ViewPortMatrix durchführen
      gD3DDevice8.SetTransform D3DTS_VIEW, View3DMatrix
    End If

    '3D Szene rendern
    Render3D
    DoEvents
  Loop While running

'--- Berechnungen ---
  'Position des Spielers berechnen
Private Sub CalculateViewStartPos()
  Dim px As Single
  Dim pz As Single

  If (PLAYER.Move And MOVE_FORE) = MOVE_FORE Then
    px = Sin((PLAYER.AngleY * (PI / 180)))
    pz = Cos((PLAYER.AngleY * (PI / 180)))
    PLAYER.X = PLAYER.X + px
    PLAYER.Z = PLAYER.Z + pz
  End If

  If (PLAYER.Move And MOVE_BACK) = MOVE_BACK Then
    px = -Sin((PLAYER.AngleY * (PI / 180)))
    pz = -Cos((PLAYER.AngleY * (PI / 180)))
    PLAYER.X = PLAYER.X + px
    PLAYER.Z = PLAYER.Z + pz
  End If

  If (PLAYER.Move And MOVE_LEFT) = MOVE_LEFT Then
    px = Sin((PLAYER.AngleY * (PI / 180)))
    pz = -Cos((PLAYER.AngleY * (PI / 180)))
    PLAYER.X = PLAYER.X - pz * -1
    PLAYER.Z = PLAYER.Z - px * -1
  End If

  If (PLAYER.Move And MOVE_RIGHT) = MOVE_RIGHT Then
    px = Sin((PLAYER.AngleY * (PI / 180)))
    pz = -Cos((PLAYER.AngleY * (PI / 180)))
    PLAYER.X = PLAYER.X + pz * -1
    PLAYER.Z = PLAYER.Z + px * -1
  End If
End Sub

Public Sub TranslateMatrix(m As D3DMATRIX, v As D3DVECTOR)
  m.m41 = v.X
  m.m42 = v.Y
  m.m43 = v.Z
End Sub

Public Function RetMatrixMult(a As D3DMATRIX, b As D3DMATRIX) _
      As D3DMATRIX
  Dim ret As D3DMATRIX

  ret.m11 = b.m11 * a.m11 + b.m21 * a.m12 + b.m31 * a.m13 + _
      b.m41 * a.m14
  ret.m12 = b.m12 * a.m11 + b.m22 * a.m12 + b.m32 * a.m13 + _
      b.m42 * a.m14
  ret.m13 = b.m13 * a.m11 + b.m23 * a.m12 + b.m33 * a.m13 + _
      b.m43 * a.m14
  ret.m14 = b.m14 * a.m11 + b.m24 * a.m12 + b.m34 * a.m13 + _
      b.m44 * a.m14
  ret.m21 = b.m11 * a.m21 + b.m21 * a.m22 + b.m31 * a.m23 + _
      b.m41 * a.m24
  ret.m22 = b.m12 * a.m21 + b.m22 * a.m22 + b.m32 * a.m23 + _
      b.m42 * a.m24
  ret.m23 = b.m13 * a.m21 + b.m23 * a.m22 + b.m33 * a.m23 + _
      b.m43 * a.m24
  ret.m24 = b.m14 * a.m21 + b.m24 * a.m22 + b.m34 * a.m23 + _
      b.m44 * a.m24
  ret.m31 = b.m11 * a.m31 + b.m21 * a.m32 + b.m31 * a.m33 + _
      b.m41 * a.m34
  ret.m32 = b.m12 * a.m31 + b.m22 * a.m32 + b.m32 * a.m33 + _
      b.m42 * a.m34
  ret.m33 = b.m13 * a.m31 + b.m23 * a.m32 + b.m33 * a.m33 + _
      b.m43 * a.m34
  ret.m34 = b.m14 * a.m31 + b.m24 * a.m32 + b.m34 * a.m33 + _
      b.m44 * a.m34
  ret.m41 = b.m11 * a.m41 + b.m21 * a.m42 + b.m31 * a.m43 + _
      b.m41 * a.m44
  ret.m42 = b.m12 * a.m41 + b.m22 * a.m42 + b.m32 * a.m43 + _
      b.m42 * a.m44
  ret.m43 = b.m13 * a.m41 + b.m23 * a.m42 + b.m33 * a.m43 + _
      b.m43 * a.m44
  ret.m44 = b.m14 * a.m41 + b.m24 * a.m42 + b.m34 * a.m43 + _
      b.m44 * a.m44

  RetMatrixMult = ret
End Function

Public Sub RotateYMatrix(ByRef ret As D3DMATRIX, _
        ByVal rads As Single)
  Dim cosine As Single
  Dim sine As Single

  cosine = Cos(rads)
  sine = Sin(rads)
  ret.m11 = cosine
  ret.m33 = cosine
  ret.m13 = sine
  ret.m31 = -sine
End Sub

Public Sub RotateXMatrix(ByRef ret As D3DMATRIX, _
        ByVal rads As Single)
  Dim cosine As Single
  Dim sine As Single

  cosine = Cos(rads)
  sine = Sin(rads)
  ret.m22 = cosine
  ret.m33 = cosine
  ret.m23 = -sine
  ret.m32 = sine
End Sub

'Hilfsfunktion - einen D3DVektor erzeugen
Public Function vec3(X As Single, Y As Single, Z As Single) _
        As D3DVECTOR
  vec3.X = X
  vec3.Y = Y
  vec3.Z = Z
End Function
 
Hinweis
Um dieses Beispiel ausführen zu können, wird die DirectX 8 for Visual Basic Type Library benötigt (siehe dazu die Erläuterungen in der DirectX-Rubrik).

Windows-Version
95
98
ME
NT
2000
XP
Vista
Win 7
VB-Version
VBA 5
VBA 6
VB 4/16
VB 4/32
VB 5
VB 6


Download  (297 kB) Downloads bisher: [ 824 ]

Vorheriger Tipp Zum Seitenanfang Nächster Tipp

Startseite | Projekte | Tutorials | API-Referenz | VB-/VBA-Tipps | Komponenten | Bücherecke | VB/VBA-Forum | VB.Net-Forum | DirectX-Forum | Foren-Archiv | DirectX | VB.Net-Tipps | Chat | Spielplatz | Links | Suchen | Stichwortverzeichnis | Feedback | Impressum

Seite empfehlen Bug-Report
Letzte Aktualisierung: Sonntag, 4. September 2011