haskell - 如何在reactive-banana中实现游戏循环?

标签 haskell reactive-banana

这个问题特定于具有物理和视觉组件(例如游戏)的响应式(Reactive)香蕉和实时模拟。

根据Fix Your Timestep!设置游戏循环的理想方法(假设物理需要可重现),您需要帧之间的固定时间步长。在考虑了一些实际的复杂性之后,作者得出了这个游戏循环:

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

概要是物理模拟总是输入相同的时间增量 (dt) 以保证数值稳定性。进行安排时必须考虑到物理和视觉效果可能会以不同的频率更新,并且您不想落后太远。

例如,您可能希望更新频率为 20hz,但视觉更新的帧速率为 60hz。该循环对物理进行线性插值,以弥补物理更新和图形更新之间的差异。

此外,当帧之间的时间差远大于 dt 时,会有一个循环来处理以 dt 为单位的步进更新。关于死亡螺旋的注释只是指当你的物理计算根本无法跟上所需的更新频率时的情况,因此你允许它跳过一些更新。

对于本次讨论,我最感兴趣的部分是进行安排,以便对物理引擎的调用(对 integrate 的调用)始终按 dt 步进。 reactive-banana 是否允许用户编写这种样式循环?如果是这样怎么办?也许进行实时物理模拟的示例是有序的(或已经存在)?

最佳答案

For this discussion, the part I'm most interested in is arranging so that the call to the physics engine (the call to integrate) is always stepped by dt. Does reactive-banana allow the user to write this style loop?

是的,reactive-banana 可以做到这一点。

这个想法是你编写一个自定义事件循环并将reactive-banana挂接到其中。该库不会对您从何处获取事件做出任何假设,它“仅”解决了根据现有事件整齐地描述新事件的问题。特别是,您可以使用 newAddHandler 函数创建两个在事件循环中适当位置调用的回调函数。本质上,reactive-banana 只是一种令人难以置信的方法来编写维护状态的普通回调函数。何时以及如何调用这些函数取决于您。

这里是一个大纲:

-- 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
}
<小时/>

其实我已经写了一个game loop example这种风格适用于旧版本的reactive-banana,但还没有抽出时间来完善并在 hackage 上发布它。我希望完成的重要事情是:

  • 选择一个易于安装且可在 GHCi 中运行的图形引擎。这个概念使用了 SDL,但这确实很尴尬,因为它不能从 GHCi 中使用。像 OpenGL + GLFW 这样的东西会很好。
  • 提供一个小的抽象,以便更轻松地编写插值阶段。可能只有两件事:代表常规物理更新的事件 eTimer::Event t () 和测量自上次更新以来的时间的行为 bSinceLastTimer::Behaviour t TimeDiff物理更新,可用于进行插值。 (这是一种行为而不是事件,因此内部“画这个!”更新是透明的。)

安德烈亚斯·伯恩斯坦的 blackout clone using reactive-banana可能是以这种风格实现的一个很好的例子。

关于haskell - 如何在reactive-banana中实现游戏循环?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12685430/

相关文章:

haskell - 带有 Reactive-banana 和 SDL 的完整点击/按键事件

haskell - 来自 Real World Haskell 的 MonadState 实例无法编译

haskell - 一个类型变量受另一个类型变量约束的 Rank-2 多态性

haskell - 是否有一个 Monad 收集结果和 `mappend` s 它们?

haskell - optparse-applicative : displaying help for programs invoked with no arguments

haskell - active 香蕉 : Bindings

haskell - react 香蕉中的推与拉

haskell - 来自reactive-banana 中行为的动态输入

haskell - 我使用的是 react 香蕉吗?

haskell - 如何在 Haskell 列表理解中中断|中断|返回