Как синхронизировать состояние многопользовательской игры более эффективно, чем обновления с полным состоянием?

10

Раньше я немного программировал в игровой сети, но в основном с TCP для игр без необходимости в реальном времени. Я работаю над 2D Java-игрой с сетевым мультиплеером. Для обучения я хочу сделать это сам, без существующего сетевого API.

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

Есть ли надежный способ, который вы использовали раньше, чтобы передавать только изменения состояния, и есть ли достаточно большой дисбаланс в производительности, который стоит дополнительной работы?

Стремись
источник
2
Испытайте полное состояние каждого фрейма и, если он слишком медленный (для довольно простой двухмерной игры, вероятно, достаточно эффективен), попробуйте оптимизировать. Если он работает нормально, то он работает нормально, и вам не нужно менять его, если только вы не заметите, что ваша сеть становится узким местом позже.
Роберт Роухани

Ответы:

10

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

У меня лично был намного больший успех со следующей моделью:

  • Состояние игры хранится в четко определенной объектной модели в структуре пространственных данных (например, октрое)
  • Все изменения состояния игры (будь то на клиенте или на сервере) описываются как события. Событием может быть изменение свойства игрового объекта, изменение тайла карты, движение игрового объекта и т. Д.
  • Игровой движок на сервере генерирует поток событий по ходу игры. Они напрямую применяются к игровому состоянию сервера.
  • События также отправляются игрокам, но только если событие относится к этому игроку (например, видимо ли событие с текущей позиции?)
  • Изменения в видимости игрока могут также привести к событиям, которые «раскрывают» новые части карты и т. Д., Когда игрок движется. Это также может быть использовано, чтобы гарантировать, что игрок получает точное начальное представление о соответствующем состоянии игры, когда он впервые присоединяется к игре.
  • Состояние игры для игрока обновляется с учетом любых событий, которые он получает. Как таковая, она имеет только частичную модель игрового состояния, но должна синхронизироваться с сервером, если все события правильно обработаны.

Это обеспечило мне хорошую производительность даже с довольно большими игровыми мирами.

Еще один совет: пусть клиент позаботится об анимации, эффектах частиц и т. Д. Без ссылки на сервер. Нет смысла передавать их - они просто должны быть вызваны соответствующими игровыми событиями.

mikera
источник
6

Синхронизация обычно делится на две части: инкрементная и абсолютная.

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

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

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

Упаковка данных также важна. Вы можете передать приблизительно 1400 байт (в зависимости от конфигурации, это значение по умолчанию) в одном пакете UDP, обычно есть несколько байтов заголовка. Таким образом, вы можете легко обновить 50-100 позиций в одной упаковке.

Matzi
источник
Спасибо за совет Маци. Я все еще работаю над реализацией сервера и клиента, но я вернусь через пару дней и, возможно, приму ваш ответ.
Хаз
Удачи тебе! ;)
Маци
1

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

В этом посте #AltDevBlogADay рассматриваются некоторые аспекты этого подхода в современной RTS (в частности, как определить, когда ваши клиенты начинают запускать «разные» игры).

Не забудьте сохранить это простым, пока не доказано обратное. :)

PT
источник
1
Это хорошие материалы от разработчика Factorio, который использует этот подход, и намекают на сложность этого подхода, но также показывают, что он жизнеспособен: factorio.com/blog/post/fff-76 factorio.com/blog/post/fff -147 factorio.com/blog/post/fff-188
AaronLS