Реализация мультиплеера в игре. Сравнение возможностей Game Center, Steamworks и GameSparks

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

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

  1. Game Center (игры под iOS)
  2. Steamworks (игры для Steam)
  3. GameSparks (кроссплатформенное решение)

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

Коротко обо всем
Перечисленные варианта расположены в порядке возрастания возможностей и гибкости. Сложность реализации при этом тоже возрастает.

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

2. Steamworks
Функционал разделен на две независимые части: матчмейкинг и обмен сообщениями. Первая половина состоит из возможности создавать комнаты, в которых игроки могу обсуждать предстоящий матч.
Вторая половина — это функционал для непосредственного обмена сообщениями. Обе части абсолютно независимы. Можно в любой момент отправить сообщение любому игроку вне зависимости от того, в каких комнатах он находится.

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

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

Немножко о нашей игре
GalaxyAdmirals — это пошаговая космическая стратегия. Геймплей состоит из коротких сражений на гексогональном игровом поле. Игроки ходят по очереди, элемент рандома отсутсвует. По сути, это шахматы в космосе.

ccc341ccc9b34690a8fcb27424eeb80b.jpg

В игре есть кампания из 30 миссий. Многопользовательский режим — это бой один на один со случайным противником.

Ссылка на AppStore
Ссылка на Steam
Игра сделана на Unity3D.

Game Center

Общее описание


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

Интеграция


Возможно, существуют какие-то готовые плагины, но я писал свой на Objective C. Кода не много, и он довольно простой.

Реализация


Существует три варианта реализации мультиплеера через Game Center:
  1. Turn-based.
    Это для пошаговых игр, типа шашек, шахмат. Игроки делают ход по очереди. После каждого хода, игроку прилетает сообщение в Game Center, что теперь его очередь. При этом игра не обязательно должна быть запущена. Состояние игры хранится в Game Center. Игроки не общаются друг с другом, они общаются только с сервером чтобы обновить состояние игры.
  2. Self-hosted.
    Game Center осуществляет только матчмейкинг. Вы должны самостоятельно реализовать мультиплеер через свой сервер.
  3. Real-time.
    Как предыдущий пример, только обмен сообщениями происходит через ретрансляционный сервер Game Center. Это то, что использовал я. О нем поподробнее ниже.

Выглядит это следующим образом. Игрок нажимает кнопку — запускается поиск противника.
338fc88e33c045679d05fb3f1c4680f2.jpg
При запуске матчмейкинга, игрок передает следующие параметры:
  • Максимальное, минимальное количество игроков
  • Группа
  • Атрибут

Группа — это число, в котором закодирована дополнительная информация о матче. Game Center подберет вам противников, которые указали ту же самую группу, что и вы.

Атрибут — это 32-битное число, которое означает роль, которую вы хотите отыграть. По сути, это битовая маска. Game Center подбирает игроков таким образом, чтобы их битовые маски, объединенные по «или», были полностью заполнены единицами.

Например, вы хотите играть в шахматы только за белых. Тогда вы передаете маску 0xFFFF0000. Игрок, желающий играть за черных, должен передать 0×0000FFFF. Если игроку все равно, за кого играть, то он передает заполненную единицами маску 0xFFFFFFFF. Тогда игроку, желающему играть за белых, подберется противник, желающий играть за черных, либо противник, которому все равно, за кого играть.

После того, как противники найдены, запускается матч. Игроку возвращается список противников, которым он может посылать сообщения. Все игроки вступают в игру на равных правах. Им нужно договориться, кто из них игрок№1, а кто игрок№2. Например, можно отсортировать игроков по айдишникам. Тогда у обоих получится один и тот же список.

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

Существует возможность послать приглашение своему другу. Это приглашение высветится у него в Game Center, даже если игра не запущена. По клику на сообщение игра запускается. Для того, чтобы приглашения работали корректно, приходится отдельно обрабатывать два варианта, когда приложение запущено и когда нет.

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

Проблемы


1. Проблема с завершением матча.
Функционал работает таким образом, что ты не можешь послать сообщение «игра окончена, я пошел», а потом сразу дисконнектиться. В этом случае противник не получит твоего сообщения, а увидит только, что ты дисконнектился. Решив, что ты отвалился, он присвоит победу себе.

Поэтому приходится посылать дополнительные подтверждения, типа «я получил твое сообщение, что игра окончена, можешь уходить».

2. Проблема дисконнекта одного из игроков.
Соединение между игроками устанавливается peer-to-peer. Поэтому, если у одного игрока пропадает интернет, то оба получают сообщение, что противник отвалился. По идее, мы должны присудить поражение игроку, который отвалился, и победу игроку, который остался в игре. И самое неприятное, что не существует способа спросить у Game Center, кто остался в игре, а кто дисконнектился.

Чтобы это выяснить приходится вставлять костыли. Например, проверять соединение c Game Center сразу после дисконнекта противника (например, я запрашивал состояние ачивок). Если Game Center не отвечает, то можно считать, что отвалился я, а не противник.

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

Минусы


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

Общее описание


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

Интеграция


Steamworks SDK написан на С++. Поэтому для интеграции в Unity3D необходим врапер. Из нескольких вариантов мною наобум был выбран Steamworks.NET. В принципе, врапер не плохой, все интегрировалось хорошо, но позже я заметил некоторое странное поведение.

Каждая функция возвращает результат через callback примерно такого вида.

protected void OnResult(LobbyEnter_t pCallback, bool failure)

Как видно, в функцию передается дополнительный булевый параметр, который должен сообщать о неудачном выполнении операции. Как оказалось, этот параметр никогда не возвращает true. Даже если сделать что-то заведомо неправильное. Не знаю, косяк ли это Steamworks SDK или Steamworks.NET. От разработчиков какого-то внятного ответа я не получил и пришлось выкручиваться с помощью костылей.

Реализация


Как я уже писал выше, Steamworks имеет более гибкие возможности матчмейкинга. Вот примерный список того, что вы можете сделать:
  • Создать комнату (здесь они называются «лобби»)
  • Присвоить комнате какой-нибудь параметр (например, указать название комнаты, и на какой карте вы будете играть)
  • Отфильтровать список комнат по одному или нескольким параметрам
  • Присвоить параметр игроку в комнате
  • Послать сообщение внутри комнаты

Мы использовали весь этот функционал в нашей стимовской версии.

944c7d63594345ff99791e00ddaaf281.jpg

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

Не все игроки равны между собой. Игрок, создавший комнату, является её владельцем (owner). Если владелец покинул комнату, то это звание перекидывается случайным образом на другого игрока в комнате. Этот игрок может выступать в роли арбитра в спорных ситуациях.

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

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

Как и в Game Center, не существует такого состояния, как «игра окончена». Т.е. игроки должны сами между собой договориться, когда игра считается оконченной.

В стиме существует еще такой интересный функционал. Допустим, вы делаете мультиплеер не через стим, а через свои сервера. Вы можете зарегистрировать свой сервер в стиме. Игрок может получить список всех зарегистрированных серверов. Таким образом стим выступает в роли каталога внешних серверов.

Проблемы


1. Проблема дисконнекта одного из игроков.
К сожалению, как и в Game Center, вы не можете так просто определить, пропало соединение с сервером у вас или у вашего противника. Приходится писать костыли.

Плюсы


  • Более гибкий функционал матчмейкинга.

Минусы


  • Странное поведение Steamworks.NET при ошибках.
  • Проблема с дисконнектом игроков.
GameSparks

Общее описание


GameSparks — это сервис, который выступает в роли backend-сервера для игр. Он позволяет хранить данные, запускать скрипты, имеет встроенные механизмы матчмейкинга, ачивок, лидербордов и много другое.

Интеграция


GameSparks интегрируется в Unity3D с помощью официального плагина. Все работает отлично.

Реализация


Все скрипты пишутся на JavaScript. Поэтому первое что я сделал, это настроил окружение так, чтобы писать на нормальном строго типизированном языке. Я использовал TypeScript.

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

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

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

Для real-time мультиплеера есть возможность создавать отдельные комнаты. В этой комнате может быть запущен скрипт, который постоянно крутится на стороне сервера. Но я не использовал этот функционал. Для передачи сообщений между игроками я использовал обычный скрипт, который просто ретранслировал сообщение нужному игроку.

плюсы


  • Есть возможность хранить данные игрока на сервере
  • Удалось избавиться от костылей, которые использовались в Game Center и Steam. Сервер всегда знает, кто онлайн, кто нет, и всегда можно достоверно определить, кто покинул игру.

минусы


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

89208210ea0b42d38dd3d0d3428c498c.jpg
Слева изображен лог одного игрока, справа — лог другого. Стрелочками нарисованы сообщения, в какой момент сообщение было послано, в какой пришло.

Комментарии (6)

  • 12 декабря 2016 в 15:19

    0

    В Game Center есть voice chat, что бесценно для карточных игр или игр 1-на-1. Подключается парой строк в Swifte.
  • 12 декабря 2016 в 15:24

    +1

    Обещали, что для инди-разработчиков можно запустить игру бесплатно. На деле, пришлось заплатить.

    Можете об этом рассказать поподробнее?
    • 12 декабря 2016 в 15:46 (комментарий был изменён)

      0

      Есть такая программа для инди/студентов.
      https://www.gamesparks.com/indie-student-programme-faq/
      Вы не платите ничего, пока MAU (Monthly Active User) не превышает 100 000.

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

      • 12 декабря 2016 в 15:54

        0

        Понятно, спасибо. То есть всё же всё более-менее честно)
        • 12 декабря 2016 в 15:55

          0

          В принципе, да, все честно.
  • 12 декабря 2016 в 15:40 (комментарий был изменён)

    0

    del

© Habrahabr.ru