Tipp 0348 Direct3D - Bewegen im 3D-Raum
Autor/Einsender:
Datum:
  Alexander Csadek
25.07.2003
Entwicklungsumgebung:
DirectX-Version:
  VB 6
DirectX 7
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 der Kamera im 3D-Raum und der zweite Vektor das Ziel 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 im 3D-Raum sie sich 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 schau wohin ich geh, oder andersrum, ich geh wohin ich schau. Um das in einem 3D-Spiel zu realisieren, braucht man einen Blickwinkel, 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 Zielposition der Kamera berechnet und die Kamera (= ViewPort) neu gesetzt, oder wir verwenden die Matrizen-Funktionen.
Der Tipp zeigt wie die Kamera (ViewPort) im 3D-Raum unter Verwendung der Matrizen-Funktionen bewegt werden kann. Hierbei muss der Blickpunkt des 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. Wird dann die Matrix als neuer ViewPort an Direct3D übergeben, wird der Blickpunkt 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.
Es folgen nun die wichtigsten Code-Bereiche...
Code im Direct3D Erstellungs-Bereich
 
   'ViewportMatrix einstellen
  With DX7
    .IdentityMatrix View3DMatrix
    .ViewMatrix View3DMatrix, RVector(0, 0, 0), _
        RVector(0, 0, 1000), RVector(0, 1, 0), 0
  End With
  Device.SetTransform D3DTRANSFORMSTATE_VIEW, View3DMatrix

  'neuen Ursprung der ViewportMatrix einstellen
  Device.GetTransform D3DTRANSFORMSTATE_VIEW, View3DMatrix
  TranslateMatrix View3DMatrix, RVector( _
        PLAYER.x * -1, PLAYER.y * -1, PLAYER.z * -1)
  Device.SetTransform D3DTRANSFORMSTATE_VIEW, View3DMatrix
 
Code im Game-Loop
 
  Do
    PLAYER.TurnHor = 0#
    PLAYER.TurnVer = 0#
    PLAYER.Move = MOVE_NONE
    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, RVector( _
            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
      Device.SetTransform D3DTRANSFORMSTATE_VIEW, View3DMatrix
    End If
    'Szene rendern
    Render3D
    BackBuffer.BltFast 394, 294, bmpCross, recCross, _
        DDBLTFAST_WAIT Or DDBLTFAST_SRCCOLORKEY
    PrimarySurface.Flip BackBuffer, DDFLIP_WAIT
    DoEvents
  Loop While Running
 
Berechnungen
 
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))) / 2
    pz = Cos((PLAYER.AngleY * (PI / 180))) / 2
    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))) / 2
    pz = -Cos((PLAYER.AngleY * (PI / 180))) / 2
    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))) / 2
    pz = -Cos((PLAYER.AngleY * (PI / 180))) / 2
    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))) / 2
    pz = -Cos((PLAYER.AngleY * (PI / 180))) / 2
    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.rc41 = v.x
  m.rc42 = v.y
  m.rc43 = v.z
End Sub

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

  Dim ret As D3DMATRIX

  ret.rc11 = b.rc11 * a.rc11 + b.rc21 * a.rc12 + _
             b.rc31 * a.rc13 + b.rc41 * a.rc14
  ret.rc12 = b.rc12 * a.rc11 + b.rc22 * a.rc12 + _
             b.rc32 * a.rc13 + b.rc42 * a.rc14
  ret.rc13 = b.rc13 * a.rc11 + b.rc23 * a.rc12 + _
             b.rc33 * a.rc13 + b.rc43 * a.rc14
  ret.rc14 = b.rc14 * a.rc11 + b.rc24 * a.rc12 + _
             b.rc34 * a.rc13 + b.rc44 * a.rc14
  ret.rc21 = b.rc11 * a.rc21 + b.rc21 * a.rc22 + _
             b.rc31 * a.rc23 + b.rc41 * a.rc24
  ret.rc22 = b.rc12 * a.rc21 + b.rc22 * a.rc22 + _
             b.rc32 * a.rc23 + b.rc42 * a.rc24
  ret.rc23 = b.rc13 * a.rc21 + b.rc23 * a.rc22 + _
             b.rc33 * a.rc23 + b.rc43 * a.rc24
  ret.rc24 = b.rc14 * a.rc21 + b.rc24 * a.rc22 + _
             b.rc34 * a.rc23 + b.rc44 * a.rc24
  ret.rc31 = b.rc11 * a.rc31 + b.rc21 * a.rc32 + _
             b.rc31 * a.rc33 + b.rc41 * a.rc34
  ret.rc32 = b.rc12 * a.rc31 + b.rc22 * a.rc32 + _
             b.rc32 * a.rc33 + b.rc42 * a.rc34
  ret.rc33 = b.rc13 * a.rc31 + b.rc23 * a.rc32 + _
             b.rc33 * a.rc33 + b.rc43 * a.rc34
  ret.rc34 = b.rc14 * a.rc31 + b.rc24 * a.rc32 + _
             b.rc34 * a.rc33 + b.rc44 * a.rc34
  ret.rc41 = b.rc11 * a.rc41 + b.rc21 * a.rc42 + _
             b.rc31 * a.rc43 + b.rc41 * a.rc44
  ret.rc42 = b.rc12 * a.rc41 + b.rc22 * a.rc42 + _
             b.rc32 * a.rc43 + b.rc42 * a.rc44
  ret.rc43 = b.rc13 * a.rc41 + b.rc23 * a.rc42 + _
             b.rc33 * a.rc43 + b.rc43 * a.rc44
  ret.rc44 = b.rc14 * a.rc41 + b.rc24 * a.rc42 + _
             b.rc34 * a.rc43 + b.rc44 * a.rc44

  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.rc11 = cosine
  ret.rc33 = cosine
  ret.rc13 = sine
  ret.rc31 = -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.rc22 = cosine
  ret.rc33 = cosine
  ret.rc23 = -sine
  ret.rc32 = sine
End Sub

Public Function RVector(ByVal x As Single, ByVal y As Single, _
      ByVal z As Single) As D3DVECTOR
  Dim v As D3DVECTOR

  v.x = x
  v.y = y
  v.z = z
  RVector = v
End Function
 
Weitere Links zum Thema
Objekte unabhängig im 3D-Raum bewegen
Hinweis
Um dieses Beispiel ausführen zu können, wird die DirectX 7 for Visual Basic Type Library benötigt (siehe dazu die Erläuterungen in der DirectX-Rubrik).

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


Download  (257 kB) Downloads bisher: [ 3021 ]

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: Freitag, 19. August 2011