2012-05-18 13 views
18


mam problemy z płynnego przewijania w OpenGL (badania na SGS2 i ACE)
I stworzył prostych aplikacji - tylko stałej prędkości poziome przewijanie obrazów, czy tylko jedno zdjęcie (odtwarzacz) przeniesienie przez akcelerator, ale ruch nie jest płynny :-(
próbowałem wielu różnych kodu, ale bez satysfakcji ...Android pętla gładkie gry

pierwszy próbowałem pracę z GLSurfaceView.RENDERMODE_CONTINUOUSLY i umieścić cały kod do onDrawFrame:

public void onDrawFrame(GL10 gl) 
    { 
     updateGame(gl, 0.017f); 
     drawGame(gl); 
    } 

to najprostszy i absolutnie gładki !! - ale to zależy od szybkości sprzętowej (= bezużyteczny)


public void onDrawFrame(GL10 gl) 
    { 
     frameTime = SystemClock.elapsedRealtime();   
     elapsedTime = (frameTime - lastTime)/1000; 
     updateGame(gl, elapsedTime); 
     drawGame(gl); 
     lastTime = frameTime; 
    } 

to jest najlepszy z wszystkich, ale to nie jest tak gładka jak poprzedni, czasami ruchem


drugim próbowałem GLSurfaceView.RENDERMODE_WHEN_DIRTY w onDrawFrame mam rysunek tylko obiekty i ten kod w oddzielnym 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ładka i nie jest proble mz "bieganiem"


Moim celem jest płynny ruch obrazu jak w pierwszym przykładzie kodu powyżej.
Możliwy błąd jest gdzieś indziej, niż szukam. Proszę, czy ktoś może mi w tym pomóc?
Czy lepiej używać RENDERMODE_WHEN_DIRTY lub RENDERMODE_CONTINUOUSLY?

Dziękuję.

+1

'ruchu przez akcelerator' akcelerometry daje dane z powrotem Jumpy. Czy to może być twój problem? Spróbuj wygładzić ruch, wykonując coś w stylu "someLoc + = (newLoc - someLoc) * .1f;" – zezba9000

+0

witam, jeśli spróbuję wyłączyć akcelerometr i tylko proste obrazy ruchome o stałej wartości to sporadycznie pstrykam też (próbuję to z RENDERMODE_WHEN_DIRTY teraz) – Commanche

Odpowiedz

32

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

Zasadniczo, te czynniki są:

  • eglSwapInterval nie jest realizowany w Androidzie, więc trudno jest znać moment gdy sprzęt narazić końcowy remis na ekranie (sprzęt ekranem Sync)
  • wątek. sen nie jest precyzyjny. Wątek może spać mniej lub więcej niż wymagany.
  • SystemClock.uptimeMillis() System.nanoTime(), System.currentTimeMillis() i inne pomiary związane z czasem nie są dokładne (dokładne).

Problem jest niezależny od technologii rysowania (rysunek, OpenGL 1.0/1.1 i 2.0) oraz metody pętli gry (ustalony krok czasowy, interpolacja, etap zmiennej fazy). Tak jak ty, próbowałem Thread.sleep, szalonych interpolacji, timerów itd. Nie ma znaczenia, co zrobisz, nie mamy kontroli nad tymi czynnikami.

Zgodnie z wielu Q & A na tej stronie, to podstawowe zasady, aby produkować płynne animacje są:

  • Zmniejszyć przy minimum GC usuwając wszystkie żądania pamięci dynamicznej.
  • Renderuj klatki tak szybko, jak sprzęt może je przetworzyć (od 40 do 60 fps jest w porządku na większości urządzeń z Androidem).
  • Używaj stałych stopni czasu z interpolacją lub zmiennymi krokami czasowymi.
  • Optymalizuj fizykę aktualizacji i procedury rysowania, które mają być wykonywane w względnym stałym czasie bez wariancji wysokich pików.

Z pewnością wykonałeś wiele wcześniejszych prac przed opublikowaniem tego pytania, optymalizując metodę updateGame() i drawGame() (bez znaczącej wartości GC i względnego stałego czasu wykonania), aby uzyskać płynną animację w głównym pętla jak wspominasz: "prosta i absolutnie gładka".

Twój szczególny przypadek ze zmiennym czasem kroku i bez specjalnych wymagań, aby być w doskonałej synchronizacji z wydarzeniami w czasie rzeczywistym (jak muzyka), rozwiązanie jest proste: "wygładź zmienną kroku czasu".
Roztwór współpracuje z innymi systemami pętli gry (stałe taktu o zmiennej renderowania) i jest łatwe do przebicia Pojęcie (gładkich wielkość przemieszczenia wytwarzanego przez updateGame i zegar czasu rzeczywistego na kilku ramkach.)

// 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. 

na schemacie kroku stałej czasowej intermetiate updateGame mogą być realizowane 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 płótna i openGlES10 rysunek z następującymi urządzeniami: SII SG (57 fps), SG Note (57 FPS), Kartę SG (60 FPS), niemarkowy runni z Androidem 2.3 (43 FPS) ng 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ącego się po ścieżce określonej w rzeczywistych parametrach fizycznych (km/h i G), bez skoków lub drgań między kilkoma urządzeniami (cóż, 8 FPS na emulatorze nie wygląda dobrze, ale jego przepływ odbywa się ze stałą prędkością zgodnie z oczekiwaniami)

Sprawdź wykresy pokazujące, jak android raportuje czas. Czasami Android zgłasza duży czas delta, a kolejna pętla jest mała od średniej, co oznacza przesunięcie odczytu wartości czasu rzeczywistego.

enter image description here

bardziej szczegółowo: enter image description here

How to limit framerate when using Android's GLSurfaceView.RENDERMODE_CONTINUOUSLY?

System.currentTimeMillis vs System.nanoTime

Does the method System.currentTimeMillis() really return the current time?

+0

dziękuję bardzo javgui, przetestuję to jak najszybciej i po tym, jak zgłoszę ... – Commanche

+0

Witam ponownie, , więc w tym przykładzie musi wystąpić błąd - wartość smoothTimeStep jest wielokrotnie zwiększana, ale jeśli spróbuję "smoothTimeStep = gameTimeElapsed * smoothFactor + (temp-gameTimeElapsed)" - wygląda ładnie :-) Teraz jest znacznie lepiej, ale czasami też trzepnąć (nie tak często) - logowałem się smoothTimeStep i tutaj jego wartości: 0.021 0.021 0.021 ........ 0.06 0.095 0.03 0.008 0.007 0.005 0.021 0.021 Może to problem tylko na Samsung HW (testowany na SGS2), spróbuję na innym urządzeniu. – Commanche

+0

Masz rację. W procesie upraszczania i dostosowywania mojego kodu ręcznie dla ciebie, wpisuję niewłaściwą formułę. (naprawione i przetestowane w rzeczywistych urządzeniach teraz). Moja sprawa wymaga synchronizowania z muzyką w tle (audio, animacja działa zsynchronizowana). Edytuję odpowiedź, aby dopasować się do twoich wymagań w lepszy sposób, bez wymogu posiadania muzyki w czasie rzeczywistym. (średnia krocząca). Wypróbuj ten kod, a jeśli czujesz się szczęśliwy, sugerujemy wyedytować tytuł dla bardziej ogólnego pytania, na przykład "Pętla z płynną grą na Androida". Problem (twoje pytanie) i rozwiązanie nie są związane z technologią rysowania (płótno lub openGL). – javqui