Actualmente estoy empezando a aprender OpenGL en la escuela, y el otro día empecé a hacer un juego simple (solo, no para la escuela). Estoy usando Freeglut, y lo estoy construyendo en C, por lo que para mi ciclo de juego realmente había estado usando una función que pasé glutIdleFunc
para actualizar todo el dibujo y la física en una sola pasada. Esto estaba bien para animaciones simples que no me importaban demasiado la velocidad de fotogramas, pero dado que el juego se basa principalmente en la física, realmente quiero (necesito) vincular la velocidad de actualización.
Entonces, mi primer intento fue tener mi función que paso a glutIdleFunc
( myIdle()
) para realizar un seguimiento de cuánto tiempo ha pasado desde la llamada anterior y actualizar la física (y los gráficos actuales) cada tantos milisegundos. Solía timeGetTime()
hacer esto (usando <windows.h>
). Y esto me hizo pensar, ¿usar la función inactiva es realmente una buena forma de recorrer el ciclo del juego?
Mi pregunta es, ¿cuál es una mejor manera de implementar el bucle del juego en OpenGL? ¿Debo evitar usar la función inactiva?
Respuestas:
La respuesta simple es no, no desea utilizar la devolución de llamada glutIdleFunc en un juego que tiene algún tipo de simulación. La razón de esto es que esta función divorcia la animación y dibuja el código del manejo de eventos de la ventana, pero no de forma asincrónica. En otras palabras, recibir y entregar código de sorteo de eventos de ventana (o lo que sea que coloque en esta devolución de llamada), esto está perfectamente bien para una aplicación interactiva (interactuar, luego responder), pero no para un juego donde la física o el estado del juego deben progresar independiente de la interacción o el tiempo de procesamiento.
Desea desacoplar completamente el manejo de entrada, el estado del juego y el código de sorteo. Hay una solución fácil y limpia para esto que no involucra la biblioteca de gráficos directamente (es decir, es portátil y fácil de visualizar); desea que todo el ciclo del juego produzca tiempo y que la simulación consuma el tiempo producido (en fragmentos). Sin embargo, la clave es integrar la cantidad de tiempo que su simulación consumió en su animación.
La mejor explicación y tutorial que he encontrado sobre esto es Fix Your Timestep de Glenn Fiedler
Este tutorial tiene el para tratamiento completo, sin embargo, si usted no tiene una verdadera simulación de la física, puede omitir la verdadera integración, sino el bucle básico todavía se reduce a (detallado en pseudo-código):
Al hacerlo de esta manera, los bloqueos en su código de procesamiento, manejo de entrada o sistema operativo no hacen que su estado de juego se quede atrás. Este método también es portátil e independiente de la biblioteca de gráficos.
GLUT es una buena biblioteca, sin embargo, está estrictamente basada en eventos. Usted registra devoluciones de llamada y dispara el bucle principal. Siempre entrega el control de su bucle principal usando GLUT. Hay trucos para evitarlo , también puede simular un bucle externo utilizando temporizadores y demás, pero otra biblioteca es probablemente una mejor manera (más fácil) de hacerlo. Hay muchas alternativas, aquí hay algunas (algunas con buena documentación y tutoriales rápidos):
fuente
Creo que
glutIdleFunc
está bien; es esencialmente lo que terminarías haciendo a mano de todos modos, si no lo usaras. No importa lo que vaya a tener un ciclo cerrado que no se llama en un patrón fijo, y debe elegir si desea convertirlo en un ciclo de tiempo fijo o mantenerlo en un ciclo de tiempo variable y hacer Seguro que todas tus cuentas de matemáticas tienen tiempo de interpolación. O incluso una mezcla de estos, de alguna manera.Ciertamente puede tener una
myIdle
función a la que pasaglutIdleFunc
, y dentro de eso, medir el tiempo que ha pasado, dividirlo por su paso de tiempo fijo y llamar a otra función tantas veces.Esto es lo que no debes hacer:
fixedTimestep no se llamaría cada 10 milisegundos. De hecho, sería más lento que el tiempo real, y podría terminar falsificando números para compensar cuando realmente la raíz del problema radica en la pérdida del resto cuando se divide
timeDifference
por 10.En cambio, haz algo como esto:
Ahora, esta estructura de bucle garantiza que su
fixedTimestep
función se llame una vez cada 10 milisegundos, sin perder milisegundos como resto ni nada por el estilo.Debido a la naturaleza multitarea de los sistemas operativos, no puede confiar en que una función se llame exactamente cada 10 milisegundos; pero el bucle anterior sucedería lo suficientemente rápido como para que sea lo suficientemente cerca, y puede suponer que cada llamada
fixedTimestep
incrementaría su simulación en 10 milisegundos.Lea también esta respuesta y especialmente los enlaces proporcionados en la respuesta.
fuente