Estuve peleando con el mismo problema durante varios días. El ciclo se ve sin ninguna referencia de tiempo de Android, pero tan pronto como incluye cualquier tipo de "sincronización de tiempo", los factores externos del control de desarrollo de Android introducen serias discontinuidades en el resultado final.
Básicamente, estos factores son:
- eglSwapInterval no está implementado en Android, por lo que es difícil saber el momento en que el hardware exponer el sorteo final en la pantalla (pantalla de sincronización de hardware)
- hilo. el sueño no es preciso. El subproceso puede dormir más o menos de lo solicitado.
- SystemClock.uptimeMillis() System.nanoTime(), System.currentTimeMillis() y otras medidas relacionadas con la temporización no son precisas (es precisa).
El problema es independiente de la tecnología de dibujo (dibujo, OpenGL 1.0/1.1 y 2.0) y el método de bucle del juego (paso de tiempo fijo, interpolación, paso de tiempo variable). Como tú, estaba intentando Thread.sleep, interpolaciones locas, temporizadores, etc. No importa lo que hagas, no tenemos control sobre estos factores.
De acuerdo con muchos Q & A en este sitio, las reglas básicas para producir animaciones continuas lisas son:
- Reducir al mínimo el GC mediante la eliminación de todas las solicitudes de memoria dinámica.
- Renderice las tramas tan rápido como el hardware pueda procesarlas (40 a 60 fps está bien en la mayoría de los dispositivos Android).
- Utilice pasos de tiempo fijos con interpolación o pasos de tiempo variables.
- Optimizar la física de actualización y dibujar rutinas para ejecutar en tiempo constante relativo sin varianza de altos picos.
Por supuesto, usted hizo una gran cantidad de trabajo previo antes de post esta pregunta mediante la optimización de su updateGame() y drawGame() (sin GC apreciable y tiempo de ejecución relativamente constante) con el fin de obtener una animación fluida en su principal bucle como usted menciona: "sencillo y absoluto suave".
Su caso particular con stepTime variable y sin requisitos especiales para estar en perfecta sincronización con los eventos realTime (como la música), la solución es simple: "suavizar la variable Time del paso".
La solución trabaja con otros esquemas de bucle de juego (paso de tiempo fijo con la representación de variables) y es fácil de portar el concepto (suavizar la cantidad de desplazamiento producido por el updateGame y el reloj de tiempo real a través de varios cuadros.)
// 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.
para un esquema de paso de tiempo fijo, un updateGame intermetiate se pueden implementar para mejorar los resultados:
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);
}
Probado con lienzo y openGlES10 dibujo con los siguientes dispositivos: SII SG (57 FPS), SG Nota (57 FPS), Pestaña SG (60 FPS), Android 2.3 sin autorización (43 FPS) slow runner emulador ng en Windows XP (8 FPS). La plataforma de prueba dibuja alrededor de 45 objetos + 1 fondo grande (textura de la imagen de origen de 70MP) moviéndose a lo largo de un camino especificado en parámetros físicos reales (km/hy G), sin picos o deslizamiento entre varios dispositivos (bueno, 8 FPS en el emulador no se ve bien, pero su flujo a velocidad constante como se esperaba)
Verificar Los gráficos de cómo informa Android la hora. Algunas veces, Android informa un gran tiempo delta y solo el siguiente ciclo es más pequeño que el promedio, lo que significa un desplazamiento en la lectura del valor en tiempo real.
con más detalle:
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?
'se mueve por el acelerador de' Los acelerómetros se dan datos de nuevo nerviosos. ¿Podria ese ser tu problema? Intente suavizar el movimiento haciendo algo como "someLoc + = (newLoc - someLoc) * .1f;" – zezba9000
hola, si trato de desactivar el acelerómetro y solo mover las imágenes por valor constante, también es esporádico (intentándolo con RENDERMODE_WHEN_DIRTY ahora) – Commanche