2012-05-18 17 views
18


ho problemi con scorrimento continuo in OpenGL (test su SGS2 e ACE)
ho creato semplici applicazioni - solo velocità di scorrimento orizzontale fissa di immagini, o di una sola immagine (giocatore) in movimento da acceleratore, ma è il movimento non è :-(liscia
ho provato molte diverse di codice, ma nessuna soddisfazione ...Android ciclo di gioco liscia

prima ho cercato lavoro con GLSurfaceView.RENDERMODE_CONTINUOUSLY e ho messo tutto il codice a onDrawFrame:

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

questo è il più semplice e assoluto liscio !! - ma è dipendente dalla velocità hardware (= inutile)


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

questo è il migliore di tutti, ma non è liscia come il precedente, talvolta flick


secondo ho provato GLSurfaceView.RENDERMODE_WHEN_DIRTY , in onDrawFrame ho solo oggetti di disegno e questo codice in thread separato:

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)); 
     } 
    } 

questo non è uniforme e non è proble m con "running behind"


Il mio obiettivo è il movimento delle immagini come nel primo esempio di codice sopra.

Possibile errore è da qualche altra parte, quindi sto cercando. Per favore, qualcuno può aiutarmi con questo?
È meglio utilizzare RENDERMODE_WHEN_DIRTY o RENDERMODE_CONTINUOUSLY?

Grazie.

+1

'in movimento da acceleratore' accelerometri ti danno dati indietro nervosi. Potrebbe essere il tuo problema? Prova ad appianare il movimento facendo qualcosa come "someLoc + = (newLoc - someLoc) * .1f;" – zezba9000

+0

ciao, se provo a disattivare l'accelerometro e solo le immagini di spostamento semplice di valore costante è anche flick sporadico (provalo con RENDERMODE_WHEN_DIRTY ora) – Commanche

risposta

32

Stavo combattendo con lo stesso identico problema per diversi giorni. Il ciclo appare fluido senza alcun riferimento temporale Android, ma non appena include qualsiasi tipo di "sincronizzazione temporale", fattori esterni fuori dal controllo di sviluppo di Android introducono serie discontinuità al risultato finale.

In sostanza, questi fattori sono:

  • eglSwapInterval non è implementata in Android, quindi è difficile sapere il momento in cui l'hardware esporre all'estrazione finale nella schermata (schermata di sincronizzazione hardware)
  • Discussione. il sonno non è preciso La discussione può dormire più o meno di quanto richiesto.
  • SystemClock.uptimeMillis() System.nanoTime(), System.currentTimeMillis() e altre misurazioni relative ai tempi non sono accurate (è preciso).

Il problema è indipendente dalla tecnologia di disegno (disegno, openGL 1.0/1.1 e 2.0) e dal metodo del ciclo di gioco (fase temporale fissa, interpolazione, passo temporale variabile). Come te, stavo provando Thread.sleep, interpolazioni folli, timer, ecc. Non importa cosa farai, non abbiamo il controllo su questi fattori.

Secondo con molte Q & A su questo sito, le regole di base per la produzione di animazioni continue lisce sono:

  • Ridurre al minimo il GC, eliminando tutte le richieste di memoria dinamica.
  • Rende i fotogrammi quanto più velocemente l'hardware può elaborarli (da 40 a 60 fps è ok nella maggior parte dei dispositivi Android).
  • Utilizzare passaggi temporali fissi con interpolazione o passi temporali variabili.
  • Ottimizzare la fisica dell'aggiornamento e disegnare le routine da eseguire in tempo costante relativo senza variazioni di picchi elevati.

Di sicuro, hai fatto un sacco di lavoro precedente prima di posta questa domanda ottimizzando l'updateGame() e drawGame() (senza apprezzabile GC e relativo tempo di esecuzione costante) al fine di ottenere un'animazione fluida nel vostro principale loop come si menziona: "semplice e assoluto liscio".

Il tuo caso particolare con stepTime variabile e nessun requisito speciale per essere perfettamente sincronizzati con eventi realTime (come la musica), la soluzione è semplice: "uniformare il passo Variabile temporale".
La soluzione funziona con altri sistemi di gioco di loop (passo temporale fissato con il rendering variabili) ed è facile da porta il concetto (liscio la quantità di spostamento prodotto dalla updateGame e l'orologio in tempo reale tra diverse cornici.)

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

per uno schema passo tempo fisso, un updateGame intermetiate possono essere implementate per migliorare i risultati:

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); 
} 

Testata con tela e openGlES10 disegno con i seguenti dispositivi: SG SII (57 FPS), SG Nota (57 FPS), Scheda SG (60 FPS), runni emulatore unbrando Android 2.3 (43 FPS) su Windows XP (8 FPS). La piattaforma di test disegna circa 45 oggetti + 1 sfondo enorme (trama da immagine sorgente 70MP) muovendosi lungo un percorso specificato in parametri fisici reali (km/he G), senza picchi o flick tra più dispositivi (beh, 8 FPS sull'emulatore non sembra buono, ma il suo flusso a velocità costante come previsto)

Controllare i grafici per come Android riporta l'ora. Alcune volte Android segnala un tempo di delta elevato e solo il ciclo successivo è inferiore alla media, ovvero un offset sulla lettura del valore realTime.

enter image description here

con maggiore dettaglio: 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

grazie mille javgui, testerò questo più presto e dopo riporterò ... – Commanche

+0

Ciao di nuovo, così, ci deve essere un errore in questo esempio - valore della smoothTimeStep viene ripetutamente aumentato, ma se provo "smoothTimeStep = gameTimeElapsed * smoothFactor + (temp-gameTimeElapsed)" - è un aspetto gradevole :-) Ora è il molto meglio, ma a volte anche flick (non così spesso) - Ho registrato smoothTimeStep e qui sono i valori: 0.021 0.021 0.021 ........ 0.06 0.095 0.03 0.008 0.007 0.005 0.021 0.021 Forse questo è un problema solo su Samsung HW (testato su SGS2), proverò su un altro dispositivo. – Commanche

+0

Hai ragione. Nel processo di semplificare e adattare il mio codice manualmente per te, digito la formula sbagliata come citi. (corretto e testato in dispositivi reali ora). Il mio caso richiede il sincronismo con la musica sullo sfondo (l'audio è sincronizzato con un'animazione). Modifico la risposta per soddisfare le tue esigenze in un modo migliore senza pensare alla musica in tempo reale. (media mobile). Prova questo codice e, se ti senti felice, ti suggerisco di modificare il titolo per una domanda più generale come "Android smooth game loop". Il problema (la tua domanda) e la soluzione non sono correlati alla tecnologia di disegno (tela o openGL). – javqui