haskell reactive-banana

haskell - ¿Cómo implementar un bucle de juego en banana reactiva?



reactive-banana (1)

Para esta discusión, la parte en la que estoy más interesado es en organizar de manera que la llamada al motor de física (la llamada a integrar) siempre esté marcada por dt. ¿Reactive-banana permite al usuario escribir este bucle de estilo?

Sí, el plátano reactivo puede hacer eso.

La idea es que escriba un bucle de evento personalizado y enganche reactive-banana en eso. La biblioteca no hace suposiciones sobre de dónde obtiene sus eventos, sino que "solo" resuelve el problema de describir de forma clara los nuevos eventos en términos de los existentes. En particular, puede usar la función newAddHandler para crear dos funciones de devolución de llamada que se llaman en los lugares apropiados en el bucle de eventos. Esencialmente, reactive-banana es solo un método alucinante para escribir funciones de devolución de llamada ordinarias que mantienen el estado. Cuándo y cómo llamas a estas funciones depende de ti.

Aquí un esquema general:

-- set up callback functions (renderEvent, render) <- newAddHandler (stateUpdateEvent, stateUpdate) <- newAddHandler -- make the callback functions do something interesting let networkDescription = do eRender <- fromAddHandler renderEvent eStateUpdate <- fromAddHandler stateUpdateEvent ... -- functionality here actuate =<< compile networkDescription -- event loop while (! quit) { ... while (accumulator >= dt) { stateUpdate (t,dt) -- call one callback t += dt accumulator -= dt } ... render () -- call another callback }

De hecho, he escrito un ejemplo de bucle de juego en este estilo para una versión anterior de reactive-banana, pero no he logrado pulirlo ni publicarlo en hackage. Las cosas importantes que me gustaría ver completadas son:

  • Elija un motor gráfico que sea fácil de instalar y que funcione en GHCi. El concepto utiliza SDL, pero esto es realmente incómodo ya que no se puede usar desde GHCi. Algo como OpenGL + GLFW sería bueno.
  • Ofrezca una pequeña abstracción para facilitar la escritura de la fase de interpolación. Probablemente solo dos cosas: un evento eTimer :: Event t () que representa las actualizaciones físicas regulares y un comportamiento bSinceLastTimer :: Behavior t TimeDiff que mide el tiempo desde las últimas actualizaciones físicas, que se puede usar para hacer la interpolación. (Es un comportamiento en lugar de un evento, por lo que las actualizaciones internas de "dibujar esto" son transparentes.)

El clon del apagón de Andreas Bernstein que utiliza banano reactivo puede ser un excelente ejemplo para implementar en este estilo.

Esta pregunta es específica para simulaciones de banano reactivo y en tiempo real con un componente físico y visual (por ejemplo, juegos).

Según Fix Your Timestep! la forma ideal de configurar un bucle de juego (suponiendo que la física debe ser reproducible), necesita un paso de tiempo fijo entre los marcos Después de considerar una serie de complicaciones reales, el autor llega a este bucle de juego:

double t = 0.0; const double dt = 0.01; double currentTime = hires_time_in_seconds(); double accumulator = 0.0; State previous; State current; while ( !quit ) { double newTime = time(); double frameTime = newTime - currentTime; if ( frameTime > 0.25 ) frameTime = 0.25; // note: max frame time to avoid spiral of death currentTime = newTime; accumulator += frameTime; while ( accumulator >= dt ) { previousState = currentState; integrate( currentState, t, dt ); t += dt; accumulator -= dt; } const double alpha = accumulator / dt; State state = currentState*alpha + previousState * ( 1.0 - alpha ); render( state ); }

La sinopsis es que la simulación física siempre recibe el mismo incremento de tiempo ( dt ) para la estabilidad numérica. Hacer arreglos para eso debe tener en cuenta que la física y los visuales pueden actualizarse a diferentes frecuencias y que no desea atrasarse demasiado.

Por ejemplo, es posible que desee actualizaciones a una frecuencia de 20 hz, pero una actualización visual con una frecuencia de cuadros de 60 hz. Este bucle hace la interpolación lineal de la física para compensar la diferencia entre las actualizaciones de la física y las actualizaciones gráficas.

Además, cuando la diferencia en el tiempo entre los marcos es mucho mayor que dt hay un bucle para manejar las actualizaciones en trozos de dt . La nota sobre la espiral de la muerte solo se refiere a un caso en el que el cálculo de la física simplemente no puede mantenerse al día con la frecuencia deseada de actualizaciones, por lo que le permite omitir algunas actualizaciones.

Para esta discusión, la parte en la que estoy más interesado es en organizar de manera que la llamada al motor de física (la llamada a integrate ) siempre esté marcada por dt . ¿ Reactive-banana permite al usuario escribir este bucle de estilo? ¿Si es así, cómo? ¿Quizás un ejemplo de simulación de física en tiempo real está en orden (o ya existe)?