Zaczynam się uczyć fizyki majsterkowania i mam pytanie dotyczące implementacji integracji na najbardziej podstawowym poziomie (tj. Nie jest to pytanie Eulera vs. RK4).
Niemal każdy przykład, na jaki natrafiam, ma integrate()
funkcję, która pobiera czas od ostatniej aktualizacji i aktualizuje przyspieszenie (i / lub prędkość i / lub pozycję) od ostatniej aktualizacji.
W najprostszej formie: position += velocity * deltaTime
Nie rozumiem jednak, dlaczego gromadzi się w ten sposób, skoro równie łatwo można go uzyskać, zmieniając funkcję . Na przykład: getPosition = makeNewFunction()
który może zwrócić coś, co ma sygnaturę Time -> Position
, a wewnętrzne funkcje tej funkcji są generowane za pomocą odpowiedniego wzoru matematycznego.
W ten sposób nie dochodzi do kumulacji ... ilekroć trzeba uzyskać pozycję, wywołuje tę funkcję z bieżącym czasem.
Rozumiem dla początkujących, że pozwoli to uniknąć błędów wynikających z akumulacji ... więc dlaczego to nie działa, czego mi brakuje?
(FWIW ja nie ułożyła podstawowy dowód koncepcji tego idea- choć to także testowanie kilka innych rzeczy w tym samym czasie, więc nie jest to najczystszy przykład: https://github.com/dakom/ball-bounce-frp )
EDYCJA 1: jak wspomniano w komentarzach, prawdopodobnie ważne jest, aby zauważyć, że jeszcze nie nauczyłem się o zmianie przyspieszenia lub radzeniu sobie z szarpnięciem i innymi rzeczami, które wymagają integracji wyższego rzędu niż stałe przyspieszenie.
EDYCJA 2: oto podstawowy przykładowy kod pomysłu i składnia pseudo javascript - zauważ, że getKinematicPosition
jest częściowo zastosowana, więc zwraca nową funkcję tylko Czas -> Pozycja:
Trzymam się pozycji, ale może to być coś innego, jak getVelocity
sądzę ...
getKinematicPosition = initialVelocity => acceleration => time =>
((.5 *acceleration) * (time * time)) + (initialVelocity * time);
getPosition = getKinematicPosition ([0,0,0]) (GRAVITY);
onTick = totalTime => {
position = getPosition (totalTime);
onCollision = () => {
getPosition = changeTheFunction(totalTime);
//changeTheFunction uses totalTime to base updates from 0
//it could use getKinematicPosition or something else entirely
}
}
źródło
Odpowiedzi:
Będzie to działać w przypadku niektórych klas problemów, a kluczową frazą do wyszukiwania jest rozwiązanie o zamkniętej formie . Na przykład w programie kosmicznym Kerbal ruch statku kosmicznego na orbicie jest obliczany w ten sposób. Niestety, większość nietrywialnych problemów (np. Ponowne wejście w atmosferę tego statku kosmicznego) nie ma znanego rozwiązania w formie zamkniętej. Zatem potrzeba matematycznie prostszych aproksymacji liczbowych (tj. W
integrate()
czasie).źródło
Problem z twoim podejściem polega na tym, że nie masz historii swojego obiektu. Możesz obliczyć pozycję, jeśli poruszasz się w określonym kierunku, ale co się stanie, jeśli trafisz coś i odskoczysz?
Jeśli zgromadzisz na swojej ostatniej znanej pozycji, możesz poradzić sobie z uderzeniem i kontynuować od tego momentu. Jeśli spróbujesz obliczyć go od początku, za każdym razem musisz ponownie obliczyć wpływ lub ustawić go jako nową pozycję początkową.
Twój przykład przypominał mi grę wyścigową. (Nie wiem, czy pozycja byłaby kontrolowana przez silnik fizyki, ale myślę, że świetnie to wyjaśnia)
Jeśli jeździsz samochodem, możesz przyspieszyć i zwolnić. Nie możesz obliczyć swojej pozycji, nie wiedząc od początku, jak wyglądał profil prędkości twojego samochodu. Akumulacja odległości jest o wiele łatwiejsza niż zapisywanie prędkości, którą miałeś w każdej klatce od początku do teraz.
Oświadczenie: Do tej pory nie pisałem fizyki gry, tak właśnie widzę problem.
Edycja: na
tym diagramie możesz zobaczyć, jak wartości zmieniają się w czasie.
czerwony = przyspieszenie (od rozpoczęcia przyspieszania do zwolnienia)
zielony = prędkość (od rozpoczęcia do zatrzymania)
niebieski = droga, którą poszedłeś.
Całkowita prędkość jest integralną częścią przyspieszenia od punktu początkowego do faktycznej rejestracji. (Obszar między linią a osią)
. Droga jest całką twojej prędkości.
Jeśli znasz wartości swojego przyspieszenia, możesz obliczyć inne wartości. Ale jeśli się nie mylę, całki są również obliczane przez akumulację na komputerach PC. I to o wiele większe obciążenie, aby przechowywać wszystkie wartości przyspieszenia.
Ponadto prawdopodobnie jest to zbyt wiele, aby obliczyć każdą klatkę.
Wiem, moje umiejętności malowania są świetne. ;)
Edycja 2:
Ten przykład dotyczy ruchu liniowego. Zmiana kierunku czyni to jeszcze trudniejszym.
źródło
Możesz!
Nazywa się to za pomocą analitycznego lub zamkniętego roztworu. Ma tę zaletę, że jest dokładniejszy, ponieważ błędy zaokrąglania, które kumulują się w czasie, nie istnieją.
Działa to jednak tylko wtedy , gdy znasz wcześniej taki zamknięty formularz. W przypadku gier często tak się nie dzieje.
Ruch gracza jest zmienny i po prostu nie można go włączyć w jakąś wstępnie obliczoną funkcję. Gracz może i często zmienia swoją prędkość i orientację.
NPC mogą potencjalnie wykorzystywać rozwiązania w formie zamkniętej, a czasem tak robią. Ma to jednak inne wady. Pomyśl o prostej grze wyścigowej. Za każdym razem, gdy Twój pojazd zderzy się z innym pojazdem, musisz zmienić swoją funkcję. Może samochód porusza się szybciej w zależności od metra. Zatem znalezienie takiego zamkniętego rozwiązania będzie dość trudne. W rzeczywistości istnieje prawdopodobnie więcej przypadków, w których znalezienie takiej zamkniętej formy jest albo niemożliwe, albo tak skomplikowane, że po prostu niemożliwe.
Doskonałym przykładem zastosowania rozwiązania w formie zamkniętej jest program kosmiczny Kerbal. Gdy tylko rakieta znajdzie się na orbicie i nie będzie pod naciskiem, KSP może umieścić ją „na szynach”. Orbity są z góry ustalone w układzie dwóch ciał i są okresowe. Dopóki rakieta nie zastosuje więcej ciągu, wiesz już, gdzie będzie rakieta, i możesz po prostu zadzwonić
getPositionAtTime(t)
(nie ma dokładnie takiej nazwy, ale masz pomysł).W praktyce jednak stosowanie integracji krok po kroku jest często znacznie bardziej praktyczne. Ale kiedy zobaczysz sytuację, w której istnieje rozwiązanie w formie zamkniętej i jest łatwe do obliczenia, idź do niego! Nie ma powodu, żeby nie aby go używać.
Na przykład, jeśli twoja postać celuje w armatę, możesz łatwo pokazać przewidywany punkt uderzenia kuli armaty, używając rozwiązania o zamkniętej formie. A jeśli twoja gra nie pozwala na zmianę przebiegu kuli armatniej (na przykład bez wiatru), możesz nawet użyć jej do przesunięcia kuli armatniej. Pamiętaj, że musisz szczególnie uważać na przeszkody poruszające się na ścieżkę twojej kuli armatniej.
Istnieje wiele podobnych sytuacji. Jeśli budujesz grę opartą na rundach, wówczas prawdopodobnie będzie dostępnych znacznie więcej zamkniętych rozwiązań niż podczas tworzenia gry RTS, ponieważ znasz wszystkie parametry z wyprzedzeniem i możesz z całą pewnością powiedzieć, że się nie zmieniają (nic nie rusza się nagle na przykład w tę ścieżkę).
Zauważ, że istnieją techniki walki z liczbowymi nieścisłościami stopniowej integracji. Na przykład możesz śledzić nagromadzony błąd i zastosować termin korygujący, aby utrzymać błąd w ryzach, np. Podsumowanie Kahana
źródło
W przypadku zwykłej odbijającej się piłki znalezienie rozwiązania w formie zamkniętej jest łatwe. Jednak bardziej złożone układy zwykle wymagają rozwiązania zwykłego równania różniczkowego (ODE). Rozwiązania numeryczne są wymagane do obsługi wszystkich oprócz najprostszych przypadków.
Rzeczywiście istnieją dwie klasy numerycznych solverów ODE: jawne i niejawne. Jawne solwery zapewniają przybliżone przybliżenie do następnego stanu, podczas gdy niejawne solwery wymagają rozwiązania równania, aby to zrobić. To, co opisujesz dla swojej odbijającej się piłki, jest w rzeczywistości ukrytym rozwiązaniem ODE, niezależnie od tego, czy o tym wiedziałeś, czy nie!
Rozwiązania niejawne mają tę zaletę, że mogą wykorzystywać znacznie większe przedziały czasowe. W przypadku algorytmu odbijającej się piłki twój czas może być co najmniej tak długi, jak czas do następnej kolizji (co zmieniłoby twoją funkcję). Może to spowodować, że Twój program będzie działał znacznie szybciej. Jednak generalnie nie zawsze możemy znaleźć dobre ukryte rozwiązania ODE, którymi jesteśmy zainteresowani. Gdy nie możemy, polegamy na wyraźnej integracji.
Dużą zaletą, którą widzę przy wyraźnej integracji, jest to, że gotcha jest dobrze znana. Możesz otworzyć dowolny podręcznik z lat 60. i przeczytać wszystko, co musisz wiedzieć o małych dziwactwach, które pojawiają się przy poszczególnych technikach integracji. W ten sposób programista uczy się tych umiejętności raz i nigdy więcej nie musi się ich uczyć. Jeśli wykonujesz integrację niejawną, każdy przypadek użycia jest nieco inny, z nieco innymi problemami. Nieco trudniej jest zastosować to, czego nauczyłeś się z jednego zadania do następnego.
źródło
pos (t) = v (t) * t
działa tylko wtedy, gdy pos (0) = 0 oraz v (t) = k
nie można powiązać pozycji z czasem bez znajomości stanu początkowego i całej funkcji prędkości, więc równanie jest przybliżeniem całki
pos (t) = całka v (t) dt od 0 do t
EDYTOWAĆ _________
Oto mały dowód na komentarze (przy założeniu, że pos (0) = 0)
niech v (t) = 4
eqn 1: pos (t) = 4 * t (poprawnie)
eqn 2: pos (t) = c + 4 * t od 0 do t = 4 * t (poprawnie)
niech v (t) = 2 * t
eqn 1: pos (t) = 2 * t ^ 2 (źle)
eqn 2: pos (t) = c + t ^ 2 od 0 do t = t ^ 2 (poprawnie)
Powinienem dodać, że twoje równanie już uwzględnia stałe przyspieszenie (tzn. Twoje równanie to eqn 2, gdzie v (t) = v0 + a * t, a granice integracji wynoszą t0 it), więc twoje równanie powinno działać tak długo, jak aktualizujesz położenie początkowe, prędkość początkowa i przyspieszenie pozostają stałe.
EDIT2 ________
Powinienem również dodać, że można również obliczyć pozycję z początkowym pos, początkową prędkością, początkowym przyspieszeniem i stałym szarpnięciem. Innymi słowy, możesz stworzyć funkcję opartą na równaniu 2, która reprezentuje pozycję w funkcji czasu, dzieląc ją na jej pochodne, tj. Prędkość, szarpnięcie, cokolwiek będzie dalej, itp. Itd., Ale będziesz dokładny w równaniu, jeśli v (t) można modelować w ten sposób. Jeśli v (t) nie może być modelowane tylko z prędkością, przyspieszeniem, stałym szarpnięciem itp., Musisz wrócić do przybliżenia eqn 2, co zwykle zdarza się, gdy coś się podskakuje, opór powietrza, wiatr itp. .
źródło