Autor Wątek: Kolizja a FPS - jak sobie z tym radzić.  (Przeczytany 1691 razy)

Offline ZiomeX

  • Użytkownik

# Maj 18, 2011, 21:53:30
Witam. Pisząc prosty projekt zauważyłem że na integrze intela (tej słynnej 950 :P) i słabiutkim procku fps drastycznie spada (do ~13fpsów przy ~250obiektach). Problem jest taki, iż przy ów 13fpsach odświeżanie ekranu jest dosyć skokowe, a obliczenia wykonywane są co większą odległość niż zamierzono. Teoretycznie jest to zwykłe przycinanie, ale co zrobić z kolizją? Zakładając że wystrzelę pocisk na x=5, a odświeżać będzie potem na x=7, x=9 itd... Co jeśli na x=6 powinien napotkać na kolizję? Teoretycznie można by sprawdzić o 1 do tyłu, ale co dla 5fpsów, gdzie x nie zmienia się co 2, a np. 3 :P

Pomysłem jest spradzanie przecinania się prostych, ale jest to dosyć niedokładne. O ile z samym poziomem (stałym) można zrobić w miarę to prosto (sprawdzać wszystko po drodze z pozycji poprzedniej do aktualnej, ale co jeśli mamy przeciwników komputerowych lub nawet żywych graczy z małą ilością FPSów?

Jestem "początkujący" ale nie proszę o gotowca, a info typu "Pomyśl nad np. iloczynem wektorowym" albo "Możesz to zrobić np. przez sprawdzenie tego i tego" :)

Na szybko naszła mnie myśl aby trzymać coś w stylu ścieżki przebytej z poprzedniej pozycji do aktualnej. Ale było by to chyba jednak podatne na błędy a przez co nie za bardzo pomocne mi.

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Xirdus

  • Redaktor

# Maj 18, 2011, 22:20:36
Może zrób logikę na fixed timestep?

Offline mINA87

  • Użytkownik

# Maj 18, 2011, 22:23:41
Logika niezależna od grafiki i tyle :) Podejść możesz mieć kilka:
1. Zakładasz stały offset logiki, mierzysz czas jaki upłynął na renderingu, aktualizujesz tyle razy logikę ile się zmieści w tym interwale.
2. Rozwiązujesz wszystkie problemy analitycznie - mierzysz po prostu upływ czasu i podstawiasz to do wzorów, żeby wyliczyć nową pozycję, nową pozycję możesz już poddać kolizji.
3. Jeśli okaże się, że w przypadku rozwiązania 2 masz za małą rozdzielczość aktualizacji logiki i okazuje się że np. pocisk "przeskakuje" przez przeszkodę dla niskich FPSów, to łączysz 1 z 2 i dla zbyt dużych interwałów czasu, dzielisz taki interwał na mniejsze części.
A tak ogólnie to polecam zoptymalizować kod :)

Offline ZiomeX

  • Użytkownik

# Maj 18, 2011, 23:17:54
@up - 2jka jest nieoptymalna (aktualnie jej stosuję).

Możesz rozwinąć 1dnkę? Chodzi o coś w stylu osobnego wątku, w którym obliczam tylko fizykę + ew. kolizje, a wyświetlam aktualne dane w innym wątku? I wyświetla to co obliczone jest za ostatnim razem? Np. coś takiego jak w załączniku? To jest dosyć ciekawe, ale jak sprawdzić czy ostatnie obliczanie dobiegło końca, tak aby wszystkie obiekty były z tego samego "punktu" w czasie (a nie że np. gracz i 2 przeciwników jest na aktualnej pozycji, a reszta na tej z poprzedniego update'u)? Jakieś wskazówki do OpenGLa/SDLa?

@Xirdus - chwilkę poszperałem w google i nic nie znalazłem pod 'fixed timestamp logic' czy 'games fixed timestamp' lub 'fixed timestamp physics'. Jeśli możesz nakierować na łatwiejsze/obszerniejsze(?) informacje do wyszukania byłbym wdzięczny ew. jakieś szersze info niż 6 słów :P
« Ostatnia zmiana: Maj 18, 2011, 23:22:59 wysłana przez ZiomeX »

Offline mINA87

  • Użytkownik

# Maj 19, 2011, 00:02:44
A co jest nieoptymalnego w ewaluacji czegoś pokroju:
[x,y] = [x0,y0] + [vx,vy] * t ?
To jest de facto analityczne rozwiązanie problemu.

Co do 1dnki to to jest właśnie fixed-step logic - piszesz funkcję dla logiki, w której zakładasz że wszystkie operacje wykonane są dla pewnego dt (powiedzmy 5ms) i teraz jeśli rendering zajmie Ci 23ms, to 23ms/5ms = 4 update'y. Ponadto wtedy 23ms % 5ms = 3ms, więc zostawiasz to sobie i czekasz aż Ci się uzbiera na pełen update.

Najlepszym rozwiązaniem, z którego korzysta większość engineów fizycznych i na którym opiera się większość rozwiązań CCD to właśnie 3 - rozwiązania analityczne + podzielenie delty na mniejsze fragmenty jeśli dela jest zbyt duża.

Offline ZiomeX

  • Użytkownik

# Maj 19, 2011, 10:11:50
Aktualnie używam analitycznej formy tzn. wyznaczam miejsce na podstawie aktualnej pozycji, prędkości i przyśpieszenia (oraz delty czasy co oczywiste), jednak problem jest taki, że jeśli deta czasu jest zbyt duża (niski FPS na b. słabych maszynach) np. pocisk może przeskoczyć cienką ścianę w czasie w którym nie jest on generowany. Dlatego odpada takie rozwiązanie (przynajmniej samo).

Odnośnie "Fixed setp logic"
Chodzi o coś takiego jak mówiłem - stworzyć 1 wątek do rysowania, a 2gi do uaktualniania logiki, i wątek rysujący korzysta z najświeższych danych, zaś wątek z logiką odświeża dane np. co 5ms? Bo jakoś nie widzę rozwiązania tego problemu na 1 wątku w twojej metodzie.

Chyba że chodzi Ci o coś w stylu -> W 1 wątku wsyzstko (zwykła pętla), eśli delta jest większa niż np. 20ms, sprawdz logikę 4 razy dla delty = 5,ms sprawdź logikę dla delty = 5, sprawdz..., sprawdz... (4x5ms) i dopiero rysuj?

Jeśli chodziło Ci o to 2gie - to które jest lepsze? Zabawa z wątkami czy za 1 obrotem pętli kilka razy obliczyć logikę dla mniejszych przedziałów czasowych?
« Ostatnia zmiana: Maj 19, 2011, 10:17:30 wysłana przez ZiomeX »

Offline Xender

  • Użytkownik


Offline ZiomeX

  • Użytkownik

# Maj 19, 2011, 15:41:27
Dzięki serdeczne za link :) teraz widzę że sposób z osobnym wątkiem i tak nic by nie dał :)

Po przeczytaniu artykułu doszedłem do wniosku że to nic innego jak aktualizowanie fizyki co stały określony czas - ok wszystko fajnie. Tylko nie było by lepsze aktualizować fizykę częściej jeśli to możliwe, a jeśli nie to co określony czasu? Coś w stylu:

if(accumulated_time < step_time) {
   UpdateGame(accumulated_time);
} else {
   while(accumulated_time > step_time) {
       UpdateGame(step_time);
        accumulated_time -= step_time;
   }
}
Chodzi o to, że jeśli jest możliwość częściej aktualizować to nie warto z tego skorzystać? :) Czy nie warto sobie zawracać głowy? Teoretycznie im więcej obliczeń tym lepiej, ale zaś nie można sporo na sztywno ustawić na słabszy sprzęt bo niepotrzebnie będzie obciążał :P

Na pewno w oryginale wszędzie będzie działać tak samo, zaś przeróbka faworyzuje tych, którzy mają więcej niż określona minimalna liczba fpsów. Problem jest jednak taki, że jeżeli aktualizujemy tylko 1/const_fps razy na sekundę mamy stały fps (choć rysujemy go więcej razy - niepotrzebnie?), przez co praktycznie widzimy tylko const_fps różnych klatek.
« Ostatnia zmiana: Maj 19, 2011, 16:10:49 wysłana przez ZiomeX »

Offline Xirdus

  • Redaktor

# Maj 19, 2011, 17:35:30
Ustaw timestep na 20ms. Chyba że ludziki będą ostro zap*** i będzie to za dużo - ale generalnie powinno być dobrze. Jeśli wszystko działa, to zmniejszanie tej wartości nie ma sensu (w dalszym ciągu wszystko będzie działać).

np. pocisk może przeskoczyć cienką ścianę w czasie w którym nie jest on generowany
W Box2D jest fajne rozwiązanie tego problemu. Obiekty są podzielone na "normalne" i "pociski" (szybko poruszające się). Ruchy tych pierwszych są obliczane o niebo szybciej, jednak mają ograniczenie co do prędkości (by wszystko dobrze działało, nie mogą w ciągu klatki poruszać się szybciej niż najmniejszy obiekt). Te drugie nie mają tego ograniczenia, dzięki czemu mogą pędzić z dowolnie wielką prędkością; obliczenia są jednak kosztowniejsze.

Offline Krzysiek K.

  • Redaktor
    • DevKK.net

# Maj 19, 2011, 17:41:27
Cytuj
Problem jest jednak taki, że jeżeli aktualizujemy tylko 1/const_fps razy na sekundę mamy stały fps (choć rysujemy go więcej razy - niepotrzebnie?), przez co praktycznie widzimy tylko const_fps różnych klatek.
Dlatego warto trzymać obecną i poprzednią pozycję każdego obiektu i interpolować między nimi o ta ułamkową część klatki. W efekcie mamy stabilną kompletnie fizykę niezależną od platformy (ze stałym krokiem) i ładne animacje.

Offline Xirdus

  • Redaktor

# Maj 19, 2011, 17:47:29
@up
Kiedyś właśnie teoretyzowałem na ten temat i zastanawiałem się czy to ma sens. W końcu przy stałych 60FPSach nie będzie to dawało widocznych korzyści (chyba).

Offline Xender

  • Użytkownik

# Maj 19, 2011, 20:04:14
Chodzi o to, że jeśli jest możliwość częściej aktualizować to nie warto z tego skorzystać? :) Czy nie warto sobie zawracać głowy? Teoretycznie im więcej obliczeń tym lepiej, ale zaś nie można sporo na sztywno ustawić na słabszy sprzęt bo niepotrzebnie będzie obciążał :P
Właśnie o to chodzi żeby krok był stały. Kiedy jest za mały po 1. marnujemy moc obliczeniową (a może można ją wykorzystać na co innego?), a po 2. słyszałem że ze zbyt małego kroku też mogą wynikać błędy. Poza tym jak stały to stały, po co kombinować...

Offline ZiomeX

  • Użytkownik

# Maj 19, 2011, 21:59:58
@up Toeretycznie można by ustawić stałe 1/75 (więcej fpsów i tak nie zobaczy się).

Po co marnować? I tak nie będzie marnowana jeśli masz mieć 300fpsów i 50razy obliczaną fizykę, a 290fpsów i 100razy fizykę - myślę że prędzej zauważysz powtarzające się klatki takie same, niż spadek <1% fps'ów ;) Tym b. że to gra 2D (na razie to tworzę). Ale chyba jednak ustawię ten stały krok 1/75 i niech liczy tak :)

Offline Xirdus

  • Redaktor

# Maj 19, 2011, 22:10:58
Po co marnować? I tak nie będzie marnowana jeśli masz mieć 300fpsów i 50razy obliczaną fizykę, a 290fpsów i 100razy fizykę
A jak komuś ze słabszym kompem tę grę dasz, to przy 29FPS dalej będzie musiał cisnąć sto fizyk - co jeszcze bardziej obniży FPS. Im mniej zasobów zabierasz tym lepiej - we współczesnych systemach, z antywirami, firewallami, komunikatorami i pięćdziesięcioma kartami w przeglądarce lepiej nieco zapasu zostawić.

Ustaw fizykę na 50ms (20FPS czy tam w twojej nomenklaturze 1/20) - jeśli będzie dobrze działać, to będzie dobrze działać, jeśli nie, to zmniejsz timestep (zwiększ FPS czy jak to tam w twojej nomenklaturze). Generalnie nie schodź poniżej 16ms (powyżej 60FPS czy tam 1/60) - u większości osób nawet grafika nie przekroczy tej granicy.

Offline ZiomeX

  • Użytkownik

# Maj 19, 2011, 22:39:58
@up w sumie to jest drobny szczegół :) Przy większym projekcie na pewno ustawię stałą ilość i nie ma sensu się bawić :)

Tak czy siak na pytanie dostałem odpowiedź i dziękuję jeszcze raz za nią :)

Pozdrawiam i owocnych prac w pisaniu ;)