Snapshot Interpolation

Компонент NetworkTransform использует Snapshot Interpolation.

Если вы не знакомы с этим термином: Snapshot Interpolation интерполирует моментальные снимки, сохраняя при этом их достаточное количество в буфере, чтобы компенсировать плохие вещи в неидеальных сетевых условиях, такие как задержка, потеря пакетов и смена местами пакетов.

При разработке нового компонента NetworkTransform, у нас был выбор из двух решений:

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

  • Сделать его многоразовым: NetworkTransform является одним из многих компонентов, которые нуждаются в Snapshot Interpolation. Кому-то это может понадобиться для Rigidbody 3D / 2D, контроллеров персонажей и многого другого. Однако алгоритм всегда был бы один и тот же.

Чтобы достичь обеих целей, мы решили разделить алгоритм Snapshot Interpolation в отдельный класс, который может быть использован кем угодно. Это необработанный C# скрипт, полностью независимый от Mirror и Unity. Вы можете использовать его в Mirror, на автономных серверах или в разных игровых движках.

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

Этот подход сравним с kcp, который представляет собой просто надежный алгоритм, который может использоваться на всех языках и движках. Для нашего транспорта kcp мы просто помещаем его в классы server и client и отправляем результаты через сокеты UDP.

Моделирование и тесты

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

Чтобы взглянуть на вещи в перспективе: нам потребовалось 4 месяца работы, чтобы заставить работать алгоритм Snapshot Interpolation. Более половины времени было потрачено на тесты и моделирование, чтобы гарантировать стабильность.

Разрабатывая Snapshot Interpolation как самостоятельный алгоритм, позволяет нам моделировать различные сценарии, даже не запуская моделирование задержки или Mirror:

  • Мы можем точно смоделировать, как ведет себя алгоритм интерполяции моментальных снимков, если пульт дистанционного управления находится в t = 5, мы находимся в t = 3 и у нас есть три моментальных снимка в t = 0, 1, 2, при этом выборка выполняется через разные точки интерполяции.

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

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

  • Мы можем смоделировать чрезвычайно плохие сетевые условия, такие как 99%-ная потеря пакетов и смена их местами.

  • Мы можем наверстать упущенное, если буфер станет слишком большим.

  • И много чего ещё..

Как результат

Это всего лишь вопрос вычислений с включенными моментальными снимками и параметрами вместе с полученным результатом.

Использование алгоритма

Пожалуйста, ознакомьтесь с нашим кодом SnapshotInterpolation.cs в Mirror чтобы ознакомиться с алгоритмом и вспомогательными функциями, а затем ознакомьтесь с примером использования NetworkTransform.

Подводя итог, можно выделить несколько ключевых аспектов:

  • Snapshot interface: ваши RigidbodySnapshot, CharacterControllerSnapshot и т.д. структуры должны реализовывать этот интерфейс.

  • Time Buffer, который является буфером SortedList<timestamp, Snapshot>. Мы предлагаем несколько вспомогательных функций, таких как InsertIfNewEnough для удобства.

    • Все эти функции в значительной степени охвачены юнит тестами.

  • Алгоритм Compute(): учитывая буфер моментального снимка, время, deltaTime и параметры конфигурации, он выдает следующий интерполированный моментальный снимок (если таковой имеется).

    • Эта функция поставляется с обширным тестовым покрытием, на самом деле это, вероятно, самая протестированная функция во всем Mirror.

Обязательно ознакомьтесь с артиклом Snapshot Interpolation для понимания как это всё работает, а затем просмотритеSnapshotInterpolation.cs и NetworkTransformBase.csчтобы увидеть это в действии. Даже с предоставленным нами алгоритмом, это все равно непростая тема для понимания и правильной реализации.

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

Обратите внимание, что NetworkTransform отправляет сообщения каждыйsendInterval. Пропускная способность будет значительно сокращена, как только мы внедрим обработку пакетов и дельта-сжатие в Mirror.

Бенчмарки и результаты

Мы рекомендуем использовать LatencySimulationTransport чтобы опробовать это самому, например использовав Benchmark demo. Даже при плохих условиях работы сети, Snapshot Interpolation будет работать хорошо до тех пор, пока bufferMultiplier достаточно высок.

Некоторые другие видео тесты:

  • NetworkTransform old vs. new comparison on Youtube (secret project).

  • Смотрите оригинал Pull Request прогресс видео. Вы можете видеть, как с каждой добавленной функцией постепенно решаются проблемы задержки, потерь и смены пакетов местами.

  • JesusLovsYooh old vs. new NetworkTransform (watch the left build, OG NT is the old one and NT 2k is the new one)

Last updated