Паттерны программирования игр
— Паттерны очередности
169
Один маленький шаг — огромный шаг
Давайте попробуем что-то чуть более сложное. Наша
проблема, по сути, сводится к следующему.
1. Каждое обновление продвигает игру на определен-
ное значение вперед.
2. Требуется определенное количество
настоящего
времени, чтобы обработать обновление.
Если второй пункт занимает больше времени, чем
первый, игра замедляется. Если требуется больше 16 мс,
чтобы продвинуть игровое время на 16 мс, игра просто
не успевает. Но если удается продвинуть игровой про-
цесс более, чем на 16 мс за один шаг, то даже при более
редких обновлениях игра будет успевать.
Идея такова: выбирать, на какое время продвинуть
игровой процесс, надо на основании того, сколько
ре-
ального
времени прошло с момента последнего обновле-
ния. Чем продолжительнее кадр, тем больший шаг дела-
ет игра. В итоге игра никогда не отстает, ведь она может
делать шаги все больше и больше. Это называется
пере-
менный
, или
гибкий
, временной шаг. Выглядит следую-
щим образом:
double lastTime = getCurrentTime();
while (true)
{
double current = getCurrentTime();
double elapsed = current -
lastTime;
processInput();
update(elapsed);
render();
lastTime = current;
}
На каждом кадре мы определяем, сколько прошло
реального
времени с момента последнего обновления
(
elapsed
). Когда мы обновляем состояние игры, мы
170
Игровой цикл (Game Loop) —
Паттерны программирования игр
передаем полученное значение. И затем движок продви-
гает состояние игры на это количество времени.
Допустим, пуля летит по экрану. При фиксирован-
ном временном шаге она будет двигаться в соответ-
ствии со своей скоростью в каждом кадре. С перемен-
ным шагом вы
масштабируете скорость в зависимости
от прошедшего времени.
Если шаги увеличиваются, пуля
начинает двигаться быстрее в каждом кадре. Она проле-
тит через экран за
одинаковое
количество
реального
вре-
мени, независимо от того, двенадцать коротких кадров
или четыре длинных ей понадобится. Похоже, именно
это мы и хотели получить.
• Скорость игры постоянна, независимо от аппарат-
ного обеспечения.
• Пользователи с более мощными компьютерами на-
граждаются более плавным игровым процессом.
Но и здесь скрывается серьезная проблема: мы сде-
лали нашу игру недетерминированной и нестабильной.
Вот как мы сами себя загнали в ловушку.
Допустим, у нас есть сетевая игра для двух пользо-
вателей. У Фреда настоящий игровой монстр, а не ком-
пьютер, а Джордж использует ПК, доставшийся по на-
следству от бабушки. Ранее упомянутая пуля летит
по экранам на обоих компьютерах. На машине Фреда
игра работает супербыстро, временные шаги — крошеч-
ные. Предположим, пуле потребуется 50 кадров в секун-
ду, чтобы пролететь весь экран. А старенький компью-
тер Джорджа может выдать только пять.
Значит, движок игры на машине Фреда обновляет по-
зицию пули 50 раз, а у Джорджа только 5. Большинство
игр используют числа с плавающей точкой, которые
подвержены
ошибкам округления
. Каждый раз, когда
вы складываете два числа с плавающей точкой, резуль-
тат может немного отличаться от ваших ожиданий. Ком-
пьютер Фреда выполняет в 10 раз больше операций,
в результате накопления ошибки он получит большее
«Детерминированный»
означает, что каждый
раз, запуская программу
и давая ей одни и те же
входные данные, вы по-
лучаете один и тот же
результат. В детермини-
рованных программах
намного проще отсле-
живать ошибки — опре-
делите ввод, породив-
ший первую ошибку,
и вы сможете воспроиз-
вести ее снова.
Компьютеры по при-
роде детерминирован-
ные: они выполняют
программы механиче-
ски. Недетерминиро-
ванность появляется то-
гда, когда вмешивается
беспорядочный внеш-
ний мир. Например,
сети, системные часы
и планировщик пото-
ков — все это опирается
на биты из внешнего
мира за пределами кон-
троля программы.
Do'stlaringiz bilan baham: |