OK, opublikowałem to już na stronie math.stackechange.com, ale nie otrzymałem żadnych odpowiedzi :(
Najpierw jest zdjęcie mojego problemu, a następnie opis:
Więc skonfigurowałem wszystkie punkty i wartości.
Statek zaczyna poruszać się po lewej planecie P1
z S=0.27 Degrees
gametickiem, a kiedy do niego dociera Point A
, zaczyna podążać za krzywą Béziera aż do osiągnięcia Point D
, a następnie podróżuje wokół właściwej planety P2
z tyknięciem S=0.42 Degrees
na grę. Różnica S
polega na tym, że podróżuje z tą samą prędkością ruchu wokół planet.
Jak dotąd tak dobrze, uruchomiłem to, teraz mój problem.
Kiedy S P1
i S P2
różnią się bardzo, statek przeskakuje między dwiema prędkościami, gdy osiąga miejsce docelowe, co wygląda dość źle. Więc muszę przyspieszyć statek między Point A
i Point D
od S P1
do S P2
.
To, czego mi brakuje, jest w kolorze fioletowym, są to:
Sposób na obliczenie tyknięć, po których statek porusza się wzdłuż Beziera, biorąc pod uwagę przyspieszenie.
I sposób na znalezienie pozycji na krzywej Béziera na podstawie T, ponownie biorąc pod uwagę przyspieszenie.
ATM Liczę długość beziera, obliczając odległość między N
jego punktami. Myślę , że potrzebuję sposobu na skalowanie T
potrzebnego do obliczenia Béziera odpowiednio do przyspieszenia.
źródło
Odpowiedzi:
OK, mam wszystko działające, zajęło to wieczność, więc opublikuję tutaj moje szczegółowe rozwiązanie.
Uwaga: wszystkie próbki kodu są w JavaScript.
Podzielmy więc problem na podstawowe części:
Musisz obliczyć długość oraz punkty pomiędzy
0..1
krzywą BezieraMusisz teraz wyregulować skalowanie,
T
aby przyspieszyć statek z jednej prędkości na drugąPoprawienie Beziera
Znalezienie kodu do rysowania krzywej Beziera jest łatwe, istnieje jednak wiele różnych podejść, jednym z nich jest algorytm DeCasteljau , ale można również użyć równania dla sześciennych krzywych Béziera:
Dzięki temu można teraz narysować krzywą Beziera dzwoniąc
x
iy
zt
którego waha się od0 to 1
rzućmy okiem:Uh ... to nie jest równomierny rozkład punktów, prawda?
Ze względu na charakter krzywej Béziera punkty na niej
0...1
mają różnearc lenghts
, więc segmenty w pobliżu początku i końca są dłuższe niż te, które znajdują się w pobliżu środka krzywej.Równomierne odwzorowanie T na parametryzacji długości łuku na krzywej AKA
Co więc zrobić? Cóż, w prostych słowach potrzebujemy funkcji do mapowania naszego
T
nat
krzywej, aby naszeT 0.25
wynikit
były na25%
długości krzywej.Jak to zrobimy? No cóż, my Google ... ale okazuje się, że termin ten nie jest tak łatwy do przeszukiwania , że w pewnym momencie trafisz na ten plik PDF . Co na pewno jest świetną lekturą, ale w przypadku, gdy już zapomniałeś wszystkich matematyki, których nauczyłeś się w szkole (lub po prostu nie lubisz tych symboli matematycznych), jest to całkiem bezużyteczne.
Co teraz? Cóż, idź i Google trochę więcej (czytaj: 6 godzin), a w końcu znajdziesz świetny artykuł na ten temat (w tym ładne zdjęcia! ^ _ ^ "):
Http://www.planetclegg.com/projects/WarpingTextToSplines.html
Robiąc rzeczywisty kod
Jeśli po prostu nie mogłeś się powstrzymać przed pobraniem tego pliku PDF, chociaż już dawno straciłeś swoją wiedzę matematyczną (i udało ci się pominąć link do świetnego artykułu), możesz teraz pomyśleć: „Boże, to zajmie setki linii kodu i tony procesora ”
Nie, nie będzie. Ponieważ robimy to, co robią wszyscy programiści, jeśli chodzi o matematykę:
po prostu oszukujemy.
Parametryzacja długości łuku, leniwy sposób
Spójrzmy prawdzie w oczy, nie potrzebujemy nieskończonej precyzji w naszej grze, prawda? Więc jeśli nie pracujesz w NASA i nie planujesz wysyłać ludzi na Marsa, nie potrzebujesz
0.000001 pixel
idealnego rozwiązania.Więc jak mamy map
T
nat
? To proste i składa się tylko z 3 kroków:Oblicz
N
punkty na krzywej za pomocąt
i zapiszarc-length
(zwaną też długością krzywej) w tej pozycji w tablicyAby odwzorować
T
nat
najpierw pomnożyćT
przez całkowitą długość krzywej dostaću
, a następnie przeszukać tablicę długościach dla indeksu o największej wartości, która jest mniejsza niżu
Jeśli mieliśmy dokładne trafienie, zwróć wartość tablicy o tym indeksie podzieloną przez
N
, jeśli nie interpoluj trochę między punktem, który znaleźliśmy, a następnym, podziel ponownie rzecz przezN
i wróć.To wszystko! Spójrzmy teraz na pełny kod:
To inicjuje naszą nową krzywą i oblicza
arg-lenghts
, a także przechowuje ostatnią długość jakototal length
krzywą, kluczowym czynnikiem jest tutajthis.len
naszaN
. Im wyższa, tym dokładniejsze będzie odwzorowanie, ponieważ krzywa wielkości na powyższym obrazku100 points
wydaje się wystarczająca, jeśli potrzebujesz tylko dobrego oszacowania długości, coś w rodzaju25
wykona już zadanie, mając tylko 1 piksel w naszym przykład, ale wtedy będziesz mieć mniej precyzyjne mapowanie, co spowoduje nierównomierne rozłożenieT
mapowaniat
.Rzeczywisty kod odwzorowania, najpierw robimy proste
binary search
na naszych przechowywanych długościach, aby znaleźć największą długość, która jest mniejszatargetLength
, a następnie po prostu zwracamy lub wykonujemy interpolację i zwracamy.Znów oblicza się to
t
na krzywej.Czas na wyniki
Do tej pory używasz,
mx
amy
otrzymujesz równomierne rozłożenieT
na krzywej :)Czy to nie było trudne? Po raz kolejny okazuje się, że do gry wystarczy proste (choć nie idealne rozwiązanie).
Jeśli chcesz zobaczyć pełny kod, dostępna jest Gist:
https://gist.github.com/670236
Wreszcie przyspieszenie statków
Pozostało już tylko przyspieszyć statki na ich drodze, odwzorowując pozycję, na
T
której następnie używamy, aby znaleźć sięt
na naszej krzywej.Najpierw potrzebujemy dwóch równań ruchu , a mianowicie
ut + 1/2at²
i(v - u) / t
W rzeczywistym kodzie wyglądałby tak:
Następnie zmniejszamy to
0...1
, wykonując:I proszę bardzo, statki poruszają się teraz płynnie wzdłuż ścieżki.
W przypadku, gdy to nie działa ...
Kiedy to czytasz, wszystko działa dobrze i elegancko, ale początkowo miałem pewne problemy z częścią przyspieszania, kiedy wyjaśniając problem komuś na czacie gamedev, znalazłem ostatni błąd w moim myśleniu.
W przypadku, gdy nie zapomniałeś już o zdjęciu w pierwotnym pytaniu, wspomnę
s
tam, okazuje się, żes
jest to prędkość w stopniach , ale statki poruszają się wzdłuż ścieżki w pikselach, a ja o tym zapomniałem. Więc w tym przypadku musiałem przekonwertować przemieszczenie w stopniach na przemieszczenie w pikselach, okazuje się, że jest to dość łatwe:I to wszystko! Dziękuje za przeczytanie ;)
źródło
Problem polega na tym, że statek nie wybrałby naturalnie tej trajektorii. Nawet jeśli działa idealnie, nadal nie będzie wyglądać dobrze.
Jeśli chcesz zasymulować płynne przejście między planetami, sugerowałbym jego modelowanie. Równania są bardzo proste, ponieważ masz tylko dwie znaczące siły: grawitację i ciąg.
Musisz tylko ustawić swoje stałe: Masa P1, P2, statek
Z każdym tyknięciem gry (czas: t) robisz 3 rzeczy
Obliczyć grawitację p1 na statku i p2 na statku, dodać powstałe wektory do wektora ciągu.
Oblicz swoją nową prędkość na podstawie nowego przyspieszenia od kroku 1
Poruszaj statkiem zgodnie z nową prędkością
Może to wydawać się dużo pracy, ale można to zrobić za pomocą kilkunastu linii kodu i będzie wyglądać bardzo naturalnie.
Jeśli potrzebujesz pomocy z fizyką, daj mi znać.
źródło
t
:)Znalazłem doskonały artykuł wyjaśniający możliwe rozwiązanie tego problemu na przykładzie kodu napisanego w javascript. Działa poprzez „przesuwanie wartości t” we właściwym kierunku.
To pytanie ma już wiele fajnych odpowiedzi, ale uznałem to rozwiązanie za warte odnotowania.
źródło
Dziękujemy za doskonałą stronę opisującą sposób rozwiązania tego problemu. Zrobiłem coś nieco innego niż ty w jednym szczególe, ponieważ byłem głęboko ograniczony pamięcią: nie buduję tablicy, ani nie muszę jej szukać odpowiedniego „segmentu” za pomocą wyszukiwania binarnego. Dzieje się tak, ponieważ zawsze wiem, że przechodzę od jednego końca mojej krzywej Beziera do drugiego: Dlatego po prostu pamiętam segment „bieżący” i jeśli widzę, że wykroczę poza granice tego segmentu, aby obliczyć następny pozycję, obliczam następny (lub poprzedni) segment (na podstawie kierunku podróży). Działa to całkiem dobrze w mojej aplikacji. Jedyną usterką, którą musiałem rozwiązać, było to, że na niektórych krzywych wielkość segmentów była tak mała, że mój następny wykres do wskazania był - w rzadkich przypadkach - o więcej niż jeden segment przed bieżącym, więc zamiast po prostu iść do '
Nie wiem, czy to ma sens, ale z pewnością mi pomogło.
źródło
Tego rodzaju modelowanie jest dziwne i może dawać dziwne nielogiczne wyniki. Zwłaszcza jeśli prędkość planet startowych jest naprawdę wolna.
Modeluj statki siłą ciągu.
Kiedy statki znajdą się na ostatniej orbicie na planecie startowej, przyspiesz z pełnym ciągiem.
Kiedy statek znajdzie się w pewnej odległości, użyj ciągu wstecznego, aby spowolnić statek do prędkości orbity docelowej planety.
Edycja: Wykonaj całą symulację jednocześnie, gdy węzeł ma zamiar opuścić orbitę. albo wysyłaj wszystkie dane, albo wysyłaj tylko kilka wektorów ruchu w odstępach i interpoluj między nimi.
źródło
Jeśli dobrze to rozumiem, twój problem jest nadmiernie ograniczony.
Uważam, że chcesz, aby statek kosmiczny podróżował określoną ścieżką między orbitami w pewnym czasie t , a także chcesz, aby przyspieszył od prędkości s1 do prędkości s2 w tym samym czasie t . Niestety, nie można (ogólnie) znaleźć przyspieszenia, które spełnia oba te ograniczenia jednocześnie.
Będziesz musiał nieco rozluźnić swój problem, aby go rozwiązać.
źródło
Natknąłem się na tę odpowiedź, ponieważ chcę równomiernie rozdzielić punkty wzdłuż ścieżki SVG, która wykorzystuje krzywą Beziera.
Mimo, że MDN mówi, że jest przestarzałe, możesz użyć go,
path.getPointAtLength
aby uzyskać poprawny wynik. https://developer.mozilla.org/en-US/docs/Web/API/SVGPathElement/getPointAtLengthObecnie działa w Chrome / Safari / Firefox i powinien również działać w IE / Edge, ale nie zweryfikowałem tych 2.
źródło
Problem z przyjętym rozwiązaniem
Jako funkcja wykładnicza Beziera , spodziewamy się różnych wskaźników postępu w różnych obszarach krzywej.
Ponieważ rozwiązanie Ivo interpoluje liniowo między tymi początkowymi próbkami wykładniczymi , niedokładności będą silnie tendencyjne w kierunku końców / środka krzywej (zwykle sześciennej), gdzie te delty są największe; więc jeśli częstotliwość próbkowania nie
N
zostanie znacznie zwiększona, jak sugeruje, błędy są widoczne, a przy pewnym poziomie powiększenia zawsze będą widoczne dla danegoN
, tj. odchylenie jest nieodłączne dla tego algorytmu. Nie nadaje się np. Do grafiki wektorowej, gdzie powiększenie może być nieograniczone.Przeciwdziałanie stronniczości poprzez kontrolowane pobieranie próbek
Alternatywnym rozwiązaniem jest liniowo przemapować
distance
abyt
po przeciwdziałaniu naturalne nastawienie, że funkcja produkuje Beziera.Zakładając, że tego właśnie pragniemy:
ale oto, co otrzymujemy z funkcji pozycji Beziera:
Patrząc na
N
pobrane próbki, możemy zobaczyć, gdzie delty odległości są największe, i ponownie próbkować („podzielić”) w połowie odległości między dwiema sąsiednimi odległościami, zwiększającN
je o 1. Na przykład, dzieląc nat=0.9
(która jest w połowie największej delty), możemy otrzymać:Powtarzamy ten proces dla następnego największego przedziału odległości, dopóki maksymalna delta między dowolnymi dwoma odległościami w całym zestawie nie spadnie poniżej niektórych
minDistanceDelta
, a dokładniej, mniej niżepsilon
od określonych odległości, które chcemy zamapować na krokit
; możemy następnie liniowo map nasze pożądanycht
czynności do odpowiednichdistance
s. W ten sposób tworzona jest tablica mieszająca / mapa, do której można tanio uzyskać dostęp i której wartości można przewijać między użytkownikami w czasie wykonywania bez uprzedzeń.Gdy zestaw rośnie
N
, koszt jego powtórzenia wzrasta, więc najlepiej zrobić to jako proces wstępny. Za każdym razemN
, gdy wzrasta, dodaj dwa nowe wynikowe interwały dointervals
kolekcji, usuwając jednocześnie stary, pojedynczy interwał, który zastąpiły. Jest to struktura, nad którą pracujesz, aby znaleźć następny największy przedział do podziału na dwa. Posortowanieintervals
według odległości ułatwia wszystko, ponieważ możesz po prostu oderwać następny element pracy od końca i podzielić itd.W efekcie powstaje coś, co idealnie chcielibyśmy:
Ponieważ zgadujemy na każdym kroku, nie uzyskamy dokładnych dokładnych odległości itp.
2
,4
Których chcieliśmy, ale poprzez powtarzanie iteracji zbliżają się one wystarczająco do pożądanych wartości odległości, dzięki czemu możesz mapować swojet
kroki z odpowiednią dokładnością, eliminując uprzedzenia z powodu do prawie równo odległych próbek.Możesz następnie pobrać np. Tak
t=0.5
jak Ivo w swojej odpowiedzi, tj. Interpolując dwie najbliższe wartości powyżej (3.9998132
i6.00703
).Wniosek
W większości przypadków rozwiązanie Ivo będzie działać dobrze, ale w przypadkach, w których za wszelką cenę należy unikać stronniczości, upewnij się, że twoje
distance
są tak równomiernie rozproszone, jak to możliwe, a następnie liniowo zmapowanet
.Zauważ, że dzielenie może być wykonywane stochastycznie zamiast dzielenia środka za każdym razem, na przykład moglibyśmy podzielić ten pierwszy przykładowy przedział
t=0.827
zamiast wt=0.9
.źródło