April 23rd, 2007

nyaload

dt

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

Если взять за фиксированный логический FPS скажем 50, то при графическом FPS в 43 или 67 кадров при равномерном движении будут противные рывки в плавном движении (см. демку ниже). Можно бороть при помощи интерполяции промежуточных состояний, но это требует очень жёсткого стиля программирования, ВСЁ мы так интерполировать не сможем (во всяком случае, мне кажется это нерациональным, так одинаково усложнить всё, даже там где не надо).

Если взять за FPS 500 - то возникает вопрос, что делать, если при сложной игровой ситуации или при низкой конфигурации компьютера или работающем в фоне Excel (я казуальщиг!) игра не будет успевать столько заапдейтить (на самом деле, такой же вопрос возникает и при фиксированном FPS равном 50).

Поэтому мне кажется, что лучше всего комбинированный подход из всех трёх - фиксированный очень высокий FPS без интерполяции, низкий FPS с интерполяцией, не фиксированный dt там где не нужен фиксированный (например, пролёт GUI-кнопки по кривой [sin(t), cos(t)] - фиксированный FPS здесь нафиг не нужен).

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

float speed, height;

{
  float gravity = -10;
  speed = speed + gravity*dt;
  height = height + speed * dt;

  bool fallen = height < 0;
}


то при увеличении dt в два раза мы получим другой характер движения. Похожий, но не такой же - парабола будет более изломаной. Из-за этого могут сдвинуться некоторые тайминги событий-реакций на падение шарика. Если есть разница на 10% и она может повлиять на геймплей - обязательно фиксированный FPS.

Так же фиксированный FPS нужен, когда есть события, промежуток между которыми сравним со временем одного кадра. Скажем, выпускание частиц из эмиттера. Если выпускается 100 частиц в секунду с промежутком 0.01 между выпуском, то при FPS = 37 в какие-то кадры будет выпускаться 2 частицы, в какие-то 3. При FPS = 207 расстояние между частицами будет будет дрожать - ..... ..... ..... ..... ......

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

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

Комменты от более опытных в dt/FPS делах людей - welcome :) Завидую тем, у кого стабильный рисовательный FPS=60 и не больше, и не меньше, и можно всё остальное привязать к нему.

updated:
Пришли всезнайки, и сказали, что 24 фиксированного FPS - это достаточно. В кино этого достаточно, потому что положение пуль считается правильно, а если бы логический фреймрейт пуль был бы 77 FPS, а отображение на экране - 24 (или наоборот), тогда бы было бы совсем недостаточно. Кто-нибудь смотрел NTSC видео на PAL? Там 50 и 60 Hz. Я не смотрел, интересно "вставить каждый пятый как шестой" или "дропнуть каждый шестой" даёт заметные рывки или нет.

Демка из PopCap framework - верхний квадратик движется с фиксированным апдейтом 100Hz, нижний - с плавающим dt. http://dobrokot.nm.ru/tempora/popcap_fixed_and_float_dt_demo.rar

Вот кусок кода из Popcap который их рисует - http://www.everfall.com/paste/id.php?ui1aw3nphqzd