Ekstrapolacja przerywa wykrywanie kolizji

10

Przed zastosowaniem ekstrapolacji do ruchu mojego duszka, moje zderzenie działało idealnie. Jednak po zastosowaniu ekstrapolacji do ruchu mojego duszka (aby wygładzić rzeczy) kolizja nie działa.

Oto jak działało to przed ekstrapolacją:

wprowadź opis zdjęcia tutaj

Jednak po zaimplementowaniu mojej ekstrapolacji procedura kolizji zostaje zerwana. Zakładam, że dzieje się tak, ponieważ działa on na nową współrzędną utworzoną przez procedurę ekstrapolacji (która znajduje się w moim wywołaniu renderowania).

Po zastosowaniu mojej ekstrapolacji

wprowadź opis zdjęcia tutaj

Jak poprawić to zachowanie?

Próbowałem poddać się dodatkowej kontroli kolizji zaraz po ekstrapolacji - wydaje się, że to rozwiązuje wiele problemów, ale wykluczyłem to, ponieważ logika w moim renderowaniu nie wchodzi w rachubę.

Próbowałem również wykonać kopię pozycji spritesX, ekstrapolując ją i rysując używając tego, a nie oryginału, pozostawiając nienaruszony oryginał, aby logika mogła go zaakceptować - wydaje się to lepsza opcja, ale nadal daje dziwne efekty podczas zderzenia ze ścianami. Jestem prawie pewien, że to też nie jest właściwy sposób, aby sobie z tym poradzić.

Znalazłem tutaj kilka podobnych pytań, ale odpowiedzi mi nie pomogły.

To jest mój kod ekstrapolacji:

public void onDrawFrame(GL10 gl) {


        //Set/Re-set loop back to 0 to start counting again
        loops=0;

        while(System.currentTimeMillis() > nextGameTick && loops < maxFrameskip){

        SceneManager.getInstance().getCurrentScene().updateLogic();
        nextGameTick+=skipTicks;
        timeCorrection += (1000d/ticksPerSecond) % 1;
        nextGameTick+=timeCorrection;
        timeCorrection %=1;
        loops++;
        tics++;

     }

        extrapolation = (float)(System.currentTimeMillis() + skipTicks - nextGameTick) / (float)skipTicks; 

        render(extrapolation);
}

Stosowanie ekstrapolacji

            render(float extrapolation){

            //This example shows extrapolation for X axis only.  Y position (spriteScreenY is assumed to be valid)
            extrapolatedPosX = spriteGridX+(SpriteXVelocity*dt)*extrapolation;
            spriteScreenPosX = extrapolationPosX * screenWidth;

            drawSprite(spriteScreenX, spriteScreenY);           


        }

Edytować

Jak wspomniałem powyżej, próbowałem wykonać kopię współrzędnych duszka, aby narysować ... to ma swoje problemy.

Po pierwsze, bez względu na kopiowanie, gdy duszek się porusza, jest super gładki, gdy się zatrzymuje, kołysze się lekko w lewo / w prawo - ponieważ wciąż ekstrapoluje swoją pozycję na podstawie czasu. Czy to jest normalne zachowanie i czy możemy je „wyłączyć”, gdy duszek się zatrzyma?

Próbowałem mieć flagi dla lewej / prawej i ekstrapolować tylko, jeśli którakolwiek z nich jest włączona. Próbowałem również skopiować ostatnie i bieżące pozycje, aby zobaczyć, czy jest jakaś różnica. Jednak w przypadku kolizji nie pomagają.

Jeśli użytkownik naciska powiedz, prawy przycisk, a duszek porusza się w prawo, gdy uderza o ścianę, jeśli użytkownik nadal przytrzymuje prawy przycisk, duszek będzie animował w prawo, zatrzymując się przy ścianie ( dlatego tak naprawdę się nie porusza), jednak ponieważ ustawiono prawą flagę, a także dlatego, że procedura kolizji stale przesuwa duszka ze ściany, nadal wydaje się kodowi (nie graczowi), że duszek wciąż się porusza, a zatem ekstrapolacja trwa. Tak więc gracz zobaczy, że duszek jest „statyczny” (tak, animuje, ale tak naprawdę nie porusza się po ekranie) i co jakiś czas trzęsie się gwałtownie, gdy ekstrapolacja próbuje to zrobić… .. Mam nadzieję, że to pomoże

BungleBonce
źródło
1
To zajmie trochę trawienia, aby w pełni zrozumieć („interpolacja” ma pozornie tuzin różnych znaczeń i na pierwszy rzut oka nie jest całkowicie jasne, co masz na myśli), ale mój pierwszy instynkt brzmi: „nie powinieneś robić nic, aby wpływać na swoje pozycja obiektu w twojej procedurze renderowania ”. Twój renderer powinien narysować obiekt w podanej pozycji obiektu, a manipulowanie nim jest przepis na kłopoty, ponieważ z natury łączy on renderer z fizyką gry. W idealnym świecie twój renderer powinien być w stanie przyjmować stałe wskaźniki do obiektów gry.
Steven Stadnicki
Cześć @StevenStadnicki, wielkie dzięki za komentarz, istnieje wiele przykładów pokazujących wartość interpolacji przekazywaną do renderera, zobacz to: mysecretroom.com/www/programming-and-software/..., z którego dostosowałem mój kod. Moje ograniczone zrozumienie jest takie, że interpoluje pozycję między ostatnimi a następnymi aktualizacjami w oparciu o czas potrzebny od ostatniej aktualizacji - Zgadzam się, że to trochę koszmar! Byłbym wdzięczny, gdybyś mógł zaproponować i alternatywę, z którą łatwiej byłoby mi pracować - na zdrowie :-)
BungleBonce
6
Cóż, powiem „tylko dlatego, że możesz znaleźć kod do czegoś, co nie czyni z niego najlepszej praktyki”. :-) W tym przypadku jednak podejrzewam, że strona, do której prowadzisz link, używa wartości interpolacji, aby dowiedzieć się, gdzie wyświetlić swoje obiekty, ale tak naprawdę nie aktualizuje z nimi pozycji obiektów; możesz to również zrobić, po prostu mając pozycję specyficzną dla rysunku, która jest obliczana dla każdej klatki, ale utrzymując tę ​​pozycję oddzielnie od rzeczywistej pozycji „fizycznej” obiektu.
Steven Stadnicki
Cześć @StevenStadnicki, jak wskazano w moim pytaniu (akapit rozpoczynający się od „Próbowałem również wykonać kopię”), faktycznie już próbowałem użyć pozycji „tylko rysuj” :-) Czy możesz zaproponować metodę interpolacji, w której ja nie musisz dostosowywać pozycji duszka w ramach procedury renderowania? Dzięki!
BungleBonce
Patrząc na kod, wygląda na to, że dokonujesz ekstrapolacji zamiast interpolacji.
Durza007

Odpowiedzi:

1

Nie mogę jeszcze opublikować komentarza, więc opublikuję to jako odpowiedź.

Jeśli dobrze rozumiem problem, wygląda to tak:

  1. najpierw masz kolizję
  2. następnie pozycja obiektu jest korygowana (przypuszczalnie przez procedurę wykrywania kolizji)
  3. zaktualizowana pozycja obiektu jest wysyłana do funkcji renderowania
  4. funkcja renderowania aktualizuje następnie lokalizację obiektu za pomocą ekstrapolacji
  5. pozycja ekstrapolowanego obiektu przerywa teraz procedurę wykrywania kolizji

Mogę wymyślić 3 możliwe rozwiązania. Wymienię je w kolejności najbardziej pożądanej dla najmniej IMHO.

  1. Przenieś ekstrapolację poza funkcję renderowania. Ekstrapoluj pozycję obiektu, a następnie sprawdź kolizję.
  2. lub jeśli chcesz zachować ekstrapolację w funkcji renderowania, ustaw flagę oznaczającą kolizję. Niech wykrywanie kolizji poprawi pozycję obiektu, tak jak już to robisz, ale przed obliczeniem wartości ekstrapolacji sprawdź najpierw flagę kolizji. Ponieważ obiekt jest już tam, gdzie powinien być, nie trzeba go przenosić.
  3. Ostatnią możliwością, która według mnie jest raczej obejściem niż naprawą, byłaby nadmierna kompensacja z wykrywaniem kolizji. Po kolizji odsuń obiekt od ściany, aby po ekstrapolacji obiekt znalazł się ponownie na ścianie.

Przykładowy kod dla # 2.

if (collisionHasOccured)
    extrpolation = 0.0f;
else
    extrapolation = (float)(System.currentTimeMillis() + skipTicks - nextGameTick) / (float)skipTicks;

Myślę, że numer 2 byłby prawdopodobnie najszybszym i najłatwiejszym do uruchomienia, chociaż numer 1 wydaje się być bardziej logicznym rozwiązaniem. W zależności od tego, jak poradzisz sobie z rozwiązaniem delta w czasie, rozwiązanie nr 1 może zostać złamane w podobny sposób przez dużą deltę, w którym to przypadku może być konieczne jednoczesne użycie obu # 1 i # 2.

EDYCJA: Źle zrozumiałem twój kod wcześniej. Pętle mają renderować tak szybko, jak to możliwe i aktualizować w ustalonych odstępach czasu. Dlatego interpolowałbyś pozycję duszka, aby obsłużyć przypadek, w którym rysujesz więcej niż aktualizowanie. Jeśli jednak pętla pozostanie w tyle, odpytujesz aktualizację, dopóki nie zostaniesz złapany lub nie przeskoczysz maksymalnej liczby losowań klatek.

Biorąc to pod uwagę, jedynym problemem jest to, że obiekt porusza się po zderzeniu. W przypadku kolizji obiekt powinien przestać się poruszać w tym kierunku. Jeśli więc dochodzi do kolizji, ustaw jego prędkość na 0. To powinno powstrzymać funkcję renderowania od dalszego przesuwania obiektu.

Aholio
źródło
Cześć @Aholio Wypróbowałem opcję 2 i działa, ale powoduje kilka błędów, może zrobiłem to źle, wrócę. Jestem jednak bardzo zainteresowany opcją 1, chociaż nie mogę znaleźć informacji, jak to zrobić. Jak mogę ekstrapolować powiedzenie w mojej procedurze logicznej? BTW moja delta jest ustalona, ​​więc wynosi 1/60 lub 0,01667 (raczej tej liczby używam do integracji, chociaż ilość czasu każdej iteracji mojej gry może oczywiście różnić się w zależności od tego, co się dzieje, ale tak naprawdę nigdy nie powinna zająć więcej niż 0,01667 ), więc wszelka pomoc w tym byłaby wielka dzięki.
BungleBonce
Być może nie do końca rozumiem, co robisz. Coś, co wydaje mi się nieco dziwne, polega na tym, że nie tylko wykonujesz ruchy pozycji w funkcji rysowania; robisz też obliczenia czasu. Czy zrobiono to dla każdego obiektu? Prawidłowa kolejność powinna być następująca: obliczenie czasu wykonane w kodzie pętli gry. Pętla gry przekazuje deltę do funkcji aktualizacji (dt). Aktualizacja (dt) zaktualizuje wszystkie obiekty gry. Powinien obsługiwać każdy ruch, ekstrapolację i wykrywanie kolizji. Po powrocie update () do pętli gry wywołuje funkcję render (), która rysuje wszystkie świeżo zaktualizowane obiekty gry.
Aholio
Hmmmmm obecnie moja funkcja aktualizacji robi wszystko. Czy ruch (przez ruch mam na myśli obliczanie nowej pozycji duszków - jest to obliczane na podstawie mojego czasu delta). Jedyne, co robię w mojej funkcji renderowania (oprócz rysowania), to ekstrapolacja „nowej” pozycji, ale oczywiście wymaga to czasu, ponieważ musi wziąć pod uwagę czas od ostatniej aktualizacji logiki do wywołania renderowania. W każdym razie tak rozumiem :-) Jak byś to zrobił? Nie rozumiem, jak korzystać z ekstrapolacji w ramach samej aktualizacji logiki. Dzięki!
BungleBonce
Zaktualizowałem moją odpowiedź. Mam nadzieję, że to pomoże
Aholio
Dzięki, widzę w moim oryginalnym Q: „kiedy się zatrzymuje, kołysze się lekko w lewo / w prawo” - więc nawet jeśli zatrzymam mojego duszka, ekstrapolacja nadal utrzymuje duszka „w ruchu” (chwieje się) - dzieje się tak, ponieważ nie używam stare i bieżące pozycje duszka, aby wypracować moją ekstrapolację, dlatego nawet jeśli duszek jest statyczny, nadal ma ten efekt wahania oparty na wartości „ekstrapolacji”, która jest obliczana przy każdej iteracji ramki. Próbowałem nawet powiedzieć „jeśli stary i aktualny pos są takie same, to nie ekstrapoluj”, ale to nadal nie działa, wrócę jednak i spojrzę jeszcze raz!
BungleBonce
0

Wygląda na to, że musisz całkowicie oddzielić renderowanie i aktualizację fizyki. Zwykle podstawowa symulacja będzie przebiegać w dyskretnych odstępach czasu, a częstotliwość nigdy się nie zmieni. Na przykład możesz symulować ruch swojej piłki co 1/60 sekundy i to wszystko.

Aby umożliwić zmienną szybkość klatek, kod renderowania powinien działać na zmiennej częstotliwości, ale każda symulacja powinna nadal odbywać się w ustalonym czasie. Pozwala to grafice odczytać pamięć z symulacji jako tylko do odczytu i pozwala ustawić interpolację zamiast ekstrapolacji.

Ponieważ ekstrapolacja próbuje przewidzieć, gdzie będą przyszłe wartości, nagłe zmiany w ruchu mogą spowodować ogromne błędy ekstrapolacji. Lepiej zamiast tego renderować scenę wokół ramki za symulacją i interpolować między dyskretnymi znanymi pozycjami.

Jeśli chcesz zobaczyć szczegóły implementacji, napisałem już krótką sekcję na ten temat w artykule tutaj . Zobacz sekcję „Pomiar czasu”.

Oto ważny kod psuedo z artykułu:

const float fps = 100
const float dt = 1 / fps
float accumulator = 0

// In units seconds
float frameStart = GetCurrentTime( )

// main loop
while(true)
  const float currentTime = GetCurrentTime( )

  // Store the time elapsed since the last frame began
  accumulator += currentTime - frameStart( )

  // Record the starting of this frame
  frameStart = currentTime

  // Avoid spiral of death and clamp dt, thus clamping
  // how many times the UpdatePhysics can be called in
  // a single game loop.
  if(accumulator > 0.2f)
    accumulator = 0.2f

  while(accumulator > dt)
    UpdatePhysics( dt )
    accumulator -= dt

  const float alpha = accumulator / dt;

  RenderGame( alpha )

void RenderGame( float alpha )
  for shape in game do
    // calculate an interpolated transform for rendering
    Transform i = shape.previous * alpha + shape.current * (1.0f - alpha)
    shape.previous = shape.current
    shape.Render( i )

RenderGameFunkcja jest najbardziej interesujące. Chodzi o użycie interpolacji między dyskretnymi pozycjami symulacji. Kod renderujący może tworzyć własne kopie danych tylko do odczytu z symulacji i używać do renderowania tymczasowej wartości interpolowanej. Zapewni to bardzo płynny ruch bez żadnych głupich problemów, takich jak to, co wydaje się mieć!

RandyGaul
źródło