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