Edycja: Komentarz POs sceptycznie odnosi się do skuteczności sugerowanego ujemnego testu kołysania w celu ulepszenia algorytmu w celu sprawdzenia, czy dowolny punkt 2D znajduje się w obróconym i / lub ruchomym prostokącie. Bawiąc się trochę w moim silniku gier 2D (OpenGL / C ++), uzupełniam moją odpowiedź, dostarczając test porównawczy wydajności mojego algorytmu w stosunku do obecnych algorytmów sprawdzania punktu i prostokątów OP (i odmian).
Początkowo zasugerowałem pozostawienie algorytmu na miejscu (ponieważ jest on prawie optymalny), ale uproszczenie poprzez samą logikę gry: (1) używając wstępnie przetworzonego okręgu wokół oryginalnego prostokąta; (2) wykonaj kontrolę odległości i czy punkt leży w danym okręgu; (3) użyj OP lub innego prostego algorytmu (zalecam algorytm isLeft podany w innej odpowiedzi). Logika, na której opiera się moja sugestia, polega na tym, że sprawdzenie, czy punkt znajduje się w okręgu jest znacznie wydajniejsze niż sprawdzenie granicy obróconego prostokąta lub dowolnego innego wielokąta.
Mój początkowy scenariusz testu porównawczego polega na uruchomieniu dużej liczby pojawiających się i znikających kropek (których pozycja zmienia się w każdej pętli gry) w ograniczonej przestrzeni, która zostanie wypełniona około 20 obracającymi się / ruchomymi kwadratami. Opublikowałem film ( link do youtube ) w celach ilustracyjnych. Zwróć uwagę na parametry: liczbę losowo pojawiających się kropek, liczbę lub prostokąty. Przeprowadzę testy porównawcze z następującymi parametrami:
WYŁ : Prosty algorytm dostarczony przez PO bez kontroli ujemnych na granicy okręgu
WŁ . : Używanie okręgów przetworzonych (granicznych) wokół prostokątów jako pierwszej kontroli wykluczenia
ON + Stack : Tworzenie granic okręgu w czasie wykonywania w pętli na stosie
ON + Dystans kwadratowy: Wykorzystanie odległości kwadratowych jako dodatkowej optymalizacji, aby uniknąć stosowania droższego algorytmu pierwiastka kwadratowego (Pieter Geerkens).
Oto podsumowanie różnych wydajności różnych algorytmów, pokazujące czas potrzebny na iterację w pętli.
Oś X pokazuje zwiększoną złożoność poprzez dodanie większej liczby kropek (a tym samym spowolnienie pętli). (Na przykład przy 1000 losowo pojawiających się punktach sprawdza się w przestrzeni zwierzeń z 20 prostokątami, pętla iteruje i wywołuje algorytm 20000 razy.) Oś y pokazuje czas (ms) ukończenia całej pętli przy użyciu wysokiej rozdzielczości licznik wydajności. Więcej niż 20 ms byłoby problematyczne dla przyzwoitej gry, ponieważ nie skorzystałby z wysokiej liczby klatek na sekundę do interpolacji płynnej animacji, a gra może czasami wydawać się „trudna”.
Wynik 1 : Wstępnie przetworzony algorytm związany z kołem z szybką kontrolą ujemną w pętli poprawia wydajność o 1900% w porównaniu do zwykłego algorytmu (5% pierwotnego czasu pętli bez kontroli). Wynik jest w przybliżeniu proporcjonalny do liczby iteracji w pętli, dlatego nie ma znaczenia, czy sprawdzimy 10 lub 10000 losowo pojawiających się punktów. Zatem na tej ilustracji można bezpiecznie zwiększyć liczbę obiektów do 10k bez odczuwania utraty wydajności.
Wynik 2 : W poprzednim komentarzu zasugerowano, że algorytm może być szybszy, ale wymaga dużej ilości pamięci. Pamiętaj jednak, że przechowywanie liczby zmiennoprzecinkowej dla wstępnie przetworzonego rozmiaru okręgu zajmuje zaledwie 4 bajty. Nie powinno to stanowić prawdziwego problemu, chyba że PO planuje uruchomić jednocześnie ponad 100 000 obiektów. Alternatywnym i efektywnym podejściem do pamięci jest obliczenie maksymalnego rozmiaru okręgu na stosie w pętli i pozostawienie go poza zakresem przy każdej iteracji, a tym samym praktycznie bez użycia pamięci dla nieznanej ceny prędkości. Rzeczywiście wynik pokazuje, że to podejście jest rzeczywiście wolniejsze niż użycie wstępnie przetworzonego rozmiaru koła, ale nadal wykazuje znaczną poprawę wydajności o około 1150% (tj. 8% pierwotnego czasu przetwarzania).
Wynik 3 : Ulepszam algorytm wyniku 1, używając kwadratowych odległości zamiast rzeczywistych odległości i tym samym podejmując kosztownie obliczeniową operację pierwiastka kwadratowego. To tylko nieznacznie zwiększa wydajność (2400%). (Uwaga: wypróbowuję również tabele skrótów dla wstępnie przetworzonych tablic dla przybliżeń pierwiastków kwadratowych z podobnym, ale nieco gorszym wynikiem)
Wynik 4 : Dalej sprawdzam przesuwanie / zderzanie prostokątów; nie zmienia to jednak podstawowych wyników (zgodnie z oczekiwaniami), ponieważ kontrola logiczna pozostaje zasadniczo taka sama.
Rezultat 5 : Zmieniam liczbę prostokątów i stwierdzam, że algorytm staje się jeszcze bardziej wydajny, im mniej miejsca jest wypełnione (nie pokazano w wersji demo). Wynik jest również nieco oczekiwany, ponieważ prawdopodobieństwo maleje, gdy punkt pojawi się w niewielkiej przestrzeni między okręgiem a granicami obiektu. Z drugiej strony staram się zwiększyć liczbę prostokątów o zbyt 100 w tej samej ograniczonej przestrzeni ORAZ zmieniać ich dynamicznie rozmiar w czasie wykonywania w pętli (sin (iterator)). Nadal działa to wyjątkowo dobrze ze wzrostem wydajności o 570% (lub 15% pierwotnego czasu pętli).
Wynik 6 : Testuję zaproponowane tutaj alternatywne algorytmy i znajduję bardzo niewielką, ale nie znaczącą różnicę w wydajności (2%). Interesujący i prostszy algorytm IsLeft działa bardzo dobrze ze wzrostem wydajności o 17% (85% pierwotnego czasu obliczeń), ale nigdzie blisko wydajności algorytmu szybkiego testu negatywnego.
Chodzi mi przede wszystkim o rozważenie szczupłego projektowania i logiki gry, szczególnie w przypadku granic i zdarzeń kolizyjnych. Obecny algorytm PO jest już dość wydajny, a dalsza optymalizacja nie jest tak istotna jak optymalizacja samej koncepcji. Ponadto dobrze jest przekazać zakres i cel gry, ponieważ wydajność algorytmu zależy od nich krytycznie.
Sugeruję, aby zawsze próbować przeprowadzić analizę porównawczą dowolnego złożonego algorytmu na etapie projektowania gry, ponieważ samo spojrzenie na prosty kod może nie ujawnić prawdy o rzeczywistej wydajności w czasie wykonywania. Sugerowany algorytm może nie być tutaj nawet potrzebny, jeśli na przykład chcemy po prostu przetestować, czy kursor myszy znajduje się w prostokącie, czy też nie, lub gdy większość obiektów już się dotyka. Jeśli większość punktów jest w prostokącie, algorytm będzie mniej wydajny. (Jednak wówczas możliwe byłoby ustanowienie granicy „wewnętrznego koła” jako dodatkowej kontroli ujemnej.) Kontrole granicy koła / kuli są bardzo przydatne do każdego przyzwoitego wykrywania kolizji dużej liczby obiektów, które mają naturalnie trochę przestrzeni między nimi .
Rec Points Iter OFF ON ON_Stack ON_SqrDist Ileft Algorithm (Wondra)
(ms) (ms) (ms) (ms) (ms) (ms)
20 10 200 0.29 0.02 0.04 0.02 0.17
20 100 2000 2.23 0.10 0.20 0.09 1.69
20 1000 20000 24.48 1.25 1.99 1.05 16.95
20 10000 200000 243.85 12.54 19.61 10.85 160.58