如何在反应香蕉中实现游戏循环? [英] How to implement a game loop in reactive-banana?

查看:108
本文介绍了如何在反应香蕉中实现游戏循环?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题是针对反应性香蕉和实体模拟以及物理和视觉元素(例如游戏)而设计的。

根据修复你的Timestep!设置游戏循环的理想方式(假设物理需要可重现) ),你需要在帧之间有一个固定的时间步长。在考虑了一些真正的复杂情况之后,笔者到达了这个游戏循环:

  double t = 0.0; 
const double dt = 0.01;

double currentTime = hires_time_in_seconds();
double accumulator = 0.0;

以前的州;
国家电流; (!!)


{
double newTime = time();
double frameTime = newTime - currentTime;
if(frameTime> 0.25)
frameTime = 0.25; //注意:避免死亡螺旋的最大帧时间
currentTime = newTime;

累加器+ =帧时间;

while(accumulator> = dt)
{
previousState = currentState;
integrate(currentState,t,dt);
t + = dt;
累加器 - = dt;
}

const double alpha = accumulator / dt;

State state = currentState * alpha + previousState *(1.0 - alpha);

渲染(状态);

$ / code>

概要是物理模拟总是以相同的时间增量 dt )以获得数值稳定性。安排这一点必须考虑到物理和视觉效果可能会以不同的频率进行更新,并且您不希望太落后。

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



此外,当帧之间的时间差远大于 dt 有一个循环来处理 dt 块中的更新。关于死亡螺旋的说明仅仅是指你的物理计算根本无法跟上更新频率的情况,所以你可以让它跳过一些更新。



对于这个讨论,我最感兴趣的部分是安排,以便物理引擎的调用(调用集成)始终由 DT reactive-banana 是否允许用户编写此样式循环?如果是这样如何?或许一个做实时物理模拟的例子是按顺序(或已经存在)?

解决方案


对于这个讨论,我最感兴趣的部分是安排,以便物理引擎(调用集成)的调用总是由dt来进行。反应香蕉是否允许用户编写这个样式循环?

是的,反应式香蕉可以做到这一点。

b
$ b

这个想法是,你写一个自定义事件循环,并将反应 - 香蕉挂钩。图书馆没有对你从哪里获得你的活动做出任何假设,它只解决了以现有的方式整齐地描述新事件的问题。特别是,您可以使用 newAddHandler 函数来创建两个回调函数,这些回调函数在事件循环的适当位置被调用。从本质上讲,反应式香蕉只是一种令人难以置信的方法来编写维持状态的普通回调函数。何时以及如何调用这些功能取决于您。



这里概述一下:

   - 设置回调函数
(renderEvent,render)< - newAddHandler
(stateUpdateEvent,stateUpdate)< - newAddHandler

- make the回调函数做一些有趣的事情
let networkDescription = do
eRender< - fromAddHandler renderEvent
eStateUpdate< - fromAddHandler stateUpdateEvent
...
- 这里的功能

actuate =<<编译networkDescription

- 事件循环
while(!quit)
{
...
while(accumulator> = dt)
{
stateUpdate(t,dt) - 调用一个回调函数
t + = dt
accumulator - = dt
}
...
render ) - 调用另一个回调
}






实际上,我已经为这种风格写了一个游戏循环示例,用于反应型香蕉的旧版本,但尚未获得围绕着抛光和发布它在黑客上。我希望看到的重要内容包括:


  • 选择一个易于安装并在GHCi中工作的图形引擎。这个概念使用SDL,但是这真的很尴尬,因为它不能从GHCi中使用。像OpenGL + GLFW就好。

  • 提供一个小抽象,以便更容易地编写插值阶段。可能只是两件事:事件 eTimer :: Event t(),它表示常规物理更新和行为 bSinceLastTimer :: Behavior t TimeDiff ,它用于度量自上次物理更新以来的时间,可用于进行插值。 (这是一种行为,而不是事件,所以内部绘制这个!更新是透明的。)
  • =https://github.com/bernstein/breakout>使用反应式香蕉的停电克隆可能是以这种风格实现的极佳示例。


    This question is specific to reactive-banana and real-time simulations with a physical and visual component (eg., games).

    According to Fix Your Timestep! the ideal way to setup a game loop (assuming physics that needs to be reproducible), you need a fixed timestep between frames. After considering a number of real complications , the author arrives at this game loop:

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

    The synopsis is that the physics simulation is always fed the same increment of time (dt) for numerical stability. Arranging for that must consider that physics and visuals may update at different frequencies and you don't want to get too far behind.

    For example, you may want updates at a frequency of 20hz, but a visual update with a framerate of 60hz. This loop does linear interpolation of the physics to make up the difference between physics updates and graphical updates.

    Additionally, when the difference in time between frames is much larger than dt there is a loop to handle stepping the updates in chunks of dt. The note about the spiral of death just refers to a case when your physics calculation simply can't keep up with the desired frequency of updates, so you allow it to skip some updates.

    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? If so how? Perhaps an example doing real-time physics simulation is in order (or already exists)?

    解决方案

    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?

    Yes, reactive-banana can do that.

    The idea is that you write a custom event loop and hook reactive-banana into that. The library doesn't make any assumptions as to where you get your events from, it "only" solves the problem of neatly describing new events in terms of existing ones. In particular, you can use the newAddHandler function to create two callback functions that are called in the appropriate places in the event loop. Essentially, reactive-banana is just a mind-boggling method to write ordinary callback functions that maintain state. When and how you call these functions is up to you.

    Here a general outline:

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


    In fact, I have written a game loop example in this style for an older version of reactive-banana, but haven't gotten around to polishing and publishing it on hackage. The important things that I would like to see completed are:

    • Pick a graphics engine that is easy to install and works in GHCi. The concept uses SDL, but this is really quite awkward as it cannot be used from GHCi. Something like OpenGL + GLFW would be nice.
    • Offer a small abstraction to make it easier to write the interpolation phase. Probably just two things: an event eTimer :: Event t () that represents the regular physics updates and a behavior bSinceLastTimer :: Behavior t TimeDiff that measures the time since the last physics updates, which can be used for doing the interpolation. (It's a behavior instead of an event, so the internal "draw this!" updates are transparent.)

    Andreas Bernstein's blackout clone using reactive-banana may be an excellent example to implement in this style.

    这篇关于如何在反应香蕉中实现游戏循环?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆