Jak mogę przeprowadzić deterministyczną symulację fizyki?

43

Tworzę grę fizyki z udziałem sztywnych ciał, w której gracze poruszają pionkami i częściami, aby rozwiązać zagadkę / mapę. Niezwykle ważnym aspektem gry jest to, że gdy gracze rozpoczynają symulację, działa tak samo wszędzie , niezależnie od systemu operacyjnego, procesora itp.

Jest dużo miejsca na złożoność, a symulacje mogą działać przez długi czas, dlatego ważne jest, aby silnik fizyki był całkowicie deterministyczny w odniesieniu do operacji zmiennoprzecinkowych, w przeciwnym razie może się wydawać, że rozwiązanie „rozwiązuje” na maszynie jednego gracza i „nie” na innym.

Jak mogę osiągnąć ten determinizm w mojej grze? Jestem gotów używać różnych frameworków i języków, w tym Javascript, C ++, Java, Python i C #.

Kusiło mnie Box2D (C ++) oraz jego odpowiedniki w innych językach, ponieważ wydaje się, że spełnia moje potrzeby, ale brakuje mu determinizmu zmiennoprzecinkowego, szczególnie w przypadku funkcji trygonometrycznych.

Najlepszą opcją, jaką do tej pory widziałem, jest Java Box2D (JBox2D). Wygląda na to, że podejmuje próbę determinizmu zmiennoprzecinkowego przy użyciu StrictMathzamiast Mathwielu operacji, ale nie jest jasne, czy ten silnik zagwarantuje wszystko, czego potrzebuję, ponieważ jeszcze nie zbudowałem gry.

Czy można użyć lub zmodyfikować istniejący silnik, aby dostosować go do moich potrzeb? Czy też będę musiał samodzielnie zbudować silnik?

EDYCJA: Pomiń resztę tego postu, jeśli nie obchodzi Cię, dlaczego ktoś potrzebuje takiej precyzji. Osoby w komentarzach i odpowiedziach wydają się wierzyć, że szukam czegoś, czego nie powinienem, dlatego wyjaśnię, jak powinna działać gra.

Gracz otrzymuje układankę lub poziom, który zawiera przeszkody i cel. Początkowo symulacja nie jest uruchomiona. Następnie mogą użyć dostarczonych im elementów lub narzędzi do zbudowania maszyny. Po naciśnięciu przycisku Start rozpoczyna się symulacja i nie można już edytować maszyny. Jeśli maszyna rozwiąże mapę, gracz przekroczył poziom. W przeciwnym razie będą musieli nacisnąć stop, zmienić maszynę i spróbować ponownie.

Powodem, dla którego wszystko musi być deterministyczne, jest to, że planuję stworzyć kod, który zamapuje każdą maszynę (zestaw elementów i narzędzi, które próbują rozwiązać poziom) na plik xml lub json, rejestrując pozycję, rozmiar i obrót każdego elementu. Gracze będą wtedy mogli dzielić się rozwiązaniami (które są reprezentowane przez te pliki), aby mogli weryfikować rozwiązania, uczyć się od siebie nawzajem, organizować konkursy, współpracować itp. Oczywiście większość rozwiązań, szczególnie proste lub szybkie tych, nie będzie dotknięty brakiem determinizmu. Ale powolne lub złożone projekty, które rozwiązują naprawdę trudne poziomy, mogą być tymi, które prawdopodobnie będą najbardziej interesujące i warte podzielenia się.

jvn91173
źródło
Komentarze nie są przeznaczone do rozszerzonej dyskusji; ta rozmowa została przeniesiona do czatu . Jeśli uważasz, że masz rozwiązanie problemu, rozważ opublikowanie go jako odpowiedzi, a nie komentarza - ułatwia to edycję, ocenę poprzez zaakceptowanie głosowania / oznaczenia lub komentarz bezpośrednio do samej sugestii w celu uzyskania opinii, a nie komentarza. tkanie odpowiedzi w dłuższy wątek konwersacji.
DMGregory

Odpowiedzi:

45

O obsłudze liczb zmiennoprzecinkowych w sposób deterministyczny

Zmienny punkt jest deterministyczny. Cóż, powinno być. To skomplikowane.

Istnieje mnóstwo literatury na temat liczb zmiennoprzecinkowych:

I jak są problematyczne:

Dla streszczenia. Przynajmniej w jednym wątku te same operacje z tymi samymi danymi, zachodzące w tej samej kolejności, powinny być deterministyczne. Dlatego możemy zacząć od martwienia się o dane wejściowe i zmiany kolejności.


Jednym z takich danych wejściowych, które powodują problemy, jest czas.

Przede wszystkim zawsze należy obliczyć ten sam czas. Nie mówię, żeby nie mierzyć czasu, mówię, że nie przejdziesz czasu do symulacji fizyki, ponieważ zmiany w czasie są źródłem hałasu w symulacji.

Dlaczego mierzysz czas, jeśli nie przekazujesz go do symulacji fizyki? Chcesz zmierzyć czas, który upłynął, aby wiedzieć, kiedy należy wywołać krok symulacji, i - zakładając, że korzystasz ze snu - ile czasu spać.

A zatem:

  • Zmierz czas: tak
  • Wykorzystaj czas w symulacji: Nie

Teraz zmiana kolejności instrukcji.

Kompilator może zdecydować, że f * a + bjest to to samo, co b + f * ajednak może mieć inny wynik. Może również skompilować się do fmadd lub może zdecydować, że weźmie wiele takich linii, które się to zdarzy, razem i napiszę je za pomocą SIMD lub innej optymalizacji, o której nie mogę teraz myśleć. I pamiętajmy, że chcemy, aby te same operacje odbywały się w tej samej kolejności, dlatego chcemy kontrolować, co się dzieje.

I nie, użycie double nie uratuje cię.

Musisz się martwić kompilatorem i jego konfiguracją, w szczególności synchronizować liczby zmiennoprzecinkowe w sieci. Musisz uzyskać wersje, aby zgodzić się na to samo.

Prawdopodobnie pisanie zestawu byłoby idealne. W ten sposób decydujesz, którą operację wykonać. Może to jednak stanowić problem w przypadku obsługi wielu platform.

A zatem:


Przypadek liczb stałych

Ze względu na sposób, w jaki pływaki są reprezentowane w pamięci, duże wartości stracą precyzję. Przyczyną jest to, że utrzymywanie małych wartości (zacisk) łagodzi problem. Zatem nie ma wielkich prędkości i nie ma dużych pomieszczeń. Co oznacza również, że możesz używać dyskretnej fizyki, ponieważ masz mniejsze ryzyko tunelowania.

Z drugiej strony będą się kumulować małe błędy. Więc obetnij. Mam na myśli, skaluj i rzutuj na liczbę całkowitą. W ten sposób wiesz, że nic się nie buduje. Będą operacje, które możesz wykonać pozostając przy typie liczby całkowitej. Kiedy musisz wrócić do zmiennoprzecinkowego, rzucasz i cofasz skalowanie.

Uwaga Mówię skalę. Chodzi o to, że 1 jednostka będzie faktycznie reprezentowana jako potęga dwóch (na przykład 16384). Cokolwiek to jest, uczyń go stałym i używaj go. Zasadniczo używasz go jako stałego numeru punktu. W rzeczywistości, jeśli można użyć znacznie lepszych liczb stałych z pewnej niezawodnej biblioteki.

Mówię obcięty. Jeśli chodzi o problem z zaokrąglaniem, oznacza to, że nie możesz ufać ostatniej części wartości uzyskanej po obsadzie. Tak więc, zanim rzucasz skalę, aby uzyskać nieco więcej niż potrzebujesz, a następnie ją obetnij.

A zatem:

  • Zachowaj małe wartości: Tak
  • Ostrożne zaokrąglanie: tak
  • Stałe numery punktów, jeśli to możliwe: Tak

Czekaj, dlaczego potrzebujesz zmiennoprzecinkowego? Czy nie możesz pracować tylko z typem całkowitym? Och, racja. Trygonometria i promieniowanie. Możesz obliczyć tabele dla trygonometrii i radiacji i upiec je w swoim źródle. Lub możesz zaimplementować algorytmy użyte do obliczenia ich za pomocą liczb zmiennoprzecinkowych, z wyjątkiem użycia liczb stałych w zamian. Tak, musisz zrównoważyć pamięć, wydajność i precyzję. Jednak możesz trzymać się z dala od liczb zmiennoprzecinkowych i pozostać deterministycznym.

Czy wiesz, że robili takie rzeczy na oryginalnym PlayStation? Proszę spotkać mojego psa, łatki .

Nawiasem mówiąc, nie mówię, żebym nie używał zmiennoprzecinkowego grafiki. Tylko dla fizyki. Oczywiście, pozycje będą zależeć od fizyki. Jednak, jak wiadomo, zderzacz nie musi pasować do modelu. Nie chcemy widzieć wyników obcinania modeli.

Dlatego: UŻYWAJ STAŁYCH NUMERÓW PUNKTOWYCH.


Żeby było jasne, jeśli możesz użyć kompilatora, który pozwala ci określić, jak działają zmiennoprzecinkowe, i to ci wystarczy, możesz to zrobić. To nie zawsze jest opcja. Poza tym robimy to dla determinizmu. Stałe numery punktów nie oznaczają, że nie ma błędów, w końcu mają ograniczoną precyzję.

Nie sądzę, aby „ustalony numer punktu był trudny” jest dobrym powodem, aby go nie używać. A jeśli chcesz mieć dobry powód, aby z nich korzystać, to determinizm, w szczególności determinizm na różnych platformach.


Zobacz też:


Dodatek : Sugeruję, aby świat był mały. Powiedziawszy to, oba OP i Jibb Smart poruszają kwestię, że odejście od pływaków początkowych ma mniejszą precyzję. Będzie to miało wpływ na fizykę, która będzie widoczna znacznie wcześniej niż krawędź świata. Stałe liczby punktowe, no cóż, mają ustaloną precyzję, będą wszędzie tak samo dobre (lub złe, jeśli wolisz). Co jest dobre, jeśli chcemy determinizmu. Chciałbym również wspomnieć, że sposób, w jaki zwykle uprawiamy fizykę, ma właściwość wzmacniania małych odmian. Zobacz efekt motyla - fizyka deterministyczna w The Incredible Machine and Contraption Maker .


Kolejny sposób uprawiania fizyki

Myślałem, że powodem, dla którego mały błąd w precyzji w liczbach zmiennoprzecinkowych jest wzmacniany, jest to, że wykonujemy iteracje na tych liczbach. Na każdym etapie symulacji bierzemy wyniki ostatniego etapu symulacji i robimy na nich różne rzeczy. Kumulacja błędów na szczycie błędów. To jest twój efekt motyla.

Nie sądzę, abyśmy zobaczyli, że jedna kompilacja korzystająca z jednego wątku na tej samej maszynie daje inną wydajność przy tym samym wejściu. Jednak na innej maszynie może to zrobić inna wersja.

Istnieje argument za przetestowaniem tam. Jeśli zdecydujemy dokładnie, jak powinny działać, i będziemy mogli przetestować na sprzęcie docelowym, nie powinniśmy wypuszczać kompilacji, które mają inne zachowanie.


Istnieje jednak również argument za niedziałaniem poza domem, który gromadzi tyle błędów. Być może jest to okazja do uprawiania fizyki w inny sposób.

Jak zapewne wiesz, istnieje ciągła i dyskretna fizyka, obie pracują nad tym, o ile każdy obiekt przesunąłby się w czasie. Jednak ciągła fizyka ma środki, aby dowiedzieć się o chwili zderzenia, zamiast sondować różne możliwe momenty, aby sprawdzić, czy doszło do zderzenia.

Proponuję zatem: użyj technik ciągłej fizyki, aby dowiedzieć się, kiedy nastąpi kolejne zderzenie każdego obiektu, z dużym krokiem czasowym, znacznie większym niż w przypadku jednego kroku symulacji. Następnie bierzesz najbliższą chwilę kolizji i zastanawiasz się, gdzie wszystko będzie w tej chwili.

Tak, to dużo pracy z jednego kroku symulacji. Oznacza to, że symulacja nie rozpocznie się natychmiast ...

... Możesz jednak przeprowadzić symulację kilku kolejnych kroków symulacji bez sprawdzania kolizji za każdym razem, ponieważ już wiesz, kiedy nastąpi następna kolizja (lub że kolizja nie nastąpi w dużym czasie). Co więcej, błędy skumulowane w tej symulacji są nieistotne, ponieważ gdy symulacja osiągnie duży czas, po prostu umieszczamy wcześniej obliczone pozycje.

Teraz możemy wykorzystać budżet czasu, który wykorzystalibyśmy do sprawdzania kolizji na każdym etapie symulacji, aby obliczyć kolejną kolizję po tej, którą znaleźliśmy. Oznacza to, że możemy symulować z wyprzedzeniem, korzystając z dużego czasu. Zakładając, że świat ma ograniczony zasięg (nie będzie to działać w przypadku dużych gier), powinna istnieć kolejka przyszłych stanów do symulacji, a następnie każda ramka, którą interpolujesz od ostatniego stanu do następnego.


Argumentowałbym za interpolacją. Biorąc jednak pod uwagę, że istnieją przyspieszenia, nie możemy po prostu interpolować wszystkiego w ten sam sposób. Zamiast tego musimy interpolować, biorąc pod uwagę przyspieszenie każdego obiektu. W tym przypadku moglibyśmy po prostu zaktualizować pozycję w ten sam sposób, co robimy dla dużego timepeptu (co oznacza również, że jest mniej podatny na błędy, ponieważ nie użylibyśmy dwóch różnych implementacji dla tego samego ruchu).


Uwaga : Jeśli wykonujemy te liczby zmiennoprzecinkowe, takie podejście nie rozwiązuje problemu obiektów zachowujących się inaczej, im dalej są od początku. Jednak prawdą jest, że precyzja jest tracona w miarę oddalania się od źródła, ale nadal jest deterministyczna. W rzeczywistości dlatego nawet nie wspomniał o tym pierwotnie.


Uzupełnienie

Od OP w komentarzu :

Chodzi o to, że gracze będą mogli zapisać swoje maszyny w jakimś formacie (np. Xml lub json), aby rejestrować pozycję i obrót każdego elementu. Ten plik XML lub JSON zostanie następnie wykorzystany do odtworzenia komputera na komputerze innego gracza.

Więc nie ma formatu binarnego, prawda? Oznacza to, że musimy się również martwić, niezależnie od tego, czy odzyskane liczby zmiennoprzecinkowe odpowiadają oryginałowi. Zobacz: Ponownie odwiedzono Float Precision: Przenośność dziewięciocyfrowa

Theraot
źródło
Komentarze nie są przeznaczone do rozszerzonej dyskusji; ta rozmowa została przeniesiona do czatu .
Vaillancourt
2
Świetna odpowiedź! 2 dodatkowe punkty na korzyść stałego punktu: 1. zmiennoprzecinkowe będzie się zachowywać inaczej bliżej lub dalej od początku (jeśli masz tę samą łamigłówkę w innym miejscu), ale stały punkt nie; 2. punkt stały ma większą precyzję niż zmiennoprzecinkowy przez większość swojego zakresu - możesz uzyskać precyzję, używając dobrze punktu stałego
Jibb Smart
Możliwe jest kodowanie danych binarnych zarówno w XML, jak i JSON przy użyciu base64elementów. Nie jest to skuteczny sposób reprezentowania dużych ilości takich danych, ale niewłaściwe jest sugerowanie, że zapobiegają one użyciu reprezentacji binarnych.
Pikalek
1
@Pikalek Jestem świadomy, OP zapytał mnie o to w komentarzach, wspomniałem base64 jako jedną z opcji, między innymi, w tym hex, reinterpretuj rzutowanie jako int i używając formatu protobuf, ponieważ i tak nikt nie zrozumie tych plików, nie są (nieprzeszkoleni) ) czytelne dla człowieka. Następnie - zakładam - mod usunął komentarze (nie, nie ma go na powyższym czacie). Czy to się powtórzy? Czy powinienem to usunąć z odpowiedzi? Czy powinienem to wydłużyć?
Theraot
@Theraot Ach, widzę, jak mogłem interpretować to inaczej w kontekście od czasu usunięcia komentarzy. (FWIW, przeczytałem czaty na temat tej odpowiedzi i pytania). I nawet jeśli istniał natywny, wydajny sposób kodowania danych, nadal jest większa kwestia upewnienia się, że oznacza to to samo na różnych platformach. Biorąc pod uwagę rezygnację, być może najlepiej po prostu zostawić ją taką, jaka jest. Dzięki za wytłumaczenie!
Pikalek
6

Pracuję dla firmy, która tworzy pewną dobrze znaną grę strategiczną w czasie rzeczywistym i mogę powiedzieć, że determinizm zmiennoprzecinkowy jest możliwy.

Używanie różnych kompilatorów lub tego samego kompilatora z różnymi ustawieniami, a nawet różnych wersji tego samego kompilatora, może złamać determinizm.

Jeśli potrzebujesz gry krzyżowej między platformami lub wersjami gry, myślę, że musisz przejść do punktu stałego - jedyna możliwa gra krzyżowa, o której wiem, że jest zmiennoprzecinkowa, jest pomiędzy PC a XBox1, ale to dość szalone.

Musisz albo znaleźć silnik fizyki, który jest w pełni deterministyczny, albo wziąć silnik open source i uczynić go deterministycznym, albo zbudować własny silnik. Mam wrażenie, że Unity wszystkich rzeczy dodało deterministyczny silnik fizyki, ale nie jestem pewien, czy jest on po prostu deterministyczny na tej samej maszynie, czy deterministyczny na wszystkich maszynach.

Jeśli masz zamiar rzucić własne rzeczy, kilka rzeczy, które mogą pomóc:

  • Elementy zmiennoprzecinkowe IEE754 są deterministyczne, jeśli nie robisz nic zbyt owocowego (Google „determinizm IEE754”, aby uzyskać więcej informacji na temat tego, co jest lub nie jest objęte)
  • Musisz upewnić się, że każdy klient ma identyczny tryb zaokrąglania i precyzję (użyj controlfp, aby to ustawić)
  • tryb zaokrąglania i dokładność mogą być zmieniane przez niektóre biblioteki matematyczne, więc jeśli używasz zamkniętych bibliotek, możesz je sprawdzić po wykonaniu połączeń (ponownie za pomocą controlfp w celu sprawdzenia)
  • niektóre instrukcje SIMD są deterministyczne, wiele nie, bądź ostrożny
  • jak wspomniano powyżej, aby zapewnić determinizm, potrzebujesz również tej samej platformy, tej samej dokładnej wersji tego samego kompilatora, kompilując tę ​​samą konfigurację, z tymi samymi ustawieniami kompilatora
  • wbuduj jakieś narzędzia do wykrywania desynchronizacji stanu i pomóż je zdiagnozować - np. CRC stan gry w każdej klatce, aby wykryć desynchronizację, a następnie włącz tryb rejestrowania pełnego, który możesz włączyć, gdy zmiany stanu gry są mozolnie logowane do pliku, następnie weź 2 pliki z symulacji, które zostały zsynchronizowane ze sobą, i porównaj w narzędziu różnicowym, aby zobaczyć, gdzie poszło nie tak
  • zainicjuj wszystkie zmienne w stanie gry, głównym źródle desynchronizacji
  • cała symulacja gry musi odbywać się dokładnie w tej samej kolejności za każdym razem, aby uniknąć desynchronizacji, niewiarygodnie łatwo jest to pomylić, wskazane jest ustrukturyzowanie symulacji gry w taki sposób, aby to zminimalizować. Naprawdę nie jestem facetem od projektowania oprogramowania, ale w tym przypadku jest to prawdopodobnie dobry pomysł - możesz rozważyć jakiś wzór, w którym symulacja gry jest jak bezpieczne pudełko, a jedynym sposobem na zmutowanie stanu gry jest wstawienie „wiadomości” lub „polecenia”, z zapewnionym stałym dostępem do wszystkiego poza stanem gry (np. renderowanie, praca w sieci itp.). Więc połączenie w sieć symulacji dla gry wieloosobowej polega na wysłaniu tych poleceń przez sieć lub odtworzenie tej samej symulacji jest przypadkiem zarejestrowania strumienia poleceń za pierwszym razem,
Joe
źródło
1
Unity rzeczywiście dąży do celu międzyplatformowego determinizmu dzięki swojemu nowemu systemowi Unity Physics dla stosu technologii zorientowanych na dane, ale jak rozumiem, jest to wciąż praca w toku i nie jest jeszcze kompletna / gotowa do użycia z półki.
DMGregory
Jaki jest przykład niedeterministycznej instrukcji SIMD? Czy myślisz o przybliżonych te jak rsqrtps?
Ruslan
@DMGregory musi być w wersji zapoznawczej, ponieważ możesz już z niego korzystać - ale jak mówisz, może nie być jeszcze ukończony.
Joe
@ Ruslan tak rsqrtps / rcpps wyniki są zależne od implementacji
Joe
5

Nie jestem pewien, czy tego typu odpowiedzi szukasz, ale alternatywą może być uruchomienie obliczeń na centralnym serwerze. Poproś klientów o przesłanie konfiguracji na twój serwer, pozwól jej przeprowadzić symulację (lub pobrać buforowaną) i odeślij wyniki, które następnie zostaną zinterpretowane przez klienta i przetworzone na grafikę.

Oczywiście wyłącza to wszelkie plany uruchomienia klienta w trybie offline, aw zależności od intensywności obliczeniowej symulacji może być potrzebny bardzo wydajny serwer. Lub wiele, ale przynajmniej masz możliwość upewnienia się, że mają taką samą konfigurację sprzętową i programową. Symulacja w czasie rzeczywistym może być trudna, ale nie niemożliwa (pomyśl o strumieniach wideo na żywo - działają, ale z niewielkim opóźnieniem).

Glorfindel
źródło
1
Całkowicie się zgadzam. W ten sposób gwarantujesz wspólne korzystanie ze wszystkich użytkowników. gamedev.stackexchange.com/questions/6645/... w pewnym sensie omawia podobny temat, porównując różnicę między fizyką po stronie klienta a fizyką po stronie serwera.
Tim Holt,
1

Przedstawię kontr-intuicyjną sugestię, że chociaż nie jest w 100% niezawodna, powinna działać dobrze przez większość czasu i jest bardzo łatwa do wdrożenia.

Zmniejsz precyzję.

Użyj wcześniej ustalonego stałego rozmiaru kroku czasowego, wykonuj fizykę dla każdego kroku czasowego w standardowym zmiennoprzecinkowym podwójnej precyzji, ale następnie skwantyzuj rozdzielczość wszystkich zmiennych do pojedynczej precyzji (lub coś jeszcze gorszego) po każdym kroku. Wtedy większość możliwych odchyleń, które może wprowadzić zmiennoprzecinkowa zmiana (w porównaniu do przebiegu referencyjnego tego samego programu), zostanie usunięta, ponieważ odchylenia te występują w cyfrach, które nawet nie istnieją ze zmniejszoną precyzją. Zatem odchylenie nie ma szans na nagromadzenie Lapunowa (Efekt Motyla), które w końcu stanie się zauważalne.

Oczywiście symulacja będzie nieco mniej dokładna niż mogłaby być (w porównaniu z prawdziwą fizyką), ale nie jest to tak naprawdę godne uwagi, o ile wszystkie uruchomione programy są niedokładne w ten sam sposób .

Teraz, technicznie rzecz biorąc, jest oczywiście możliwe, że zmiana kolejności spowoduje odchylenie, które sięga do cyfry o wyższym znaczeniu, ale jeśli odchylenia są tak naprawdę spowodowane wyłącznie zmiennoprzecinkowym, a twoje wartości reprezentują ciągłe wielkości fizyczne, jest to niezwykle mało prawdopodobne. Zauważ, że doublemiędzy dwoma dowolnymi jest pół miliarda wartości single, więc można oczekiwać, że znaczna większość kroków czasowych w większości symulacji będzie dokładnie taka sama między przebiegami symulacji. Miejmy nadzieję, że kilka przypadków, w których odchylenie przejdzie przez kwantyzację, będzie w symulacjach, które nie trwają tak długo (przynajmniej nie z chaotyczną dynamiką).


Radziłbym również, abyś rozważył całkowicie przeciwne podejście do tego, o co pytasz: zaakceptuj niepewność ! Jeśli zachowanie jest nieco niedeterministyczne, wówczas jest to faktycznie bliższe faktycznym eksperymentom fizyki. Dlaczego więc nie celowo losowo dobrać parametrów początkowych dla każdego przebiegu symulacji i wprowadzić wymóg, aby symulacja przebiegała konsekwentnie przez wiele prób? To nauczy o wiele więcej na temat fizyki i tego, jak zaprojektować maszyny, aby były wystarczająco solidne / liniowe, zamiast super delikatnych, które są realistyczne tylko w symulacji.

po lewej stronie
źródło
Zaokrąglanie w dół nie pomoże, ponieważ jeśli wynik o wysokiej precyzji jest niedeterministyczny, ostatecznie wynik zaokrągli w jedną stronę w jednym systemie, a drugą w innym systemie. Na przykład zawsze można zaokrąglić w dół do najbliższej liczby całkowitej, ale wtedy jeden komputer systemowy 1.0, a drugi oblicza wartość 0,9999999999999999999999999999999999999999 i zaokrągla inaczej.
yoyo
Tak, jest to możliwe, jak już powiedziałem w odpowiedzi. Ale zdarza się to niezwykle rzadko , podobnie jak inne usterki w fizyce gry. Więc zaokrąglenia nie pomocy. (Nie chciałbym jednak zaokrąglać w dół ; zaokrąglać do najbliższego, aby uniknąć
promowania
0

Stwórz własną klasę do przechowywania liczb!

Możesz wymusić deterministyczne zachowanie, jeśli dokładnie wiesz, jak zostaną wykonane obliczenia. Na przykład, jeśli jedynymi operacjami, z którymi masz do czynienia, są mnożenie, dzielenie, dodawanie i odejmowanie, wystarczy przedstawić wszystkie liczby jako liczbę wymierną. Aby to zrobić, wystarczy zwykła klasa Rational.

Ale jeśli chcesz poradzić sobie z bardziej skomplikowanymi obliczeniami (np. Funkcjami trygonometrycznymi), będziesz musiał sam napisać takie funkcje. Jeśli chcesz mieć sinus liczby, musisz być w stanie napisać funkcję zbliżoną do sinusa liczby, używając tylko operacji wymienionych powyżej. Wszystko to jest wykonalne i moim zdaniem omija wiele owłosionych szczegółów w innych odpowiedziach. Kompromis polega na tym, że zamiast tego będziesz musiał zająć się matematyką.

Hahahh Jaddy Amal
źródło
3
Racjonalna arytmetyka jest całkowicie niepraktyczna dla jakiejkolwiek integracji numerycznej. Nawet jeśli każdy krok ma miejsce tylko * / + -wtedy, mianowniki będą z czasem coraz większe.
leftaroundabout
Spodziewałbym się, że nawet bez rozważenia integracji nie byłoby to dobre rozwiązanie, ponieważ po zaledwie kilku mnożeniach lub podziałach liczby reprezentujące twój licznik i mianownik przepełniłyby 64-bitową liczbę całkowitą.
jvn91173
0

Jest tu trochę pomieszania terminologii. Układ fizyczny może być całkowicie deterministyczny, ale niemożliwy do modelowania przez przydatny okres czasu, ponieważ jego zachowanie jest niezwykle wrażliwe na warunki początkowe, a nieskończenie mała zmiana warunków początkowych spowoduje zupełnie inne zachowania.

Oto film z prawdziwego urządzenia, którego zachowanie jest celowo nieprzewidywalne, z wyjątkiem statystycznego:

https://www.youtube.com/watch?v=EvHiee7gs9Y

Łatwo jest zbudować proste systemy matematyczne (wykorzystujące tylko dodawanie i mnożenie), w których wynik po N krokach zależy od N-tego miejsca dziesiętnego warunków początkowych. Pisanie oprogramowania do spójnego modelowania takiego systemu na dowolnym sprzęcie komputerowym i oprogramowaniu, które użytkownik może mieć, jest prawie niemożliwe - nawet jeśli masz wystarczająco duży budżet na przetestowanie aplikacji na każdej prawdopodobnej kombinacji sprzętu i oprogramowania.

Najlepszym sposobem na rozwiązanie tego problemu jest zaatakowanie problemu u źródła: uczyń fizykę swojej gry tak deterministyczną, jak to konieczne, aby uzyskać powtarzalne wyniki.

Alternatywą jest próba uczynienia go deterministycznym poprzez ulepszenie oprogramowania komputerowego w celu modelowania czegoś, co nie jest zgodne z fizyką. Problem polega na tym, że wprowadziłeś jeszcze kilka warstw złożoności do systemu, w porównaniu z wyraźną zmianą fizyki.

Jako konkretny przykład, załóżmy, że twoja gra obejmuje zderzenia sztywnych ciał. Nawet jeśli zignorujesz tarcie, dokładne modelowanie kolizji między obiektami o dowolnym kształcie, które mogą się obracać podczas ruchu, jest w praktyce niemożliwe. Ale jeśli zmienisz sytuację tak, aby jedynymi obiektami były nieobrotowe prostokątne cegły, życie stanie się znacznie prostsze. Jeśli obiekty w grze nie wyglądają jak cegły, to ukryj ten fakt za pomocą „niefizycznej” grafiki - na przykład dosłownie ukryj chwilę kolizji za jakimś dymem lub płomieniami lub kreskówkową bańką tekstową „Ouch” lub cokolwiek.

Gracz musi odkryć fizykę gry, grając w nią. Nie ma znaczenia, czy nie jest ono „całkowicie realistyczne”, o ile jest spójne i wystarczająco podobne do zdrowego rozsądku, aby było prawdopodobne.

Jeśli sprawisz, że sama fizyka zachowuje się w stabilny sposób, jej model komputerowy może również dawać stabilne wyniki, przynajmniej w tym sensie, że błędy zaokrąglania będą nieistotne.

alephzero
źródło
1
Nie widzę żadnych nieporozumień w terminologii. OP chce deterministycznego zachowania od potencjalnie chaotycznego systemu. To całkowicie wykonalne.
Mark
Używanie prostszych kształtów (takich jak koła i prostokąty) wcale nie zmienia problemu. Nadal potrzebujesz wielu funkcji trygonometrycznych, sqrt itp.
jvn91173
-1

Użyj podwójnej precyzji zmiennoprzecinkowej , zamiast pojedynczej precyzji zmiennoprzecinkowej . Chociaż nie jest idealny, jest wystarczająco dokładny, aby uznać go za deterministyczny w twojej fizyce. Możesz wysłać rakietę na Księżyc z podwójną precyzją zmiennoprzecinkową, ale nie z pojedynczą precyzją zmiennoprzecinkową.

Jeśli naprawdę potrzebujesz idealnego determinizmu, użyj matematyki stałoprzecinkowej . To da ci mniejszą precyzję (zakładając, że używasz tej samej liczby bitów), ale wyniki deterministyczne. Nie znam żadnych silników fizyki, które używają matematyki stałoprzecinkowej, więc być może będziesz musiał napisać własny, jeśli chcesz iść tą drogą. (Odradzam.)

Evorlor
źródło
11
Podejście z podwójną precyzją działa z efektem motyla . W układzie dynamicznym (takim jak fizyka) nawet niewielkie odchylenie w warunkach początkowych może ulec wzmocnieniu poprzez sprzężenie zwrotne, śnieżki aż do zauważalnego błędu. Wszystkie dodatkowe cyfry opóźniają to nieco dłużej - zmuszając śnieżkę, aby potoczyła się nieco dalej, zanim stanie się wystarczająco duża, aby spowodować problemy.
DMGregory
2
Dwa błędy na raz: 1) Podwójne zmiennoprzecinkowe napotykają te same problemy i zwykle tylko odkładają problem na coraz trudniejszą do debugowania przyszłość. 2) Nie ma reguły, która mówi, że punkt stały musi być mniej precyzyjny niż punkt zmienny. W zależności od skali i aktualnego problemu lub od pamięci, którą możesz użyć według ustalonego numeru punktu, mogą one być mniej precyzyjne, równie precyzyjne lub bardziej precyzyjne. Nie ma sensu mówić „są mniej precyzyjne”.
fresnel
@phresnel, jako przykład precyzji stałoprzecinkowej, seria IBM 1400 zastosowała matematykę dziesiętną o dowolnej precyzji. Poświęć 624 cyfr na każdą liczbę, a przekroczyłeś zakres i precyzję zmiennoprzecinkowego podwójnej precyzji.
Mark
@phresnel (2) Dobra uwaga. Zaktualizowałem odpowiedź, aby przyjąć taką samą liczbę bitów.
Evorlor,
-2

Użyj wzorca pamiątkowego .

W początkowej fazie zapisuj dane pozycji w każdej ramce lub w dowolnych testach porównawczych, których potrzebujesz. Jeśli jest to zbyt mało wydajne, rób to tylko co n klatek.

Następnie podczas odtwarzania symulacji postępuj zgodnie z dowolną fizyką, ale aktualizuj dane pozycyjne co n klatek.

Zbyt uproszczony pseudo-kod:

function Update():
    if(firstRun) then (SaveData(frame, position));
    else if(reproducedRun) then (this.position = GetData(frame));
Evorlor
źródło
9
Nie sądzę, że to działa w przypadku OP. Powiedzmy, że oboje gramy w tę grę na różnych systemach. Każdy z nas układa elementy układanki w ten sam sposób - rozwiązanie, które nie zostało z góry przewidziane przez programistę. Po kliknięciu przycisku „Start” komputer symuluje fizykę, dzięki czemu rozwiązanie jest skuteczne. Kiedy robię to samo, niewielka różnica w symulacji powoduje, że moje (identyczne) rozwiązanie nie jest oceniane jako udane. Tutaj nie mam okazji zapoznać się z pamiątką po udanym biegu, ponieważ wydarzyło się to na twoim komputerze, a nie w czasie projektowania.
DMGregory
@DMGregory To prawda. Dziękuję Ci.
jvn91173