Timer Resolution - Contador temporal de resolución. Java Game 6
El Timer Resolution o contador de resolución en un juego es el tiempo mínimo que tarda en sucederse una imagen en pantalla para que parezca continua en el ojo humano.
Por ejemplo, diff nos calula la diferencia de tiempo reciente en milisegundos tomando como referencia que t1 y t2 nos pueden medir la diferencia de tiempo en el que se realiza un bucle run() para un juego.
long t1 = System.currentTimeMillis ();
long t2 = System.currentTimeMillis ();
long diff = t2 - t1; // en ms
currentTimeMillis() nos da el valor de resolución en milisegundos, que desafortunadamente depende del sistema operativo.
En Windows 95 y 98 la resolución es de 55 ms, lo que significa que las llamadas a currentTimeMillis() sólo devolverán valores diferentes aproximadamente cada 55 ms.
En el bucle de animación run(), el efecto general de una mala resolución hace que la animación se ejecute más lento de lo previsto y reduce el FPS. Esto se debe al valor timeDiff, que se establecerá en 0 si la actualización del juego y el tiempo de renderizado es inferior a 55 ms. Esto hace que el tiempo de reposo sea asignado al valor del período de iteración, haciendo que cada iteración duerma más de lo necesario.
Windows 2000, NT y XP, currentTimeMillis() tiene una resolución de 10 a 15 ms, lo que hace posible obtener de 67 a 100 FPS. Esto se considera aceptable para la mayoria de los juegos.
En Mac OS X y Linux hay resoluciones del temporizador de 1 ms, lo que es excelente.
Para calcular los FPS (frames por segundo) a partir de un currentTimeMillis() dado de 15ms, simplemente tenemos que pasar de 15ms a segundos, lo que serían 0,015segundos, que para un frame vendría dado por el cociente entre 1Frame/0,015 dándonos como resultado aproximado 67FPS, una medida más aproximada de lo que queremos conseguir para un juego.
Si lo que queremos es hacer el cálculo en nanosegundos simplemente tendríamos que hacer lo mismo:
long count1 = System.nanoTime( );
long count2 = System.nanoTime( );
long diff = (count2 – count1); // in nanoseconds
Para la medición del Timer Resolution tenemos dos clases, una principal llamada TimerRes(), y otra secundaria llamada StopWatch():
Tendremos dos métodos sysTimeResolution() y perfTimeResolution(), el primero realiza el cálculo como hemos explicado anteriormente, y el segundo realiza el cálculo a través del método Perf, siempre un poco mas exacto. Los métodos start() y stop() añaden una pequeña sobrecarga al contador, como se ilustra en el método perfTimeResolution() en TimerRes.
El ciclo de animación en run() depende de un buen temporizador y de la precisión de la llamada a la función sleep().
Otra manera interesante de ver el tiempo que perdemos en la máquina virtual de java 'JVM' con el método currentTimeMillis() es a través de la siguiente clase en la que creamos una variable delay a la que le imponemos un retardo sleep() y calculamos su porcentaje de pérdida:
El resultado será:
Ahora expondremos otro ejemplo del método run() para un juego:
en este próximo ejemplo se calcula el tiempo de ejecución de sleep () y su error (almacenado en overSleepTime). Thread.yield() se utiliza para dar a otros subprocesos una oportunidad de ejecutarse si la animación no ha dormido por un tiempo.
Aquí está el método run() actualizado:
Si la llamada a sleep() duerme durante 12 ms en vez de los 10 ms deseados, entonces a overSleepTime se le asignará 2 ms. En la siguiente iteración del bucle este valor se deducirá
del tiempo de reposo, reduciéndolo a 2 ms, de esta manera las inexactitudes de sleep se corrigen.
Si los pasos de actualización y reproducción del juego duran más que el período de iteración, entonces SleepTime tendrá un valor negativo y esta iteración no incluirá un sleep, esto hace que el contador noDelays se incremente, y cuando alcanza NO_DELAYS_PER_YIELD, yield() será llamado. Esto permite que otros subprocesos se ejecuten si es necesario, y evita el uso de un período de descanso arbitrario en run().
Los valores de tiempo cambian de milisegundos a nanoseconds, lo que motiva el cambio a variables largas. Además, el tiempo se debe convertir de nanoseconds a milisegundos antes de llamar a sleep(), o estaré esperando mucho tiempo para que el juego se reanude.
Por ejemplo, diff nos calula la diferencia de tiempo reciente en milisegundos tomando como referencia que t1 y t2 nos pueden medir la diferencia de tiempo en el que se realiza un bucle run() para un juego.
long t1 = System.currentTimeMillis ();
long t2 = System.currentTimeMillis ();
long diff = t2 - t1; // en ms
currentTimeMillis() nos da el valor de resolución en milisegundos, que desafortunadamente depende del sistema operativo.
En Windows 95 y 98 la resolución es de 55 ms, lo que significa que las llamadas a currentTimeMillis() sólo devolverán valores diferentes aproximadamente cada 55 ms.
En el bucle de animación run(), el efecto general de una mala resolución hace que la animación se ejecute más lento de lo previsto y reduce el FPS. Esto se debe al valor timeDiff, que se establecerá en 0 si la actualización del juego y el tiempo de renderizado es inferior a 55 ms. Esto hace que el tiempo de reposo sea asignado al valor del período de iteración, haciendo que cada iteración duerma más de lo necesario.
Windows 2000, NT y XP, currentTimeMillis() tiene una resolución de 10 a 15 ms, lo que hace posible obtener de 67 a 100 FPS. Esto se considera aceptable para la mayoria de los juegos.
En Mac OS X y Linux hay resoluciones del temporizador de 1 ms, lo que es excelente.
Para calcular los FPS (frames por segundo) a partir de un currentTimeMillis() dado de 15ms, simplemente tenemos que pasar de 15ms a segundos, lo que serían 0,015segundos, que para un frame vendría dado por el cociente entre 1Frame/0,015 dándonos como resultado aproximado 67FPS, una medida más aproximada de lo que queremos conseguir para un juego.
Si lo que queremos es hacer el cálculo en nanosegundos simplemente tendríamos que hacer lo mismo:
long count1 = System.nanoTime( );
long count2 = System.nanoTime( );
long diff = (count2 – count1); // in nanoseconds
Para la medición del Timer Resolution tenemos dos clases, una principal llamada TimerRes(), y otra secundaria llamada StopWatch():
Tendremos dos métodos sysTimeResolution() y perfTimeResolution(), el primero realiza el cálculo como hemos explicado anteriormente, y el segundo realiza el cálculo a través del método Perf, siempre un poco mas exacto. Los métodos start() y stop() añaden una pequeña sobrecarga al contador, como se ilustra en el método perfTimeResolution() en TimerRes.
El ciclo de animación en run() depende de un buen temporizador y de la precisión de la llamada a la función sleep().
Otra manera interesante de ver el tiempo que perdemos en la máquina virtual de java 'JVM' con el método currentTimeMillis() es a través de la siguiente clase en la que creamos una variable delay a la que le imponemos un retardo sleep() y calculamos su porcentaje de pérdida:
El resultado será:
Ahora expondremos otro ejemplo del método run() para un juego:
en este próximo ejemplo se calcula el tiempo de ejecución de sleep () y su error (almacenado en overSleepTime). Thread.yield() se utiliza para dar a otros subprocesos una oportunidad de ejecutarse si la animación no ha dormido por un tiempo.
Aquí está el método run() actualizado:
Si la llamada a sleep() duerme durante 12 ms en vez de los 10 ms deseados, entonces a overSleepTime se le asignará 2 ms. En la siguiente iteración del bucle este valor se deducirá
del tiempo de reposo, reduciéndolo a 2 ms, de esta manera las inexactitudes de sleep se corrigen.
Si los pasos de actualización y reproducción del juego duran más que el período de iteración, entonces SleepTime tendrá un valor negativo y esta iteración no incluirá un sleep, esto hace que el contador noDelays se incremente, y cuando alcanza NO_DELAYS_PER_YIELD, yield() será llamado. Esto permite que otros subprocesos se ejecuten si es necesario, y evita el uso de un período de descanso arbitrario en run().
Los valores de tiempo cambian de milisegundos a nanoseconds, lo que motiva el cambio a variables largas. Además, el tiempo se debe convertir de nanoseconds a milisegundos antes de llamar a sleep(), o estaré esperando mucho tiempo para que el juego se reanude.
Comentarios
Publicar un comentario