Przestrzeń ekranu do przestrzeni świata

10

Piszę gra 2D, gdzie jest mój świat gry został x osi przebiegającej lewej do prawej, y osi biegnącej od góry do dołu i z osi z ekranu:

wprowadź opis zdjęcia tutaj

Podczas gdy mój świat gier jest odgórny, gra jest renderowana z lekkim przechyleniem:

wprowadź opis zdjęcia tutaj

Pracuję nad projektowaniem z przestrzeni świata na przestrzeń ekranu i odwrotnie. Mam ten pierwszy działający w następujący sposób:

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

Metoda NegateY()rozszerzenia działa dokładnie tak, jak to brzmi, ponieważ oś y XNA przebiega od dołu do góry zamiast od góry do dołu. Powyższy zrzut ekranu pokazuje, że wszystko działa. Zasadniczo mam kilka punktów w przestrzeni 3D, które następnie renderuję w przestrzeni ekranu. Mogę modyfikować właściwości kamery w czasie rzeczywistym i zobaczyć, jak animuje się w nowej pozycji. Oczywiście moja rzeczywista gra będzie wykorzystywała duszki zamiast punktów, a pozycja kamery zostanie ustalona, ​​ale ja po prostu próbuję ustawić całą matematykę na miejscu, zanim do tego dojdę.

Teraz staram się przekonwertować w drugą stronę. To znaczy, biorąc pod uwagę x i y punkt na powierzchni ekranu powyżej, określenie odpowiedniego punktu w przestrzeni światowej. Więc jeśli wskażę kursor, powiedzmy, lewy dolny lewy zielony trapezoid, chcę uzyskać odczyt przestrzeni świata (0, 480). Z współrzędnych znaczenia. Lub raczej współrzędna z będzie zawsze równa zero podczas odwzorowywania z powrotem do przestrzeni świata. Zasadniczo chcę zaimplementować tę sygnaturę metody:

public Vector2 ScreenPointToWorld(Vector2 point)

Próbowałem kilku rzeczy, aby to zadziałało, ale po prostu nie mam szczęścia. Moje ostatnie zdanie jest takie, że muszę zadzwonićViewport.Unproject dwa razy z różnymi blisko / daleko Z wartości, obliczyć wypadkową Ray, normalizują, a potem obliczyć przecięcia Rayz Planektóry zasadniczo reprezentuje przyziemnej mojego świata. Jednak utknąłem na ostatnim etapie i nie byłem pewien, czy nadmiernie komplikuję sprawy.

Czy ktoś może wskazać mi właściwy kierunek, w jaki sposób to osiągnąć?

mnie--
źródło

Odpowiedzi:

4

Myślę, że twój pomysł jest dość trafny! Najpierw obliczyć promień dla kursora, używając zarówno płaszczyzny bliskiej, jak i dalekiej jako wartości Z dla współrzędnych 2D (tj. Użyj 0 i 1 dla współrzędnej Z). Oto metoda pomocnicza do obsługi tego:

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));
}

Następnie musisz mieć Planeinstancję, która pasuje do twojego podłoża. Najłatwiejszym sposobem obliczenia tego jest użycie konstruktora, który pobiera trzy punkty na płaszczyźnie i przekazuje go dowolne trzy wierzchołki od obiektu naziemnego. Przykład obliczania płaszczyzny podłoża:

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

I wreszcie znajdź punkt przecięcia promienia z płaszczyzną za pomocą tej metody . Możesz użyć parametru wyniku wyjściowego, aby obliczyć współrzędne przecięcia, jak w poniższym przykładzie:

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

Być może będziesz musiał coś zrobić z tą NegateYmetodą, ale nie jestem pewien, gdzie.

David Gouveia
źródło
Jest to bardzo pomocne - dziękuję bardzo. To był ostatni wiersz, którego mi brakowało. Nie miałem pojęcia, co zrobić z pływakiem, kiedy go miałem! Myślę, że będę musiał podać twoje imię w kredytach z gry :)
ja--
@ user13414 Rzeczywiście, dokumentacja jest nieco rzadka w tej metodzie i nie podaje żadnego przykładu wykorzystania odległości do odzyskania punktu przecięcia.
David Gouveia