Как вы отделите игровую логику от дисплея?

Как сделать так, чтобы количество отображаемых кадров в секунду было независимым от игровой логики? Таким образом, игровая логика работает с одинаковой скоростью, независимо от того, насколько быстро может воспроизводиться видеокарта.

20.08.2008 04:30:24
8 ОТВЕТОВ

Вы можете сделать свой игровой цикл похожим на:

int lastTime = GetCurrentTime();
while(1) {
    // how long is it since we last updated?
    int currentTime = GetCurrentTime();
    int dt = currentTime - lastTime;
    lastTime = currentTime;

    // now do the game logic
    Update(dt);

    // and you can render
    Draw();
}

Тогда вам просто нужно написать свою Update()функцию, чтобы учесть разницу во времени; Например, если у вас есть объект, движущийся с некоторой скоростью v, обновите его положение v * dtкаждым кадром.

4
20.08.2008 04:37:39

Я думаю, что вопрос показывает небольшое недопонимание того, как игровые движки должны быть разработаны. Что совершенно нормально, потому что это чертовски сложные вещи, которые трудно понять правильно;)

У вас правильное впечатление, что вы хотите то, что называется независимостью от частоты кадров. Но это касается не только рендеринга кадров.

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

То, что вы хотите сделать, это уметь обрабатывать игровую логику на любом FPS (Frames Per Second) и иметь детерминированный результат.

Это становится проблемой в следующем случае:

Проверьте ввод: - Ввод ключа: «W», что означает, что мы перемещаем персонажа игрока вперед на 10 единиц:

playerPosition + = 10;

Теперь, поскольку вы делаете это каждый кадр, если вы работаете со скоростью 30 кадров в секунду, вы будете перемещать 300 единиц в секунду.

Но если вместо этого вы работаете со скоростью 10 FPS, вы будете перемещать только 100 единиц в секунду. И поэтому ваша игровая логика не зависит от частоты кадров.

К счастью, решить эту проблему и сделать игровую логику независимой от частоты кадров - довольно простая задача.

Во-первых, вам нужен таймер, который будет отсчитывать время рендеринга каждого кадра. Это число в секундах (т. Е. 0,001 секунды для завершения тика) затем умножается на то, что вы хотите быть независимым от частоты кадров. Итак, в этом случае:

Когда держишь "W"

playerPosition + = 10 * frameTimeDelta;

(Дельта - причудливое слово для «Измени что-то»)

Таким образом, ваш игрок будет перемещать некоторую долю от 10 за один тик, и через полные секунды тиков вы будете перемещать полные 10 юнитов.

Тем не менее, это будет падать, когда речь идет о свойствах, где скорость изменения также меняется со временем, например, ускорение транспортного средства. Это может быть решено с помощью более продвинутого интегратора, такого как «Verlet».

Многопоточный подход

Если вы все еще заинтересованы в ответе на ваш вопрос (так как я не ответил на него, но представил альтернативу), вот он. Разделение игровой логики и рендеринга на разные темы. У него есть свои недостатки, хотя. Достаточно, чтобы подавляющее большинство игровых движков оставалось однопоточным.

Это не значит, что в так называемых однопоточных движках работает только один поток. Но все значимые задачи обычно находятся в одном центральном потоке. Некоторые вещи, такие как Collision Detection, могут быть многопоточными, но обычно фаза Collision блока Tick блокируется до тех пор, пока все потоки не вернутся, и механизм не вернется к одному потоку выполнения.

Многопоточность представляет целый, очень большой класс проблем, даже некоторые из которых связаны с производительностью, поскольку все, даже контейнеры, должны быть поточно-ориентированными. И игровые движки - это очень сложные программы для начала, поэтому они редко стоят того, чтобы усложнить их многопоточность.

Фиксированный временной шаг

Наконец, как заметил другой комментатор, наличие временного шага фиксированного размера и контроль того, как часто вы «шагаете» по игровой логике, также может быть очень эффективным способом решения этой проблемы со многими преимуществами.

Ссылка здесь для полноты, но другой комментатор также ссылается на это: Fix Your Time Step

30
31.05.2012 19:29:19
Не могли бы вы рассказать, как добиться синхронизации времени на большем количестве устройств (многопользовательские игры)? Поскольку для каждого устройства однопоточный подход занимал бы различное количество времени ... если у вас есть какие-то материалы, которые я мог бы прочитать, я был бы очень признателен
arenaq 21.05.2015 11:55:51
Это гораздо более сложная тема, на которую я не смог бы ответить. Я предлагаю погуглить по этому поводу, там много хороших ресурсов.
Adam 21.05.2015 18:23:21

Из моего опыта (не очень) ответы Джесси и Адама должны поставить вас на правильный путь.

Если вам нужна дополнительная информация и понимание того, как это работает, я обнаружил, что примеры приложений для TrueVision 3D были очень полезны.

-2
20.08.2008 04:45:36

Koen Witters имеет очень подробную статью о различных настройках игрового цикла.

Он покрывает:

  • FPS зависит от постоянной скорости игры
  • Скорость игры зависит от переменной FPS
  • Постоянная скорость игры с максимальным FPS
  • Постоянная скорость игры не зависит от переменной FPS

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

8
26.10.2019 10:12:43

В тот день была отличная статья о флипкоде. Я хотел бы выкопать это и представить это для вас.

http://www.flipcode.com/archives/Main_Loop_with_Fixed_Time_Steps.shtml

Это хорошо продуманный цикл запуска игры:

  1. Однопоточный
  2. При фиксированных игровых часах
  3. С графикой как можно быстрее, используя интерполированные часы

Ну, по крайней мере, я так думаю. :-) Жаль, что обсуждение, которое продолжалось после этой публикации, труднее найти. Возможно, машина обратного пути может помочь там.

time0 = getTickCount();
do
{
  time1 = getTickCount();
  frameTime = 0;
  int numLoops = 0;

  while ((time1 - time0)  TICK_TIME && numLoops < MAX_LOOPS)
  {
    GameTickRun();
    time0 += TICK_TIME;
    frameTime += TICK_TIME;
    numLoops++;
// Could this be a good idea? We're not doing it, anyway.
//    time1 = getTickCount();
  }
  IndependentTickRun(frameTime);

  // If playing solo and game logic takes way too long, discard pending
time.
  if (!bNetworkGame && (time1 - time0)  TICK_TIME)
    time0 = time1 - TICK_TIME;

  if (canRender)
  {
    // Account for numLoops overflow causing percent  1.
    float percentWithinTick = Min(1.f, float(time1 - time0)/TICK_TIME);
    GameDrawWithInterpolation(percentWithinTick);
  }
}
while (!bGameDone);
2
20.08.2008 05:06:03

Enginuity имеет немного другой, но интересный подход: пул задач.

0
21.08.2008 13:33:51

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

Но вы должны правильно синхронизировать потоки;) Реализация займет много времени, поэтому, если ваша игра не слишком большая, однопоточное решение подойдет.

Кроме того, извлечение графического интерфейса в отдельный поток кажется отличным подходом. Вы когда-нибудь видели всплывающее сообщение «Миссия выполнена», когда юниты перемещаются в играх RTS? Как раз об этом я и говорю :)

0
15.09.2008 14:26:48

Это не распространяется на более высокий уровень абстракции программы, то есть конечные автоматы и т. Д.

Это хорошо, чтобы контролировать движение и ускорение, регулируя их с интервалом кадра. Но как насчет таких вещей, как запуск звука через 2,55 секунды после того или иного или изменение уровня игры через 18,25 секунды и т. Д.

Это может быть привязано к накопителю истекшего времени (счетчику), НО эти временные интервалы могут быть испорчены, если ваша частота кадров падает ниже разрешения сценария состояния, т. Е. Если вашей более высокой логике требуется гранулярность 0,05 с, и вы падаете ниже 20 кадров в секунду.

Детерминизм можно сохранить, если игровая логика запускается в отдельном «потоке» (на уровне программного обеспечения, который я бы предпочел для этого или на уровне ОС) с фиксированным интервалом времени, независимым от fps.

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

0
29.01.2010 17:35:49