Android smooth game loop


Mam problemy z płynnym przewijaniem w OpenGL (testowanie na SGS2 i ACE)
Tworzyłem proste aplikacje - tylko stała prędkość poziomego przewijania obrazów, lub tylko jeden obraz (odtwarzacz) poruszający się akceleratorem, ale ruch nie jest płynny: - (
Próbowałem wielu różnych kodów, ale bez satysfakcji...

Najpierw próbowałem pracować z GLSurfaceView.RENDERMODE_CONTINUOUSLY i wrzucam cały kod do onDrawFrame:
    public void onDrawFrame(GL10 gl) 
    {
      updateGame(gl, 0.017f);
      drawGame(gl); 
    }

Jest to najprostszy i absolutny gładko!! - ale to zależy od szybkości sprzętu (=bezużyteczne)


    public void onDrawFrame(GL10 gl) 
    { 
      frameTime = SystemClock.elapsedRealtime();        
      elapsedTime = (frameTime - lastTime) / 1000; 
      updateGame(gl, elapsedTime);
      drawGame(gl); 
      lastTime = frameTime; 
    }
To jest najlepsze ze wszystkich, ale nie jest tak gładkie jak poprzednie, czasami flick


po drugie próbowałem GLSurfaceView.RENDERMODE_WHEN_DIRTY , w onDrawFrame mam tylko obiekty rysunkowe i ten kod w osobnym wątku:
   while (true) 
   {
     updateGame(renderer, 0.017f);
     mGLSurfaceView.requestRender();
     next_game_tick += SKIP_TICKS;
     sleep_time = next_game_tick - System.currentTimeMillis();
     if (sleep_time >= 0) 
     {
       try 
       {
          Thread.sleep(sleep_time);
       } 
       catch (Exception e) {}
       }
       else 
       {
         Log.d("running behind: ", String.valueOf(sleep_time));
       }
     } 

To nie jest gładkie i to nie jest problem z "bieganie za"


Moim celem jest płynny ruch obrazu jak w pierwszym przykładzie kodu powyżej.

Możliwy błąd jest gdzie indziej, a potem patrzę. Proszę, czy ktoś może mi z tym pomóc?
Czy lepiej używać RENDERMODE_WHEN_DIRTY lub RENDERMODE_CONTINUOUSLY?

Dziękuję.
Author: Commanche, 2012-05-18

1 answers

Walczyłem z tym samym problemem przez kilka dni. Pętla wygląda gładko bez odniesienia czasu Androida, ale jak tylko zawiera dowolny rodzaj "synchronizacji czasu", czynniki zewnętrzne poza kontrolą rozwoju Androida wprowadzają poważne nieciągłości do ostatecznego wyniku.

Zasadniczo te czynniki to:

  • eglSwapInterval nie jest zaimplementowany w Androidzie, więc trudno jest poznać moment, w którym sprzęt wyświetla ostateczne losowanie na ekranie (Ekran sprzętowy sync)
  • wątek.sen nie jest precyzyjny. Wątek może spać mniej lub bardziej niż jest to wymagane.
  • SystemClock.System uptimeMillis ().nanoTime (), System.currentTimeMillis() i inne pomiary związane z czasem nie są dokładne(jego dokładne).

Problem jest niezależny od technologii rysowania (rysowanie, openGL 1.0/1.1 i 2.0) i metody pętli gry (stały krok czasu, interpolacja, zmienny krok czasu). Tak jak ty, próbowałem Thread.sen, szalone interpolacje, timery, itd. Nie ma znaczenia, co zrobisz, nie mamy kontroli nad tymi czynnikami.

Według wielu pytań i odpowiedzi na tej stronie, podstawowe zasady tworzenia płynnych, ciągłych animacji są następujące:]}

  • zmniejsz co najmniej GC, usuwając wszystkie żądania pamięci dynamicznej.
  • Renderuj ramki tak szybko, jak sprzęt może je przetwarzać (40 do 60 klatek na sekundę jest w porządku w większości urządzeń z Androidem).
  • Użyj stałych kroków czasu z interpolacją lub zmiennych kroków czasu.
  • zoptymalizuj fizykę aktualizacji i procedury draw, które należy wykonać we względnym stałym czasie bez wariancji wysokich szczytów.

Na pewno wykonałeś dużo pracy przed wysłaniem tego pytania, optymalizując updategame() i drawGame () (bez znaczącego GC i względnego stałego czasu wykonania) w celu uzyskania płynnej animacji w głównej pętli, jak wspomniałeś: "simple and absolute smooth".

Twój szczególny przypadek ze zmiennym czasem kroku i bez specjalnych wymagań, aby być w doskonałej synchronizacji z zdarzenia w czasie rzeczywistym (takie jak muzyka), rozwiązanie jest proste: "płynna zmienna czasu kroku".
Rozwiązanie współpracuje z innymi schematami pętli gry (stały krok czasu ze zmiennym renderowaniem) i jest łatwe do przeniesienia koncepcji (płynna ilość przesunięć wytwarzana przez updateGame i zegar czasu rzeczywistego w kilku klatkach.)

    // avoid GC in your threads. declare nonprimitive variables out of onDraw
    float smoothedDeltaRealTime_ms=17.5f; // initial value, Optionally you can save the new computed value (will change with each hardware) in Preferences to optimize the first drawing frames 
    float movAverageDeltaTime_ms=smoothedDeltaRealTime_ms; // mov Average start with default value
    long lastRealTimeMeasurement_ms; // temporal storage for last time measurement

    // smooth constant elements to play with
    static final float movAveragePeriod=40; // #frames involved in average calc (suggested values 5-100)
    static final float smoothFactor=0.1f; // adjusting ratio (suggested values 0.01-0.5)

    // sample with opengl. Works with canvas drawing: public void OnDraw(Canvas c)   
    public void onDrawFrame(GL10 gl){       
        updateGame(gl, smoothedDeltaRealTime_ms); // divide 1000 if your UpdateGame routine is waiting seconds instead mili-seconds.
        drawGame(gl);  

        // Moving average calc
        long currTimePick_ms=SystemClock.uptimeMillis();
        float realTimeElapsed_ms;
        if (lastRealTimeMeasurement_ms>0){
        realTimeElapsed_ms=(currTimePick_ms - lastRealTimeMeasurement_ms);
        } else {
                 realTimeElapsed_ms=smoothedDeltaRealTime_ms; // just the first time
        }
        movAverageDeltaTime_ms=(realTimeElapsed_ms + movAverageDeltaTime_ms*(movAveragePeriod-1))/movAveragePeriod;

         // Calc a better aproximation for smooth stepTime
        smoothedDeltaRealTime_ms=smoothedDeltaRealTime_ms +(movAverageDeltaTime_ms - smoothedDeltaRealTime_ms)* smoothFactor;

        lastRealTimeMeasurement_ms=currTimePick_ms;
    }

    // Optional: check if the smoothedDeltaRealTIme_ms is too different from original and save it in Permanent preferences for further use.

Dla stałego schematu stopni czasowych można zaimplementować przerywaną aktualizację w celu poprawy wyników:

float totalVirtualRealTime_ms=0;
float speedAdjustments_ms=0; // to introduce a virtual Time for the animation (reduce or increase animation speed)
float totalAnimationTime_ms=0;
float fixedStepAnimation_ms=20; // 20ms for a 50FPS descriptive animation
int currVirtualAnimationFrame=0; // useful if the updateGameFixedStep routine ask for a frame number

private void updateGame(){
    totalVirtualRealTime_ms+=smoothedDeltaRealTime_ms + speedAdjustments_ms;

    while (totalVirtualRealTime_ms> totalAnimationTime_ms){
        totalAnimationTime_ms+=fixedStepAnimation_ms;
        currVirtualAnimationFrame++;
        // original updateGame with fixed step                
        updateGameFixedStep(currVirtualAnimationFrame);
    }


    float interpolationRatio=(totalAnimationTime_ms-totalVirtualRealTime_ms)/fixedStepAnimation_ms;
    Interpolation(interpolationRatio);
}

Testowane z canvas i openGlES10 rysowanie na następujących urządzeniach: SG SII (57 FPS), SG Note (57 FPS), SG tab (60 FPS), unbranded Android 2.3 (43 FPS) powolny emulator działający na Windows XP(8 FPS). Platforma testowa rysuje około 45 obiektów + 1 ogromne tło (tekstura z obrazu źródłowego 70mp) poruszające się po ścieżce określonej w rzeczywistych parametrach fizycznych (km/h i G), bez kolców lub migotania między kilkoma urządzeniami (dobrze, 8 FPS na emulatorze nie wygląda dobrze, ale jego przepływ przy stałej prędkości zgodnie z oczekiwaniami)

Sprawdź wykresy jak Android zgłosić czas. Czasami Android zgłasza duży czas delta i tylko Następna pętla jest mała niż średnia, co oznacza przesunięcie na odczyt wartości w czasie rzeczywistym.

Tutaj wpisz opis obrazka

Bardziej szczegółowo: Tutaj wpisz opis obrazka

Jak ograniczyć liczbę klatek na sekundę podczas korzystania z Glsurfaceview Androida.RENDERMODE_CONTINUOUSLY?

System.currentTimeMillis vs System.nanoTime

Czy system metody.currentTimeMillis () zwraca bieżący czas?

 33
Author: javqui,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:15