Separar la lógica / actualización del código de render / dibujo en un solo hilo usando el modo de suspensión

9

He leído que la velocidad de los objetos del juego no debe verse obstaculizada por FPS, sino que debe basarse en el tiempo. ¿Cómo puedo separar el código de actualización / dibujo para maximizar el rendimiento sin limitar la velocidad de dibujo y proporcionar una velocidad de actualización lógica constante basada en el tiempo?

Mi pseudocódigo actual es el siguiente

loop
{
    draw();
    if (ticksElapsed() > 100)
    {
        update();
        ticks+= ticksElapsed();
    }        
}

El problema es que el código de dibujo dificulta el rendimiento de la tasa de actualización (). Y consume 100% de CPU porque si se le duerme, se liberan ambas funciones de dibujo / lógica.

También estoy usando SDL y no parece tener una opción vsync. También he oído hablar de los términos fijo y variable, sin embargo, no estoy seguro de cómo se puede hacer con el sueño ()

Oskenso Kashi
fuente
1
No necesita desperdiciar el 100% de la potencia de la CPU solo por esperar, ponga un sueño (0) al final de los bucles while si ticksElapsed () <100. El sistema operativo volverá al subproceso inmediatamente si no hay otro subproceso que quiere correr Pero ya no desperdicia el 100% de la potencia de la CPU.
Maik Semder
Sin embargo, la mejor solución para tal configuración de 1 subproceso es usar vsync, si no puede vsync, luego llame a dormir (0) en un bucle hasta que alcance la velocidad de fotogramas objetivo, luego actualice y dibuje
Maik Semder

Respuestas:

3

En su fragmento de código, parece que está intentando ejecutar su juego en modo de paso de tiempo fijo esperando ocupado si su dibujo y actualización tomó menos de 15 ms (60 fps). Esto es posible y has acertado al decir que esto no se puede hacer usando una llamada de suspensión porque no sabes exactamente cuánto tiempo vas a dormir. El ocupado-esperando-loop es la buena solución.

Sin embargo, considere el caso en el que su actualización y dibujo superan los 15 ms, ahora tiene el dibujo y la actualización del juego más lentos. Ahora puede hacer dos cosas: detectar este estado y soltar cuadros (omitir el dibujo y pasar directamente a la actualización hasta que esté sincronizado nuevamente), sin embargo, si la computadora está lenta, nunca se pondrá al día.

Otra solución es hacer que su lógica de actualización sea independiente del tiempo fijo. No necesita un hilo separado para esto, solo tiene que respetar lo rápido que deberían moverse las cosas. En lugar de 5 píxeles por marca, debe usar 50 píxeles por segundo. Necesitaría un temporizador de alta precisión para lograr esto, y toda su lógica de actualización debería poder acceder al temporizador para ver cuánto tiempo pasó desde la última actualización.

Básicamente vas de:

void UpdatePlayer()
 player.x += 10;

A

void UpdatePlayer(float elapsedSeconds) //the total seconds elapsed since last update
 player.x += walkspeed * elapsedSeconds;
Roy T.
fuente
¿Entonces mi motor siempre consumirá el 100% y no hay nada que realmente pueda hacer al respecto?
Oskenso Kashi
1
Consumirá tantos ciclos como lo permita el programador, pero eso no es realmente un problema, ya que no puedes hacer muchas otras cosas mientras juegas un juego :).
Roy T.
@Oskenso Sin embargo, es un problema si usas más de 1 hilo, entonces el hilo principal no permitirá que los demás corran tanto como puedan, desperdiciando mucha potencia computacional en el ciclo while, realmente deberías considerar dormir
Maik Semder
@Maik Semder: ¿tiene una solución para que el sueño (x) no sea preciso? Una vez que ha pasado el intervalo de suspensión, el subproceso está listo para ejecutarse. Pero no se garantiza que un subproceso listo se ejecute de inmediato. Eso depende del planificador. Cuando usa dos hilos hay otras soluciones, para eso vea este excelente artículo: altdevblogaday.com/2011/07/03/threading-and-your-game-loop
Roy T.
1
@Roy sleep (0) es la solución. Regresa inmediatamente si no hay otro hilo que quiera ejecutarse ( Sleep WinAPI ) y le da la oportunidad a otros hilos de ejecutarse. Si el otro subproceso no le da la oportunidad al subproceso principal de ejecutarse a cambio, entonces tiene un problema de subprocesamiento, pero bloquear todo lo demás al no llamar a dormir en primer lugar lo hace aún peor y no es una solución. La clave es llamar a la suspensión (0) y probar el tiempo transcurrido hasta alcanzar la velocidad de fotogramas fija objetivo, para no perder el 100% de la CPU solo por esperar.
Maik Semder