Як я можу намалювати стрілку в краю екрана, що вказує на об'єкт, який не входить у екран?


12

Я хочу зробити те, що описано в цій темі:

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

Я спробував різні методи, згадані тут.

Спочатку я спробував використати метод, описаний Carrus85:

Просто візьміть співвідношення двох трикутникових гіпонтануз (не має значення, який трикутник ви використовуєте для іншого, я пропоную пункт 1 і 2 як відстань, яку ви обчислите). Це дасть вам співвідношення сторін трикутника в кутку від більшого трикутника. Тоді ви просто помножите дельтакс на це значення, щоб отримати зсув координати x, і дельтай на це значення, щоб отримати зміщення y координати.

Але я не зміг знайти спосіб обчислити, як далеко об’єкт знаходиться від краю екрана.

Потім я спробував використовувати рентгенівський кастинг (чого я ніколи раніше не робив), запропонований 23yrold3yrold:

Стріляйте промінь від центру екрана до екрана. Обчисліть, де на прямокутнику перетинається промінь. Там є ваші координати.

Я вперше обчислив гіпотенузу трикутника, утворену різницею позицій x і y двох точок. Я використовував це для створення одиничного вектора по цій лінії. Я перекинув цей вектор, поки або координата x, або координата y не вийшли з екрана. Дві поточні значення x і y потім утворюють стрілки x і y.

Ось код для мого методу випромінювання (написаний на C ++ та 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);
}

Це було відносно успішно. Стрілки відображаються в нижній правій частині екрана, коли об’єкти розташовані вище та зліва від екрану так, як ніби місця, де намальовані стрілки, повернуті на 180 градусів навколо центру екрана.

Я припускав, що це пов'язано з тим, що коли я обчислював гіпотенузу трикутника, він завжди буде позитивним незалежно від того, різниця в x чи різниця у y від'ємна.

Поміркувавши про це, передавання променів не здається вдалим способом вирішення проблеми (через те, що воно передбачає використання sqrt () та великого для циклу).

Відповіді:


6

Отже, у вас є дві координати або вектори, один - це центр екрана (C відтепер), а другий - ваш об'єкт (P відтепер.)

Якщо ви знаєте математику, ви можете знати, що лінія може бути виражена як походження та вектор напряму. Походження - ваш екран-центр, тоді як вектор напряму можна знайти, віднімаючи C від P. Це рівняння також може бути виражене в параметричній формі, яка по суті однакова:

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

Бачите (P.? - C.?)шматочок? Це ваш вектор напрямку (як я вже сказав, відніміть C від P). Останній C.?біт - це початок рядка.

t- це змінна, яка може змінюватись від 0того 1, 0що є походженням вектора (якщо ви працюєте, xі yстав би C.xі C.y), 1будучи координатою вашого об'єкта (знову ж таки, працюючи, він би став P.xі P.y, або "кінцем" вектора, якщо бажаєте) та значення між інтерполяцією між обома кінцями сегмента. Ви також можете призначити зовнішні значення: внизу 0ви повернете векторний напрямок, а вище 1ви будете "розширювати" ваш вектор далі в тому ж напрямку.

Як тільки ви це отримаєте, це стає досить легко. Ваша мета - знайти точку цього вектора ( xі yдля заданого значення t), де X=WIDTHабо Y=HEIGHT, що вийде першим. Як бачите, tєдина ваша змінна тут:

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

Або повторно висловивши це:

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

Це отримає точку перерізу лінії, визначену вектором праворуч і вгорі. Те ж саме для лівої і нижньої межі екрану, де ви повинні перевірити проти 0обох випадків, а НЕ WIDTHі HEIGHT.

Оскільки вона врешті-решт вирізає межі, навіть поза екраном, найнижчим tзначенням стане ваша перша контактна точка. Повернення операції та застосування знайденого tзначення на рівняннях (0)(однакове значення для обох!) Принесе нове (x,y), яке буде вашими координатами різання.

Можливо, є деякі математичні помилки або відмінності в реалізації для вашої проблеми, але це основна ідея. Я також залишив деякі частини (завжди є чотири випадки різання, і вам потрібна лише одна), але трохи думки приведе вас до остаточного рішення :)


Дякую. Я піду. РЕДАКТИРУВАННЯ: Як ви думаєте, чи вважаєте ви цей метод тим, який описав Каррус85 (використовуючи співвідношення гіпотенузи)?
Адам Хендерсон

1
@AdamHenderson Я радий допомогти :) Пам'ятайте, що ви можете зберегти вектор напрямку, щоб пізніше намалювати стрілку. Ви можете його нормалізувати, щоб отримати ваш вектор унітарного напрямку, відняти його в arrow-lengthрази від ріжучого вектора "et voila", у вас є походження та призначення стрілки.
якDo

1
@AdamHenderson візуально це те саме, оскільки ваша лінія - це гіпотенуза, про яку він говорить. Практично це не те саме, оскільки його пропозиція передбачає кути (і, отже, тригонометрія), які, на мою думку, є надмірними для цього. Це взагалі не стосується трикутників (хоча ви можете вважати свій вектор як трикутник, де гіпотенуза - вектор, а обидві сторони - xі yкомпоненти.)
kaoD

Знову дякую! Ви вирішили мою наступну проблему - вказати стрілку в потрібному напрямку.
Адам Хендерсон

1
@AdamHenderson так, знизу, якщо вісь перевернута. BTW, я б запропонував розмістити посилання на це питання на форумі Allegro для подальшого ознайомлення.
kaoD
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.