Подтвердить что ты не робот

Как реализовать игровой цикл в реактивном банане?

Этот вопрос специфичен для реактивного банана и моделирования в реальном времени с физическим и визуальным компонентом (например, играми).

В соответствии с 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) для численной устойчивости. Для этого необходимо учитывать, что физика и визуальные эффекты могут обновляться на разных частотах, и вы не хотите слишком сильно отставать.

Например, вам могут потребоваться обновления с частотой 20 Гц, но визуальное обновление с частотой кадров 60 Гц. Этот цикл делает линейную интерполяцию физики, чтобы составить разницу между физическими обновлениями и графическими обновлениями.

Кроме того, когда разница во времени между кадрами намного больше, чем dt, существует цикл для обработки шагов по обновлению в кусках dt. Примечание о спирали смерти просто относится к случаю, когда ваш физический расчет просто не может идти в ногу с нужной частотой обновлений, поэтому вы позволяете ему пропускать некоторые обновления.

Для этого обсуждения часть меня больше всего интересует, так что вызов физическому движку (вызов integrate) всегда вызывается dt. Поддерживает ли реактивно-банановый пользовательский стиль этого цикла? Если да, то как? Возможно, пример, выполняющий физическую симуляцию в реальном времени, находится в порядке (или уже существует)?

4b9b3361

Ответ 1

Для этого обсуждения часть меня больше всего интересует, так что вызов физическому движку (вызов для интеграции) всегда выполняется с помощью dt. Может ли реактивно-банановый пользователь писать этот цикл стиля?

Да, реактивный банан может это сделать.

Идея заключается в том, что вы пишете настраиваемый цикл событий и включаете в него реактивный банан. Библиотека не делает никаких предположений относительно того, откуда вы получаете свои события, она "только" решает проблему аккуратного описания новых событий с точки зрения существующих. В частности, вы можете использовать функцию newAddHandler для создания двух функций обратного вызова, вызываемых в соответствующих местах цикла событий. По сути, реактивный банан - просто умопомрачительный метод для написания обычных функций обратного вызова, которые поддерживают состояние. Когда и как вы называете эти функции, зависит от вас.

Здесь общий контур:

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

Фактически, я написал пример игрового цикла в этом стиле для более старой версии реактивного банана, но не стал полировать и публиковать его о взломе. Важными вещами, которые я хотел бы увидеть, являются:

  • Выберите графический движок, который легко установить и работает в GHCi. Концепция использует SDL, но это действительно довольно неудобно, поскольку она не может использоваться из GHCi. Что-то вроде OpenGL + GLFW было бы хорошо.
  • Предложите небольшую абстракцию, чтобы упростить запись фазы интерполяции. Вероятно, всего две вещи: событие eTimer :: Event t (), которое представляет регулярные обновления физики и поведение bSinceLastTimer :: Behavior t TimeDiff, которое измеряет время с момента последних обновлений физики, которое можно использовать для выполнения интерполяции. (Это поведение вместо события, поэтому внутренние "нарисовать это!" Обновления прозрачны.)

Андреас Бернстайн blackout clone, использующий реактивный банан, может стать отличным примером для реализации в этом стиле.