Jak narysować strzałkę na krawędzi ekranu wskazującą obiekt znajdujący się poza ekranem?

12

Chcę zrobić to, co opisano w tym temacie:

http://www.allegro.cc/forums/print-thread/283220

Próbowałem różnych metod wymienionych tutaj.

Najpierw próbowałem użyć metody opisanej przez Carrus85:

Po prostu weź stosunek dwóch trójkątów przeciwprostokątnych (nie ma znaczenia, którego trójkąta użyjesz dla drugiego, sugeruję punkt 1 i punkt 2 jako obliczaną odległość). To da ci procent proporcji trójkąta w rogu od większego trójkąta. Następnie wystarczy pomnożyć deltax przez tę wartość, aby uzyskać przesunięcie współrzędnej x, i opóźnić przez tę wartość, aby uzyskać przesunięcie współrzędnej y.

Ale nie mogłem znaleźć sposobu, aby obliczyć, jak daleko obiekt znajduje się od krawędzi ekranu.

Następnie spróbowałem użyć rzutowania promieniowego (czego nigdy wcześniej nie robiłem) sugerowanego przez 23yrold3yrold:

Wystrzel promień ze środka ekranu na obiekt poza ekranem. Oblicz, gdzie w prostokącie przecina się promień. Oto twoje współrzędne.

Najpierw obliczyłem przeciwprostokątną trójkąta utworzoną przez różnicę w pozycjach xiy dwóch punktów. Użyłem tego, aby utworzyć wektor jednostkowy wzdłuż tej linii. Zapętlałem ten wektor, dopóki współrzędna x lub y nie znalazła się poza ekranem. Dwie bieżące wartości xiy tworzą następnie xiy strzałki.

Oto kod mojej metody rzutowania promieniami (napisany w C ++ i Allegro 5)

void renderArrows(Object* i)
{
    float x1 = i->getX() + (i->getWidth() / 2);
    float y1 = i->getY() + (i->getHeight() / 2);

    float x2 = screenCentreX;
    float y2 = ScreenCentreY;

    float dx = x2 - x1;
    float dy = y2 - y1;
    float hypotSquared = (dx * dx) + (dy * dy);
    float hypot = sqrt(hypotSquared);

    float unitX = dx / hypot;
    float unitY = dy / hypot;

    float rayX = x2 - view->getViewportX();
    float rayY = y2 - view->getViewportY();
    float arrowX = 0;
    float arrowY = 0;

    bool posFound = false;
    while(posFound == false)
    {
        rayX += unitX;
        rayY += unitY;

        if(rayX <= 0 ||
            rayX >= screenWidth ||
            rayY <= 0 ||
            rayY >= screenHeight)
        {
            arrowX = rayX;
            arrowY = rayY;
            posFound = true;
        }               
    }

    al_draw_bitmap(sprite, arrowX - spriteWidth, arrowY - spriteHeight, 0);
}

To było względnie udane. Strzały są wyświetlane w prawej dolnej części ekranu, gdy obiekty znajdują się nad i po lewej stronie ekranu, tak jakby miejsca, w których narysowane są strzałki, zostały obrócone o 180 stopni wokół środka ekranu.

Założyłem, że było to spowodowane faktem, że kiedy obliczałem przeciwprostokątną trójkąta, zawsze będzie ona dodatnia, niezależnie od tego, czy różnica x lub różnica y jest ujemna.

Myśląc o tym, rzutowanie promieniami nie wydaje się dobrym sposobem na rozwiązanie problemu (z uwagi na fakt, że wymaga użycia sqrt () i dużej pętli for).

Adam Henderson
źródło

Odpowiedzi:

6

Więc masz dwie współrzędne lub wektory, jeden jest środkiem ekranu (C od teraz), a drugi jest twoim obiektem (P od teraz).

Jeśli znasz matematykę, możesz wiedzieć, że linię można wyrazić jako początek i wektor kierunku. Początek jest środkiem ekranu, podczas gdy wektor kierunku można znaleźć odejmując C od P. To równanie można również wyrazić w postaci parametrycznej, która jest zasadniczo taka sama:

x = (P.x - C.x)t + C.x;
y = (P.y - C.y)t + C.y;

Widzisz (P.? - C.?)kawałek? To twój wektor kierunku (jak powiedziałem, odejmij C od P). Ostatni C.?bit to początek linii.

tjest zmienną, która może się różnić od 0do 1, 0będąc początkiem wektora (jeśli działasz , xa ywoulld stał się C.xi C.y), 1będąc współrzędną obiektu (ponownie, działając, stałby się P.xi P.y, lub „końcem” wektora, jeśli chcesz) i wartości pomiędzy interpolacją między dwoma końcami segmentu. Możesz także przypisać wartości zewnętrzne: poniżej 0zmienisz kierunek wektora, a powyżej 1„rozszerzysz” wektor w tym samym kierunku.

Kiedy już to dostaniesz, stanie się całkiem łatwe. Twoim celem jest znalezienie punktu tego wektora ( xi ydla danej wartości t), gdzie X=WIDTHlub Y=HEIGHTcokolwiek będzie pierwsze. Jak widać, tjest twoją jedyną zmienną tutaj:

(0)
WIDTH = (P.x - C.x)t + C.x;
and
HEIGHT = (P.y - C.y)t + C.y;

Lub ponownie wyrażając to:

(1)
t = (WIDTH - C.x)/(P.x - C.x)
and
t = (HEIGHT - C.y)/(P.y - C.y)

Spowoduje to uzyskanie punktu cięcia linii zdefiniowanej przez wektor na prawej i górnej granicy. To samo dotyczy lewej i dolnej krawędzi ekranu, na której musisz sprawdzić 0w obu przypadkach, WIDTHa nie i HEIGHT.

Ponieważ ostatecznie tspowoduje to przekroczenie granic, nawet poza ekranem, najniższa wartość będzie Twoim pierwszym punktem kontaktu. Odwrócenie operacji i zastosowanie znalezionej twartości do równań w (0)(ta sama wartość dla obu!) Przyniesie nowy (x,y), który będzie twoimi współrzędnymi cięcia.

Mogą występować błędy matematyczne lub różnice w implementacji problemu, ale to podstawowa idea. Pominąłem też niektóre części (zawsze są cztery przypadki cięcia, a ty potrzebujesz tylko jednego), ale trochę przemyśleń doprowadzi cię do ostatecznego rozwiązania :)

kaoD
źródło
Dzięki. Spróbuję. EDYCJA: Czy z ciekawości uważasz, że ta metoda jest tą, którą opisał Carrus85 (używając stosunku przeciwprostokątnej)?
Adam Henderson
1
@AdamHenderson Cieszę się, że mogę pomóc :) Pamiętaj, że możesz zachować wektor kierunku, aby później narysować strzałkę. Możesz go znormalizować, aby uzyskać wektor jednolitego kierunku, odjąć go arrow-lengthod wektora cięcia „et voila”, masz strzałkę i początek.
kaoD
1
@AdamHenderson wizualnie to to samo, ponieważ twoja linia jest przeciwprostokątną, o której mówi. Praktycznie to nie to samo, ponieważ jego sugestia dotyczy kątów (a zatem trygonometrii), co moim zdaniem jest do tego przesadą. W ogóle nie dotyczy to trójkątów (chociaż można myśleć o wektorze jako o trójkącie, w którym przeciwprostokątna jest wektorem, a obie strony są składowymi xi y).
kaoD
Dzięki jeszcze raz! Rozwiązałeś mój następny problem polegający na skierowaniu strzałki we właściwym kierunku.
Adam Henderson
1
@AdamHenderson tak, dolny, jeśli oś jest odwrócona. BTW, proponuję zamieścić link do tego pytania na forum Allegro w celu późniejszego wykorzystania.
kaoD