3D Kamera basierend der Rotation bewegen

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 6 Antworten in diesem Thema. Der letzte Beitrag () ist von PadreSperanza.

    3D Kamera basierend der Rotation bewegen

    Wie im Titel steht, würde ich gerne eine Kamera in einem Dreidimensionalen Raum basierend der momentenen Rotation der Kamera frei bewegen können.

    Den Code der für die Rotation der Kamera zuständig ist habe ich bereits erledigt:

    C#-Quellcode

    1. // Cam Rotation X/Y Values
    2. if (mouse.Movement.Y < 0f) { // Down
    3. if (!(freeCamera.Rotation.X >= maxAngle - 0.01f)) mouseMovementYStored += topDownlookSpeed * frameTime;
    4. }
    5. if (mouse.Movement.Y > 0f) { // Top
    6. if (!(freeCamera.Rotation.X <= minAngle + 0.01f)) mouseMovementYStored -= topDownlookSpeed * frameTime;
    7. }
    8. if (mouse.Movement.X > 0f) { // Right
    9. mouseMovementXStored += leftRightlookSpeed * frameTime;
    10. }
    11. if (mouse.Movement.X < 0f) { // Left
    12. mouseMovementXStored -= leftRightlookSpeed * frameTime;
    13. }
    14. // Rotate Cam based on Mouse Movement
    15. Matrix camRotMatrixZ = Matrix.RotationZ(mouseMovementXStored * 5f); // Mouse Left/Right
    16. Matrix camRotMatrixX = Matrix.RotationX(mouseMovementYStored * 5f); // Mouse Top/Down
    17. float camRotVectorZ = Quaternion.RotationMatrix(camRotMatrixZ).ToRotation().Z;
    18. float camRotVectorX = Quaternion.RotationMatrix(camRotMatrixX).ToRotation().X;
    19. camRotVectorX = Clamp(camRotVectorX, minAngle, maxAngle);
    20. freeCamera.Rotation = new Vector3(camRotVectorX, 0f, -camRotVectorZ);


    Im moment habe ich nur das "Basic Movement" was nicht basierend der Rotation arbeitet bedeutet wenn ich die "W" Taste gedrückt halte, fährt die Kamera weiter nach Norden obwohl ich nach Süden schaue. Das gleich passiert mit Westen und Osten.

    C#-Quellcode

    1. if (isUpKeyPressed2) freeCamera.Position += Vector3.WorldNorth * (movementSpeed * frameTime); // Forward
    2. if (isDownKeyPressed2) freeCamera.Position += Vector3.WorldSouth * (movementSpeed * frameTime); // Backward
    3. if (isLeftKeyPressed2) freeCamera.Position += Vector3.WorldWest * (movementSpeed * frameTime); // Left
    4. if (isRightKeyPressed2) freeCamera.Position += Vector3.WorldEast * (movementSpeed * frameTime); // Right


    Es wäre toll wenn mir jemand bei der Sache weiterhelfen kann, denn ich erziele einfach nicht den Effekt den ich mir vorstelle.
    Wenn ich dir auf irgendeiner Art und Weise helfen konnte, drück doch bitte den "Hilfreich" Button :thumbup:

    Für VB.NET Entwickler: Option Strict On nicht vergessen!
    @ClonkAndre In diesem Sinne ist der Code verwirrend.
    Beschreib mal verbal, was da passieren soll, vielleicht mit einem Bildchen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @RodFromGermany
    Ok, ich habe hier ein Beispiel Objekt welches standardmäßig in Richtung Norden schaut. Sagen wir mal, dass das unsere Kamera in der Welt ist.



    Ich kann mittels diesem Code die Kamera in alle Richtungen Rotieren wenn ich die Maus bewege (Pitch und Yaw. Roll ist ausgeschlossen).

    C#-Quellcode

    1. // Rotate Cam based on Mouse Movement
    2. Matrix camRotMatrixZ = Matrix.RotationZ(mouseMovementXStored * 5f); // Mouse Left/Right
    3. Matrix camRotMatrixX = Matrix.RotationX(mouseMovementYStored * 5f); // Mouse Top/Down
    4. float camRotVectorZ = Quaternion.RotationMatrix(camRotMatrixZ).ToRotation().Z;
    5. float camRotVectorX = Quaternion.RotationMatrix(camRotMatrixX).ToRotation().X;
    6. camRotVectorX = Clamp(camRotVectorX, minAngle, maxAngle); // Limit Pitch to a certain angle
    7. freeCamera.Rotation = new Vector3(camRotVectorX, 0f, -camRotVectorZ); // Set Camera Rotation


    Nun habe ich diesen Code, mit dem wir die Kamera in jede Richtung bewegen können (Vor (Norden), Zurück (Süden), Links (Westen) und Rechts (Osten)).

    C#-Quellcode

    1. if (isUpKeyPressed2) freeCamera.Position += Vector3.WorldNorth * (movementSpeed * frameTime); // Forward
    2. if (isDownKeyPressed2) freeCamera.Position += Vector3.WorldSouth * (movementSpeed * frameTime); // Backward
    3. if (isLeftKeyPressed2) freeCamera.Position += Vector3.WorldWest * (movementSpeed * frameTime); // Left
    4. if (isRightKeyPressed2) freeCamera.Position += Vector3.WorldEast * (movementSpeed * frameTime); // Right


    Das Problem mit diesem Code ist, das sich die Kamera nicht in der Richtung bewegt in die sie schaut.
    Dies bedeutet: Die Kamera schaut nach Westen, ich drücke W, weil ich möchte die Kamera nach Westen hin bewegen da diese in die Richtung schaut, die Kamera bewegt sich aber trotzem nach Norden obwohl sie sich nach Westen hin bewegen soll weil sie in diese Richtung schaut.

    Ich würde es gerne erreichen das sich die Kamera in jede Richtung bewegen kann in die sie gerade schaut wenn man W (Nach vorne fahren) drückt.
    Da weiß ich leider nicht mehr weiter
    Wenn ich dir auf irgendeiner Art und Weise helfen konnte, drück doch bitte den "Hilfreich" Button :thumbup:

    Für VB.NET Entwickler: Option Strict On nicht vergessen!
    da gibt es mehrere Ansätze, die man fahren kann. Ein recht simpel gehaltener wäre folgender:

    Wenn du rotierst, dann speicherst du den Winkel, um den du insgesamt rotiert bist. Diesen Winkel nutzt du dann, wenn du mittels "W" nach vorne fährst und fährst nicht einfach nur in Richtung vorne, sondern in Richtung vorne multipliziert mit dem Winkel, den du gespeichert hast. Rein mathematisch sieht es etwas komplizierter aus. Grundwissen in Trigonometrie ist hier hilfreich.
    @ClonkAndre Ich glaube, Du machst Dir Dein Leben unnötig schwer.
    Wenn Du mit der MAus arbeitest, bekommst Du stets Quer-Koordinaten rein.
    Mach Dir eine kleine Test-GUI, wo Du jede Richtung einzeln ändern kannst, dann solltest Du schnell sehen, wo sich die Fehler einschleichen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Okay ich habe es hinbekommen! @PadreSperanza hat mir nochmal einen kleinen Gedanken-Schub gegeben, und mir ist eingefallen, das die Kamera Klasse mir einen Direction Vector zur verfügung stellt den ich natürlich die ganze Zeit ignoriert habe..

    Die Vorwärts und Rückwärts Bewegung der Kamera basierend der aktuellen Rotation sieht nun so aus:

    C#-Quellcode

    1. if (isUpKeyPressed2) {
    2. freeCamera.Position += Vector3.WorldNorth * freeCamera.Direction.Y * (movementSpeed * frameTime);
    3. freeCamera.Position += Vector3.WorldWest * -freeCamera.Direction.X * (movementSpeed * frameTime);
    4. }
    5. if (isDownKeyPressed2) {
    6. freeCamera.Position += Vector3.WorldNorth * -freeCamera.Direction.Y * (movementSpeed * frameTime);
    7. freeCamera.Position += Vector3.WorldWest * freeCamera.Direction.X * (movementSpeed * frameTime);
    8. }


    Auf die Links und Rechts Bewegung der Kamera basierend der aktuellen Rotation bin ich nicht so stolz, sieht für mich ziemlich ineffizient aus aber naja, es erfüllt seinen Job relativ gut.
    Spoiler anzeigen

    C#-Quellcode

    1. private Direction GetDirection(Vector3 target)
    2. {
    3. Vector3 normalizedVector = new Vector3(target.X, target.Y, target.Z);
    4. normalizedVector.Normalize();
    5. if (Math.Abs(target.X) > Math.Abs(target.Y)) {
    6. if (normalizedVector.X >= 0.1) {
    7. //East
    8. return Direction.East;
    9. }
    10. else if (normalizedVector.X <= -0.1) {
    11. //West
    12. return Direction.West;
    13. }
    14. else { return Direction.Unknown; }
    15. }
    16. else if (Math.Abs(target.X) < Math.Abs(target.Y)) {
    17. if (normalizedVector.Y >= 0.1) {
    18. //North
    19. return Direction.North;
    20. }
    21. if (normalizedVector.Y <= -0.1) {
    22. //South
    23. return Direction.South;
    24. }
    25. else { return Direction.Unknown; }
    26. }
    27. else { return Direction.Unknown; }
    28. }
    29. Direction camDir = GetDirection(freeCamera.Direction);
    30. if (isLeftKeyPressed2) {
    31. switch (camDir) {
    32. case Direction.North:
    33. freeCamera.Position += Vector3.WorldWest * (movementSpeed * frameTime);
    34. break;
    35. case Direction.East:
    36. freeCamera.Position += Vector3.WorldNorth * (movementSpeed * frameTime);
    37. break;
    38. case Direction.South:
    39. freeCamera.Position += Vector3.WorldEast * (movementSpeed * frameTime);
    40. break;
    41. case Direction.West:
    42. freeCamera.Position += Vector3.WorldSouth * (movementSpeed * frameTime);
    43. break;
    44. }
    45. }
    46. if (isRightKeyPressed2) {
    47. switch (camDir) {
    48. case Direction.North:
    49. freeCamera.Position += Vector3.WorldEast * (movementSpeed * frameTime);
    50. break;
    51. case Direction.East:
    52. freeCamera.Position += Vector3.WorldSouth * (movementSpeed * frameTime);
    53. break;
    54. case Direction.South:
    55. freeCamera.Position += Vector3.WorldWest * (movementSpeed * frameTime);
    56. break;
    57. case Direction.West:
    58. freeCamera.Position += Vector3.WorldNorth * (movementSpeed * frameTime);
    59. break;
    60. }
    61. }


    Wenn ich dir auf irgendeiner Art und Weise helfen konnte, drück doch bitte den "Hilfreich" Button :thumbup:

    Für VB.NET Entwickler: Option Strict On nicht vergessen!

    ClonkAndre schrieb:

    Auf die Links und Rechts Bewegung der Kamera basierend der aktuellen Rotation bin ich nicht so stolz, sieht für mich ziemlich ineffizient aus aber naja, es erfüllt seinen Job relativ gut.


    Ja, kann ich verstehen. Um hier nochmal einen Ansatz zu geben: Es gibt bei Vektor-Rechnung auch eine Methode, um einen Vektor zu finden, der exakt Senkrecht zu dem eigentlichen Vektor steht => Stichwort Skalarprodukt.

    Wenn du diese Information damit verbindest, dass die Richtungen links und rechts ja genau senkrecht (orthogonal) zu deinem Vektor stehen, der für nach vorne oder hinten zuständig ist, solltest du hiermit auch weiterkommen.

    C#-Quellcode

    1. if (isUpKeyPressed2) {
    2. freeCamera.Position += Vector3.WorldNorth * freeCamera.Direction.Y * (movementSpeed * frameTime);
    3. freeCamera.Position += Vector3.WorldWest * -freeCamera.Direction.X * (movementSpeed * frameTime);
    4. }
    5. if (isDownKeyPressed2) {
    6. freeCamera.Position += Vector3.WorldNorth * -freeCamera.Direction.Y * (movementSpeed * frameTime);
    7. freeCamera.Position += Vector3.WorldWest * freeCamera.Direction.X * (movementSpeed * frameTime);
    8. }


    Im übrigen solltest du genau aus diesem Grund auch nochmal diese Funktion überarbeiten. Das einzige, das hier tatsächlich relevant ist, ist das Vorzeichen, das sich bei deinen freeCamera.Direction... ergeben. Sie sind genau umgekehrt - was auch logisch ist, denn eine Vorwärtsbewegung ist zeitgleich eine negative Rückwärtsbewegung. Du könntest also den Code dahingehend verschlanken, dass du nur einmal die Anweisung freeCamera.Position für WorldNorth und einmal für WorldWest brauchst. Abhängig vom isDownKeyPressed müsstest du nur noch das Vorzeichen setzen. der Rest ist exakt gleich.

    Und wenn du dann noch das Skalarprodukt für links und rechts verwurschtelst, wirst du feststellen, dass sich sogar noch einiges vereinfachen lassen dürfte.