Создание сервера для онлайн ММО игр на PHP и Unity ч. 11 — FPS, Ping и как с ним бороться (интерполяция и экстраполяция)

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

В этой серии статей речь пойдет о том что такое PING и какими приемами можно сгладить задержку пересылки пакетов при его низком значении в realtime онлайн играх с примерами кода на C# в игровом движке Unity для игр на ПК, мобильных устройствах и браузерных игр WebGL.

Статья включает в себя видео примеры, демонстрации кода и алгоритм подсчета PING без использований командной строки устройств

Что такое PING

Начнем с теории: отправляя команду в ММО игре на сервер она доходит не моментально - ей требуется время дойти до физического сервера ("железа") по кабелю который проложен в том числе по дну океана и часто этот сервер находится очень далеко (поэтому часто делают несколько серверов в странах близких к игрокам).

Сервер так же может отправлять пакеты игроку на устройство, но т.к. у нас websocket сервер это не происходит сразу после запроса (хотя можно сделать, но будет неправильно) как например в HTTP соединениях (то есть наш сервер подойдет не только к браузерным играм, но и для ПК , мобильных устройств и консолей, однако для демонстрации я использую браузерную на моем сайте).

я изменил картинку из предыдущей статье что бы показать что websocket это не классическая модуль запрос - ответ
я изменил картинку из предыдущей статье что бы показать что websocket это не классическая модуль запрос - ответ

Время которое затрачено на отправку пакета и его возврат обратно - называется Ping , выражается (обычно) в миллисекундах (в одной секунде 1000 мс.) и чем он ниже - тем лучше. Важно отметить что в него не включено время программных вычислений для обработки запроса, как например в расчете RPS сайтов и web приложений

Как высчитать PING

В Unity для версий игр ПК и мобильных устройств есть готовые инструменты анализа PING на устройстве, но в браузерных версиях игр (WebGL) они не работают.

инструменты расчета ping через код в Unity
инструменты расчета ping через код в Unity

Я расскажу о своем приеме расчета PING который сможет заменить встроенный инструмент в Unity и использоваться в браузерных версиях игр:

  1. Отправляем с пакетом временную метку Unix Timestamp с точностью до миллисекунды по времени клиента. На скриншоте ниже пример демонстрационной игры с моего сайта (код ее открыт и доступен в GIT) я добавляю метку к каждому запросу (по времени на устройстве пользователя)

  1. На сервере когда нужно отправить пакет по данной команде обратно я отнимаю от этой метки-времени время которое прошло с момента как сервер получил данную команду (разница в миллисекундах) - строки $event['time'] - $microtime .

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

код написан на PHP, но это не важно - идею можно использовать на любом языке
код написан на PHP, но это не важно - идею можно использовать на любом языке
  1. При получении команд в клиентской части я снова по времени клиента отнимаю полученные значения и это и есть чистый сетевой Ping. Для статистики я сохраняю результаты в массив (список) что бы выводить среднее значение (сумма всех значений деленое на количество этих значений в массиве).

  1. Я периодически шлю попутно с пакетами из клиента в сервер дополнительный параметр ping, тем самым сервер знает средний пинг каждого игрока (этот параметр не влияет ни на какие вычисления на сервере и служит как информативный в связи с тем что его легко подменить в клиентской части через код)

Вот и все. С первого взгляда может показаться что нам нужно знать на какую отправленную команду пришел этот PING, но это не так - мы вычисляем именно время как долго наш пакет шел в сторону сервера (не время работы игровых механик) и обратно по сети пренебрегая размером пакета и выводя среднее значение.

Однако можно заметить что PING в нашем случае зависит от размера пакета (в добавок Websocket протокол на базе TCP в случае потери пакетов начнут их повторно отправлять, будет ждать подтверждение и т.п.), но как по мне это значение более удобно (мне важнее сетевые задержки на реальных игровых пакетах, чем анализ скорости отправки пары байт).

На вопрос каким является хороший показатель Ping в играх Google ответил цифру 50 мс.

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

Я подготовил видео, где поясняю и показываю то о чем написал в данном разделе (функционал уже немного изменился, но концепт остался тот же):

Что такое FPS

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

Возможно Вы знаете из Wikipedia что глаз человека воспринимает смену изображений как плавную при частоте в диапазоне 48 (фильм "Хоббит" был первым снят с этой частой) - 60 FPS (кадров в секунду), поэтому у каждой команды есть своя пауза и она составляет обычно столько , сколько человеческий глаз может уловить и больше, а значит минимальную комфортную паузу между кадрами можно высчитать по формуле: 1 секунда / 60 FPS = 0,017 (округленно) секунды = 17 мс.

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

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

Отсюда и считается что хорошим показателем FPS в играх является 60 FPS

FixedUpdate Unity

В Unity есть циклы выполняющийся раз в фиксированное времяFixedUpdateчастота которого и выражается в виде миллисекунд. В текущем примере стоят частота 50 FPS

Интерполяция

Когда я начинал разработку игры все проверки проводились на сервере и отсылал команды без ограничений из клиента, но обрабатывал лишь те, которое пришли спустя прошествии таймаута (например для движения у меня был 300 мс. который мог быть меньше если у игрока хороший показатель характеристики - скорость). Вообще способы по оптимизации скорости доставки пакетов на сервер более актуальны и заметны на непрерывно выполняющихся анимациях в ответ на команды (движении например) и оптимизация по сути сглаживает время PING между отправкой и получением.

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

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

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

  2. мы знаем среднее значение него PING на текущий момент, а значит мы знаем когда примерно когда пакет достигнет сервера от момента отправки - 1/2 от скорости пинг

  3. при получения ответа от сервера мы знаем что она выполнилась 1/2 пинга назад (т.к. этот пакет шел до нас это время).

Оперируя этими данными мы знаем примерное (т.к. PING величина не постоянная) время когда сервер будет готов принять новую команду и позволить ее заранее отправить с клиента (не дожидаясь предыдущего ответа от сервере).

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

Видео ниже демонстрирует как работает интерполяция:

Экстраполяция

Выше я уже сказал что PING величина не постоянная и может резко меняться (особенно при работе с беспроводным интернетом) и в моменте он может резко увеличится (ухудшиться) и вернутся на нормальное значение. За это время весь игровой мир может замедлится - такое явление называют "фризы" (от англ. freez - замерзнуть). Для подобных ситуаций используют подход , который продолжает поведение объекта как если бы он продолжил бы делать то же самое , но в нашем случае (в игре) - с меньшей скоростью , называется экстраполяция

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

А так же подготовил видео с демонстрацией как это работает в самой игре (это улучшило переход в бесшовном мире)

В настоящий момент проект на моем сайте http://my-fantasy.ru является не коммерческим и я веду его в частном порядке, но верю что продолжая и делясь архитектурными идеями в будущем это станет рабочим продуктом сделанным в России, а статьи помогут в написании игрового сервера для MMO игр.

Буду благодарен за лайк статье.

Если вам интересно чем закончится данный проект - подписывайтесь на мой профиль (я публикую только статьи данного проекта) и следите за новыми видео на Youtube (они выходят чаще чем статьи)

Источник: https://habr.com/ru/articles/725472/


Интересные статьи

Интересные статьи

# Шаблон backend сервера на Golang — часть 5 — оптимизация Worker pool Пятая часть посвящена оптимизации Worker pool и особенностям его работы в составе микросервиса, развернутого в Kubernetes. Пред...
Наша маленькая команда сделала игру AirHockeyVR под Oculus Quest, в ней есть обучение на английском языке, которое записал ютюбер, который сначала записал видео про игру -> мне понравился его голос...
В этой статье я покажу вам, как можно расширять схему AD, создавать нестандартные атрибуты и управлять ими в AD — и всё это с помощью Windows PowerShell. Следуя этому руководству, вы сможете устанав...
Привет, Хабр! Поговорим о возможностях организации доступа к корпоративным системам через мобильные устройства без потери скорости и качества работы?Цифровая трансформация не стоит на месте. Сегодня п...
В 2021 г. ожидается рост числа запросов на чат-боты на 15-20% от ор­га­низа­ций из госсектора, об­ра­зова­ния, медицины, ло­гис­ти­ки, ре­тей­ла и e-commerce, промышленны...