Wie kann ich einen Pfeil am Rand des Bildschirms zeichnen, der auf ein Objekt zeigt, das sich außerhalb des Bildschirms befindet?

12

Ich möchte das tun, was in diesem Thema beschrieben wird:

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

Ich habe eine Vielzahl der hier genannten Methoden ausprobiert.

Zuerst habe ich versucht, die von Carrus85 beschriebene Methode anzuwenden:

Nimm einfach das Verhältnis der beiden Dreieckshyponten (egal welches Dreieck du für das andere verwendest, ich schlage Punkt 1 und Punkt 2 als berechneten Abstand vor). Dadurch erhalten Sie das prozentuale Seitenverhältnis des Dreiecks in der Ecke zum größeren Dreieck. Dann multiplizieren Sie einfach Deltax mit diesem Wert, um den x-Koordinatenversatz zu erhalten, und Deltay mit diesem Wert, um den y-Koordinatenversatz zu erhalten.

Ich konnte jedoch nicht berechnen, wie weit das Objekt vom Bildschirmrand entfernt ist.

Ich habe dann versucht, Ray Casting zu verwenden (was ich noch nie zuvor gemacht habe), vorgeschlagen von 23yrold3yrold:

Feuern Sie einen Strahl von der Mitte des Bildschirms auf das Objekt außerhalb des Bildschirms. Berechnen Sie, wo sich der Strahl auf dem Rechteck schneidet. Da sind deine Koordinaten.

Ich habe zunächst die Hypotenuse des Dreiecks berechnet, die sich aus der Differenz der x- und y-Positionen der beiden Punkte ergibt. Ich habe dies verwendet, um einen Einheitsvektor entlang dieser Linie zu erstellen. Ich durchlief diesen Vektor, bis entweder die x-Koordinate oder die y-Koordinate nicht mehr auf dem Bildschirm zu sehen war. Die beiden aktuellen x- und y-Werte bilden dann das x und y des Pfeils.

Hier ist der Code für meine Ray Casting Methode (geschrieben in C ++ und 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);
}

Dies war relativ erfolgreich. Pfeile werden im unteren rechten Bereich des Bildschirms angezeigt, wenn sich Objekte über und links vom Bildschirm befinden, als ob die Stellen, an denen die Pfeile gezeichnet werden, um 180 Grad um die Mitte des Bildschirms gedreht worden wären.

Ich nahm an, dass dies auf die Tatsache zurückzuführen war, dass die Hypotenuse des Dreiecks bei der Berechnung immer positiv war, unabhängig davon, ob die Differenz in x oder die Differenz in y negativ ist oder nicht.

Wenn man darüber nachdenkt, scheint Ray Casting keine gute Möglichkeit zu sein, das Problem zu lösen (aufgrund der Tatsache, dass sqrt () und eine große for-Schleife verwendet werden).

Adam Henderson
quelle

Antworten:

6

Sie haben also zwei Koordinaten oder Vektoren, eine ist die Mitte des Bildschirms (C ab jetzt) ​​und die andere ist Ihr Objekt (P ab jetzt).

Wenn Sie sich mit Mathematik auskennen, wissen Sie vielleicht, dass eine Linie als Ursprung und Richtungsvektor ausgedrückt werden kann. Der Ursprung ist Ihre Bildschirmmitte, während der Richtungsvektor C von P subtrahiert. Diese Gleichung kann auch in parametrischer Form ausgedrückt werden, die im Wesentlichen dieselbe ist:

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

Siehst (P.? - C.?)du das bisschen? Das ist dein Richtungsvektor (wie gesagt, subtrahiere C von P). Das letzte C.?Bit ist der Ursprung der Linie.

tist eine Variable, die von 0bis variieren kann 1. Sie 0ist der Ursprung des Vektors (wenn Sie operieren xund ywoulld werden C.xund C.y), 1Ihre Objektkoordinate (wieder operieren, würde es werden P.xund P.yoder das "Ende" des Vektors). wenn Sie möchten) und Werte dazwischen, die zwischen beiden Enden Ihres Segments interpolieren. Sie können auch äußere Werte zuweisen: Darunter 0wird die Vektorrichtung umgekehrt, und darüber 1wird der Vektor in dieselbe Richtung "erweitert".

Sobald Sie dies erhalten, wird es ziemlich einfach. Ihr Ziel ist es, den Punkt dieses Vektors ( xund yfür einen gegebenen Wert von t) zu finden, wo X=WIDTHoder Y=HEIGHTwas zuerst kommt. Wie Sie sehen können, tist Ihre einzige Variable hier:

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

Oder es neu ausdrücken:

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

Dadurch wird der Schnittpunkt der von Ihrem Vektor definierten Linie am rechten und oberen Rand angezeigt. Das Gleiche gilt für den linken und unteren Rand Ihres Bildschirms, an dem Sie in 0beiden Fällen prüfen müssen , nicht WIDTHund HEIGHT.

Der niedrigste tWert ist Ihr erster Ansprechpartner, da er möglicherweise die Ränder schneidet, auch außerhalb des Bildschirms . Wenn Sie die Operation umkehren und Ihren gefundenen tWert auf die Gleichungen unter (0)(für beide den gleichen Wert!) Anwenden, erhalten Sie eine neue (x,y), die Ihre Schnittkoordinaten sind.

Möglicherweise gibt es einige mathematische Fehler oder Implementierungsunterschiede für Ihr Problem, aber das ist die Grundidee. Ich habe auch einige Teile weggelassen (es gibt immer vier Fälle des Schneidens, und Sie brauchen nur einen), aber ein bisschen Nachdenken bringt Sie zu einer endgültigen Lösung :)

kaoD
quelle
Vielen Dank. Ich werde es versuchen. EDIT: Glauben Sie, dass diese Methode aus Neugier die von Carrus85 beschriebene ist (unter Verwendung des Verhältnisses der Hypotenuse)?
Adam Henderson
1
@AdamHenderson Ich helfe Ihnen gerne :) Denken Sie daran, dass Sie Ihren Richtungsvektor beibehalten können, damit Sie Ihren Pfeil später zeichnen können. Sie können ihn normalisieren, um Ihren einheitlichen Richtungsvektor zu erhalten. Subtrahieren Sie ihn arrow-lengthmal vom Schnittvektor "et voila". Sie haben einen Ursprung und ein Ziel für Ihren Pfeil.
KaoD
1
@AdamHenderson visuell ist es das gleiche, da Ihre Linie die Hypotenuse ist, über die er spricht. Praktisch ist es nicht dasselbe, da sein Vorschlag Winkel (und daher Trigonometrie) beinhaltet, die ich für übertrieben halte. Dabei handelt es sich überhaupt nicht um Dreiecke (obwohl Sie sich Ihren Vektor als Dreieck xy
vorstellen
Danke noch einmal! Sie haben mein nächstes Problem gelöst, den Pfeil in die richtige Richtung zu richten.
Adam Henderson
1
@AdamHenderson Ja, unten, wenn die Achse gespiegelt ist. Übrigens, ich würde vorschlagen, einen Link zu dieser Frage im Allegro-Forum zu veröffentlichen, um später darauf zurückgreifen zu können.
kaoD