Bildschirmraum zum Weltraum

10

Ich schreibe ein 2D-Spiel, bei dem in meiner Spielwelt die x- Achse von links nach rechts, die y- Achse von oben nach unten und die z- Achse außerhalb des Bildschirms verläuft:

Geben Sie hier die Bildbeschreibung ein

Während meine Spielwelt von oben nach unten ist, wird das Spiel leicht geneigt gerendert:

Geben Sie hier die Bildbeschreibung ein

Ich arbeite daran, vom Weltraum auf den Bildschirmraum zu projizieren und umgekehrt. Ich habe die erstere wie folgt arbeiten:

var viewport = new Viewport(0, 0, this.ScreenWidth, this.ScreenHeight);
var screenPoint = viewport.Project(worldPoint.NegateY(), this.ProjectionMatrix, this.ViewMatrix, this.WorldMatrix);

Die NegateY()Erweiterungsmethode macht genau das, wonach es sich anhört, da die y- Achse von XNA von unten nach oben und nicht von oben nach unten verläuft. Der Screenshot oben zeigt, wie alles funktioniert. Grundsätzlich habe ich eine Reihe von Punkten im 3D-Raum, die ich dann im Bildschirmraum rendere. Ich kann die Kameraeigenschaften in Echtzeit ändern und sehen, wie sie an der neuen Position animiert werden. Natürlich werden in meinem eigentlichen Spiel eher Sprites als Punkte verwendet und die Kameraposition wird festgelegt, aber ich versuche nur, die gesamte Mathematik in Ordnung zu bringen, bevor ich dazu komme.

Jetzt versuche ich, zurück in die andere Richtung zu konvertieren. Das heißt, wenn ein x- und y- Punkt im Bildschirmbereich oben angegeben ist, bestimmen Sie den entsprechenden Punkt im Weltraum. Wenn ich also den Cursor beispielsweise auf die linke untere Ecke des grünen Trapezes zeige, möchte ich einen Weltraumwert von (0, 480) erhalten. Die z- Koordinate ist irrelevant. Oder besser gesagt, die z- Koordinate ist immer Null, wenn sie zurück in den Weltraum abgebildet wird. Im Wesentlichen möchte ich diese Methodensignatur implementieren:

public Vector2 ScreenPointToWorld(Vector2 point)

Ich habe verschiedene Dinge versucht, um dies zum Laufen zu bringen, aber ich habe einfach kein Glück. Mein neuester Gedanke ist, dass ich Viewport.Unprojectzweimal mit unterschiedlichen Nah / Fern- Z- Werten aufrufen , das Ergebnis berechnen Ray, normalisieren und dann den Schnittpunkt von Raymit einem berechnen muss, der im PlaneGrunde das Bodenniveau meiner Welt darstellt. Ich blieb jedoch beim letzten Schritt stecken und war mir nicht sicher, ob ich die Dinge zu kompliziert machte.

Kann mich jemand in die richtige Richtung weisen, wie dies erreicht werden kann?

mich--
quelle

Antworten:

4

Ich denke, Ihre Idee ist ziemlich genau richtig! Berechnen Sie zuerst einen Strahl für Ihren Cursor, indem Sie sowohl die nahe als auch die ferne Ebene als Z-Werte für Ihre 2D-Koordinaten verwenden (dh verwenden Sie 0 und 1 für Ihre Z-Koordinate). Hier ist eine Hilfsmethode, um damit umzugehen:

public Ray GetScreenRay(Vector2 screenPosition, Viewport viewport, Matrix projectionMatrix, Matrix viewMatrix, Matrix worldMatrix)
{
    Vector3 nearPoint = viewport.Unproject(new Vector3(screenPosition, 0f), projectionMatrix, viewMatrix, worldMatrix);
    Vector3 farPoint = viewport.Unproject(new Vector3(screenPosition, 1f), projectionMatrix, viewMatrix, worldMatrix);
    return new Ray(nearPoint, Vector3.Normalize(farPoint - nearPoint));
}

Als nächstes benötigen Sie eine PlaneInstanz, die Ihrem Boden entspricht. Der einfachste Weg, dies zu berechnen, besteht darin, den Konstruktor zu verwenden, der drei Punkte in der Ebene nimmt und drei beliebige Eckpunkte vom Bodenobjekt übergibt. Beispiel für die Berechnung der Grundebene:

Plane groundPlane = new Plane(ground.Vertices[0], ground.Vertices[1], ground.Vertices[2]);

Und schließlich finden Sie mit dieser Methode den Schnittpunkt zwischen dem Strahl und der Ebene . Mit dem Parameter out result können Sie die Schnittpunktkoordinaten wie im folgenden Beispiel berechnen:

float? result;
mouseRay.Intersects(ref groundPlane, out result);
if(result != null)
    Vector3 worldPoint = mouseRay.Position + mouseRay.Direction * result.Value;

Möglicherweise müssen Sie auch etwas gegen die NegateYMethode unternehmen, aber ich bin mir nicht sicher, wo.

David Gouveia
quelle
Das ist super hilfreich - vielen Dank. Es war die letzte Zeile, die mir fehlte. Ich konnte nicht herausfinden, was ich mit dem Schwimmer machen sollte, wenn ich ihn hatte! Ich denke, ich muss deinen Namen in meinen Spiel-Credits haben :)
ich--
@ user13414 In der Tat ist die Dokumentation zu dieser Methode etwas knapp und gibt kein Beispiel dafür, wie die Entfernung verwendet werden kann, um den Schnittpunkt zurückzugewinnen.
David Gouveia