Player.IO. Интерполяция или удивительный мир обмана

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

Когда мы начинаем создавать многопользовательские игры в реальном времени, то возникает интересная штука: дело в том, что нет никакого значения в том что, если все, что происходит на экране — это наиточнейшее реальное отражение вещей, которые происходят в игре. То есть по факту совершенно не обязательно отражать сущность игровых вещей таким образом как это есть на самом деле. Как правило, в динамичных реалтаймовых играх игроки настолько увлечены игровым процессом, что у них нет возможности замечать незначительные погрешности в игровом процессе, при условии, что игра протекает плавно и их ничего не раздражает.

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

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

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

Интерполяция или сглаживание

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

Сейчас нам необходимо исправить проблему, которая выглядит так ужастно:

А в идеале решение должно выглядить таким образом:

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

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

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

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

Экстраполяция или прогноз движения

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

Помните наш клиент-серверную архитектуру? Давайте вернемся к этой теме еще, чтобы увидеть, что одна из очень важных частей этой системы сейчас у нас отсутствует.

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

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

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

Что такое пинг?

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

Для рассчета пинга мы можем одновременно отправить простое сообщение на сервер и записать текущее время игры по вызову Date.getTime(). Когда сервер получит такое сообщение он должен отправить его обратно. Когда вы получаете это сообщение обратно от сервера, вы вызываете Date.getTime() вновь и разница между первым и вторым вызвом метода Date.getTime() и будет нашим пингом.

Пинг следует рассчитывать регулярно, например каждые пять секунд, чтобы иметь наиболее точный пинг.

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

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

Почему не следует предсказывать движение других игроков

Если мы попытаемся предсказать движение такого объекта, который может изменить скорость или направление движение без нашего ведома, то может случиться неприятность. Давайте рассмотрим простой пример: другой игрок движется вперед, а затем назад. Призрак игрока представляет собой фактическое положение игрока на сервере, а нормальное представление игрока показывает положение игрока согласно информации которую мы получаем от сервера.

А так может выглядить наше предсказание:

Красный контур демонстрирует предсказание.

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

Заключение

И так, подведем небольшой итог того, что необходимо использовать для сетевой игры:

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

 

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

Ознакомтесь с обновленным примером сетевой игры. Новый пример практически не отличается от прошлого примера, основные изменения были произведены в методe enterFrameHandler() и в методе messageHandler() в секции обновления состояния. Более того пример подключается к той же ячейки сервера, что и первый пример, таким образом, открыв новый и старый пример в разных окнах браузера, вы можете наглядно увидеть разницу с интерполяцией и без в одной сетевой игре. Серверный код остался без изменений.

 

Это вольный перевод статьи: «Building Flash Multiplayer Games — Interpolation»

Содержание

  1. Как создать онлайн игру? Анонс
  2. Player.IO. Введение
  3. Player.IO. Основы или первая онлайн игра
  4. Player.IO. Пошаговые игры
  5. Player.IO. Сетевые архитектуры
  6. Player.IO. Безопасность в онлайн играх
  7. Player.IO. Пример пошаговой игры
  8. Player.IO. Онлайн игры в реальном времени
  9. Player.IO. Синхронизация игроков
  10. Player.IO. Интерполяция или удивительный мир обмана
  11. Player.IO. Решение проблемы задержек
  12. Player.IO. Советы и рекомендации

 


Индикаторы: Action Script 3, Уроки
Постоянная ссылка

 

 

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

alice.cray
8 Января 2013
— 01:52
#

Сейчас как-раз работаем над многопользовательской игрой на флеше. Открыл пример и заметил вот такую неточность в синхронизации:
http://clip2net.com/clip/m54242/1357641289-clip-11kb.png

Regul
8 Января 2013
— 14:36
#

За статью, кстати, спасибо! Познавательно и наглядно

Regul
8 Января 2013
— 14:39
#

@Regul, в данном примере как минимум две причины по которым происходит такое расхождение:

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

2. При синхронизации через состояния для персонажа игрока используется более плавная (не точная) интерполяция чтобы персонаж игрока не дергался и вел бы себя более отзывчиво не вызывая лишних подозрений у игрока о работе игры :)

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

Ant.Karlov
8 Января 2013
— 18:49
#

@alice.cray, кстати нечто подобное о чем вы написали будет описываться в заключительной статье «Советы и рекомендации». Речь пойдет о том, что те объекты или игроки, которые далеко от нас — их можно вообще игнорировать и не получать и не отправлять им никакую информацию до тех пор пока они не приблизятся на более близкую дистанцию к нам.

Ant.Karlov
8 Января 2013
— 18:52
#

Спасибо, @Ant.Karlov . А ты не мог бы привести пример хорошей, не пошаговой, мутиплеейр игры на флеше?

rndD
9 Января 2013
— 20:00
#

Спс . за урок

GriMMM
9 Января 2013
— 20:08
#

Отличный урок! С нетерпением, как и всегда, жду следующего! Спасибо!

Ян
9 Января 2013
— 23:23
#

@rndD, вот пример того что можно сделать с использованием Player.IO: Everybody Edits. Загляни еще в раздел Multiplayer Games на kongregate.com (правда в топе там пока обосновались игры на Unity). Еще в анонсе данного цикла статетей, я упоминал многопользовательскую физическую игру Transformice. В общем это первое что приходит в голову, не говоря уже массовых многопользовательских флеш играх типа Королевства, Танков Онлайн или Faor.

Ant.Karlov
10 Января 2013
— 03:25
#

Очень жду следующего урока :) Всё просто супер! Спасибо!

Дмитрий
17 Января 2013
— 14:07
#

Большое спасибо за уроки! ждем продолжения)

У меня такой вопрос не по теме:
А как правильней сделать переключение экранов в флеш-игре? Я нашел, что в IDE создаются
спрайт-классы возможных экранов(меню, настройки, правила...) и в них запихиваются все нужные компоненты(кнопки...) а в коде создаются их экземпляры. Так вот это хороший способ или есть лучше?

Дима
26 Января 2013
— 15:27
#