2012-05-18 20 views
18


tengo problemas con el desplazamiento suave en OpenGL (pruebas en SGS2 y ACE)
que crean aplicaciones sencillas - solamente desplazamiento horizontal velocidad fija de imágenes, o sólo la imagen de un (jugador) que se mueve por el acelerador, pero es movimiento no es suave :-(
he intentado muchas diferentes de código, pero ninguna satisfacción ...Android bucle de juego sin problemas

principio traté de trabajo con GLSurfaceView.RENDERMODE_CONTINUOUSLY y puse todo el código a onDrawFrame:

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

este es el más simple y absoluto suave !! - pero es función de la velocidad del hardware (= inútil)


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

este es el mejor de todos, pero no es tan suave como la anterior, a veces película


segundo Probé GLSurfaceView.RENDERMODE_WHEN_DIRTY , en onDrawFrame sólo he objetos de dibujo y este código en hilo separado:

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

esto no es suave y no es proble m con "corriendo detrás"


Mi objetivo es un movimiento suave de la imagen como en el ejemplo del primer código anterior.

Posible error es en otro lugar que el que busco. Por favor, ¿alguien puede ayudarme con esto?
¿Es mejor usar RENDERMODE_WHEN_DIRTY o RENDERMODE_CONTINUOUSLY?

Gracias.

+1

'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

+0

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

Respuesta

32

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.

enter image description here

con más detalle: 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

muchas gracias javgui, lo probaré lo antes posible y después informaré ... – Commanche

+0

Hola de nuevo, entonces, debe haber un error en este ejemplo - el valor de smoothTimeStep se incrementa repetidamente, pero si intento "smoothTimeStep = gameTimeElapsed * smoothFactor + (temp-gameTimeElapsed)" - se ve bien :-) Ahora es mucho mejor, pero a veces también (no tan seguido) - registré smoothTimeStep y aquí están los valores: 0.021 0.021 0.021 ........ 0.06 0.095 0.03 0.008 0.007 0.005 0.021 0.021 Quizás esto solo sea un problema en Samsung HW (probado en SGS2), probaré en otro dispositivo. – Commanche

+0

Tienes razón. En el proceso de simplificación y adaptar mi código manualmente, tecleo la fórmula equivocada a medida que citas. (arreglado y probado en dispositivos reales ahora). Mi caso requiere sincronización con la música en segundo plano (audio y animación se ejecuta en sincronización). Edito la respuesta para que se ajuste a sus requisitos de una mejor manera sin tener en cuenta el requisito de la música en tiempo real. (media móvil). Pruebe este código, y si se siente feliz, sugiero que edite el título para una pregunta más general como "Android smooth loop". El problema (su pregunta) y la solución no están relacionados con la tecnología de dibujo (canvas o openGL). – javqui

Cuestiones relacionadas