Создание мультиплеерной RTS на Unreal Engine — опыт авторов The Maestros

Организация передачи данных для стабильной игры.

Автор RTS The Maestros Эндрю Эрридж в блоге на сайте Gamasutra рассказал о том, с какими трудностями он столкнулся при создании многопользовательской стратегии на движке Unreal, не приспособленном для такого рода игр. Мы выбрали из материала главное.

Создание мультиплеерной RTS на Unreal Engine — опыт авторов The Maestros

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

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

Создание мультиплеерной RTS на Unreal Engine — опыт авторов The Maestros

Чем дольше клиенту приходится ждать ответа от сервера, тем выше вероятность того, что «предсказание» окажется неверным. Лаги же возникают из-за того, что игра ошибочно предположила, что вы, например, продолжите двигаться вперёд, в то время как вы решили свернуть, а, получив ответ от сервера, отправила вас туда, куда вы на самом деле отправлялись.

Частота обновления сервера шесть раз в секунду
Частота обновления сервера 30 раз в секунду

Сетевая модель Unreal не поддерживает большого количества персонажей, ведь данные о каждом из них отправляются отдельно, а это нагружает соединение. Поэтому в RTS вроде Age of Empires, Starcraft, Warcraft или Total Annihilation используется другой подход.

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

Создание мультиплеерной RTS на Unreal Engine — опыт авторов The Maestros

Кроме того, некоторые домашние файерволлы препятствуют прямому подключению пользователей друг к другу. Для обхода этой проблемы в некоторых современных RTS вроде Age of Empires II HD используется прокси, однако это повышает задержку.

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

Авторы The Maestros посчитали, что частоты обновления, доступной по умолчанию в Unreal Engine, должно им хватить. По словам Эрриджа, разработчики не планировали делать игру, в которой в битве сходились бы сотни или тысячи юнитов, поэтому пропускная способность каждого отдельного игрока не обязательно должна быть очень высокой.

Создание мультиплеерной RTS на Unreal Engine — опыт авторов The Maestros

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

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

Показатели при одновременном движении 200 юнитов
Показатели при одновременном движении 200 юнитов

В Unreal есть два основных способа передачи данных между клиентом и сервером: репликация переменных и отправка RPC. В первом случае, информация отправляется на сервер, как только она меняется. RPC — это функции, которые клиент «просит» сервер изменить. Разработчики The Maestros выбрали второй способ для передачи данных о командах, отдаваемых юнитам.

Первым делом клиент отправляет серверу JSON-текст, в котором содержится ID юнита и его координаты. Команда движения выглядит следующим образом.

{“commandType”:”MOVE”,“unitIds”:
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21],”location”:
{“x”:100.0,”y”:200.0,”z”:300.0}}

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

Впрочем, если в одном матче встречались более двух пользователей, производительность падала. Поэтому авторы The Maestros стали оптимизировать пакеты данных. Например, они приняли решение избавиться от информации о названии команды, «освободив» четыре байта.

Unreal Engine не приспособлен, чтобы хорошо «запаковывать» данные для передачи по сети. Однако движок может использовать данные типа 3float и vector, которые намного «дешевле» сериализовать и десериализовать. Авторы игры создали объект FastEvent, содержащий в себе простые данные и позволивший снизить вес отправляемых пактов до 110 байт. Таким образом, каждую секунду игроки уже отправляли не 8 тысяч байт, а лишь 2 тысячи. И это в том случае, если очень активно кликать мышью.

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

Создание мультиплеерной RTS на Unreal Engine — опыт авторов The Maestros

У каждой команды был свой объект, который имплементировал простой интерфейс с методом «перевода» команды в FastEvent. После перевода разработчики могли вручную распаковать каждую переменную в функцию, чтобы отсеять нулевые. Всё это позднее снова собирается воедино и снова переводится в определённый тип команды.

Создание мультиплеерной RTS на Unreal Engine — опыт авторов The Maestros
2323
13 комментариев

Как то неожиданно статья обрывается :)

9
Ответить

Большая часть пакетов - служебные символы. Что типично для json. Если бы сократили или, ещё лучше, если бы использовать протобаф, то уменьшили бы размер пакетов. Думаю, даже по дефолту без магии раза в 2.

1
Ответить

Паравоз еблановоз что ты сегодня нам привез! protobuf - детишки!

Что бы было быстро, надо делать кастомный бинарный протокол, он насует протобафу по скорости в два раза по скорости. Что бы не писать код, надо взять инструмент кодогенерации твоего языка (либо макросы) и сделать всю рутину им.

3
Ответить

Комментарий недоступен

1
Ответить

Только ли 8? Скорее всего проблема ровесница первого WarCraft, если говорить о Blizzard. В данном же случае скорее всего никто не будет заморачиваться делать мапхак.

И кстати, можно ведь отправлять другим игрокам только то, что они должны видеть? Или это приведет к рассинхрону?

Ответить

Почему бы не отправлять только координаты курсора и нажатия кнопок/клавиш?

Ответить

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

1
Ответить