Так называемые .io-игры — это браузерные massively multiplayer action-игры, в которых множество людей борются с излишками свободного времени. Massively multiplayer — это значит, что игра представляет собой многопользовательскую массовку из большого количества (сотен+) игроков, играющих в общей локации. Существует мнение, что все началось с игры Agar.io (клетки в чашке Петри). Другими успешными примерами можно назвать Slither.io (змейки) и Diep.io (танчики). Если верить статистике, то каждый день в эти игры играют миллионы игроков. Сегодня существуют десятки различных .io-игр, большинство из которых можно найти, загуглив по запросу «io games list».


Я расскажу о том, как мы делали нашу .io-игру Oceanar.io — игру про рыбок и прочих морских жителей, постараюсь при этом сосредоточиться на вопросах общего технического устройства всей системы, и дам несколько скромных советов.


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


Игровой процесс


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


  1. Управляемый персонаж должен набираться сил постепенно в течение длительного времени, и этот набор сил должен отражаться визуально.
  2. Нужно чтобы был смысл играть очень долго — много часов подряд. Игрок, проигравший 5 часов, должен иметь определенное преимущество перед тем, кто проиграл 3.
  3. Игровое преимущество не должно зависеть исключительно от времени, проведенного в игре. Должен быть разумный баланс между отыгранным временем и игровым мастерством. Человек, отыгравший несколько минут, должен иметь возможность победить того, кто играет уже несколько часов. Пусть это будет очень сложно, пусть здесь понадобится фактор удачи, но такая возможность должна быть.
  4. Бонусы, полученные в результате победы на другим игроком, не должны безоговорочно доставаться победителю — у других игроков должна быть возможность отхватить их часть.
  5. Иметь хотя бы что-то оригинальное :)

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


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


У меня нет опыта в создании серьезных MMO-игр, но, понабивав шишки на своих прошлых проектах, а также почитав об опыте различных разработчиков (например) у меня сложилось мнение, что клиент-серверная архитектура .io-игр принципиально не сильно отличается от остальных MMO-игр — в ее основе лежат примерно те же идеи. В качестве одного из важных (и приятных для разработчиков-одиночек) отличий .io-игр я бы выделил существенно упрощенные игровые правила: если в серьезной MMORPG для вычисления урона от удара топором по голове гоблина на сервере нужно отыграть целую вериницу правил, то в .io-игре это будет простая формула в одну строчку кода. То есть, .io-игра — это та же MMO с большим количеством игроков, но гораздо более простая с точки зрения организации своих внутренностей. Достаточно простая, чтобы ее мог написать один программист, и клиентскую, и серверную часть.


Клиент


Игровой клиент — это браузер с открытой в нем html-страницей. Технологии те же, что и у большинства других .io-игр, а именно: язык программирования — JavaScript, графика — WebGL, взаимодействие с игровым сервером — через WebSockets. Так уж сложилось, что .io-игры не принято озвучивать (хотя все технические возможности для этого имеются), поэтому мы решили, что и наша игра будет без звука (чтобы не привлекать лишнего внимания у начальства на работе).


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


  1. Умеет отправлять на сервер действия пользователя.
  2. Умеет принимать с сервера сообщения и диспетчеризировать их.
  3. Хранит у себя копию игрового мира, попадающую в его поле зрения.
  4. Визуализирует хранимую копию игрового мира.

Вот примеры сообщений, и действий по их обработке со стороны клиента:


Показать рыбу с идентификатором creature_id, которая находится в позиции position, имеет вектор скорости velocity, принадлежит игроку с идентификатором owner_id, и является рыбой типа creature_type_id.


С точки зрения JavaScript это будет например вот такой объект:


{
    message_type_id: 1,
    creature_id: 68328,
    creature_type_id: 2,
    owner_id: 9306,
    position: { x: 1.436, y: -39.32 },
    velocity: { x: -0.17235, y: -0.1157 }
}

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


Рыба с идентификатором id1 укусила рыбу с идентификатором id2.


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


Таким образом можно сказать, что сообщения от сервера клиенту — это в основном уведомления о том, что в поле зрения клиента что-то произошло. Что-то такое, о чем клиент должен знать, чтобы правильным и понятным образом (графически) уведомить об этом игрока. Примеры других типов сообщений — «игра окончена с таким-то счетом», «в игру вошел игрок с таким-то именем», «список топ-10 игроков», «список всех игроков и их координат» (для отображения их на карте), и так далее.


Сервер


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


Наш сервер написан на C++, из сторонних библиотек используется Boost, сетевая часть — Boost.Asio. Разработка и тестирование — Visual Studio / Windows, рабочий сервер — GCC / Linux.


Игровая комната


Здесь происходит вся игровая логика. Комната — это объект, который, среди прочего:


  1. Хранит контейнеры игровых объектов: существ, еды, наблюдателей.
  2. Умеет принимать от клиентов сообщения и диспетчеризировать их.
  3. Умеет уведомлять клиентов о событиях, наступивших в комнате. Некоторые из уведомлений отправляются только если данные события наступили в поле зрения клиента.
  4. Реализует функцию перехода комнаты из состояния An в состояние An+1 (апдейт игрового мира).
  5. Периодически выполняет апдейт игрового мира с течением времени (в нашем случае — 10 раз в секунду).

Обезжиренный интерфейс игровой комнаты на псевдо-коде:


class room
{
methods:
    join(user);
    update(dt);
    dispatch(protocol::kill_creature);
    dispatch(protocol::consume_food);
    dispatch ...
    dispatch ...
    dispatch ...

properties:
    container<creature> creatures;
    container<food> foods;
    container<observer> observers;
}

Апдейт игрового мира


Это функция, которая реализует изменения в игровом мире, произошедшие за фиксированный отрезок времени dt (в нашем случае — 0,1 секунды), я буду называть эту функцию update. В общих чертах update игровой комнаты — это вызов update для игровых объектов, то есть вызов update далее вниз по лестнице агрегации, от общего к частному. Простой пример: пусть у нас есть рыба, находящаяся в координатах position, и двигающаяся с вектором скорости velocity. Тогда ее функия update — это вычисление ее позиции через dt времени:


next_position = position + dt * velocity;

Помимо перемещения, рыба может за это dt принять решение отправиться в какое-то другое место, и тогда, помимо обновления своих координат, в результате update может измениться ее вектор скорости velocity. На псевдо-коде update комнаты выглядит следующим образом:


room::update(dt)
{
    message_queue.dispatch(); // Диспетчеризовать сообщения, адресованные комнате, находящиеся в очереди

    foreach(creature in creatures)
    {
        creature.update(dt);
    }

    foreach(food in foods)
    {
        food.update(dt);
    }

    foreach(observer in observers)
    {
        observer.update(dt);
    }

    spawn_food();
    spawn_npc();
}

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


Разбиение игрового пространства


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


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


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


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


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


Протокол


Отдельный вопрос — протокол взаимодействия сервера и клиентов. Под протоколом я подразумеваю описание набора и структуры сообщений, которыми будут обмениваться сервер и клиенты, их сериализацию при отправке, и десериализацию и диспетчеризацию при получении. Я использовал свое собственное упрощенное велосипедное решение — кодогенератор из JSON-описателя пакетов (так уж получилось). Если вам нужно готовое серьезное решение, а не велосипед, то есть ru.wikipedia.org/wiki/Protocol_Buffers, который тоже является кодогенератором.


Почему кодогенерация?


  1. Позволяет минимизировать код, который нужно писать руками.
  2. Скорость работы — в кодогенераторе вы не ограничены никакими правилами, можно сгенерировать максимально быстрый код, в рамках используемого языка.
  3. Объем передаваемых данных — можно (и нужно) плотно упаковывать данные в соответствии с вашими потребностями, минимизируя трафик (об этом отдельно ниже). С кодогенератором особенно удобно ввести дополнительные атрибуты к полям сообщений, задающие правила упаковки этих полей.

Очереди сообщений


Для отправки сообщений комнате используется multiple producer single consumer очередь. Для отправки сообщений клиентам — single producer single consumer. Оба типа очередей — самописно-велосипедные. Вероятно, можно было воспользоваться Boost.Lockfree, но, опять же, так уж получилось. Для (де-)сериализации и диспетчеризации сообщений, путешествующих внутри сервера, используется тот же кодогенерированный механизм, что и для передачи сообщений по сети.


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


Советы


Если вы собрались сделать свою .io-игру, то я могу сказать лишь одно — берите и делайте! :) И вот несколько скромных советов:


Боты


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


Сетевой трафик


Так вот, минимизация сетевого трафика. Обычно у хостинг-провайдеров трафик либо платный по счетчику, либо предоплаченный пакет с возможностью докупки, либо безлимитный, но со звездочкой-сноской, по которой находится оговорка, что на самом деле не такой уж он и безлимитный — при исчерпании определенного объема вас будут ждать карательные меры в виде, например, урезания ширины канала, или же придется таки платить сверху. Поэтому — тщательно продумайте ваш протокол и экономьте каждый байт! У нас 10000 живых игроков, отыгравших за сутки, генерировали за эти сутки порядка 200 гигабайт игрового трафика. Наверно это может показаться не так уж много, но не забывайте, что объем потребляемого трафика будет расти вместе с вашей аудиторией. Несколько сэкономленных байт в отдельно взятом пакете могут сэкономить сотни мегабайт или даже гигабайты трафика в сутки.


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


HTTPS


Если вы захотите сделать вашу игру в том числе iframe-игрой ВКонтакте, то придется принудительно переехать на HTTPS, поскольку раз сам vk.com работает через HTTPS, то и от iframe внутри него будет требоваться тот же протокол. А сайт, работающий на HTTPS, может работать только с SSL-вебсокетами.


Список технологий


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


  1. HTML / CSS — верстаем сайт.
  2. JavaScript — программируем клиентскую часть игры.
  3. WebGL / OpenGL ES Shading Language — делаем графон.
  4. WebSocket — делаем сеть.
  5. Protocol Buffers / Велосипед — делаем протокол клиент-серверного общения.
  6. Язык, на котором будет написан игровой сервер. Здесь выбор огромен: Java, Scala, C++, Rust, Erlang, Haskell… Что только вашей душе угодно.
  7. ...
  8. PROFIT!

Результат


Наша игра все еще находится в бете, но посмотреть на результат можно уже сейчас: oceanar.io (пока что работает только для десктопа, мобильные и планшетные версии на очереди).


Спасибо за внимание. В комментариях отвечу на ваши вопросы.

Поделиться с друзьями
-->

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


  1. Miwwa
    10.11.2016 21:53

    Периодически выполняет апдейт игрового мира с течением времени (в нашем случае — 10 раз в секунду).

    Тикрейт 10 раз в секунду — не слишком мало? Обычно в динамичных играх используется обновление не меньше 30 раз в секунду


    1. nyan
      10.11.2016 21:54

      Сначала я тоже сомневался, но потом сделал дополнительную интерполяцию на клиенте, и оказалось что этого вполне достаточно.


      1. Miwwa
        10.11.2016 22:23

        В таком случае, я не совсем понимаю, почему я не вижу адских лагов в игре.
        Получается, клиент видит остальной мир в прошлом, отставая от сервера на 100мс? Т.е. ему приходит состояние мира, и он в течение следующих 100мс интерполирует текущее состояние до последнего полученного?
        А клиент отправляет данные серверу также каждые 100мс или чаще?


        P.S. Медуз стоит заменить на каких-нибудь акул, странно выглядит, когда милая медузка пожирает половину стаи...


        1. nyan
          10.11.2016 22:36
          +2

          100 мс — это не адские лаги, возьмите секундомер и посмотрите сколько длится 1/10 секунды — это очень быстро :) Видит ли клиент мир в прошлом — вопрос сложный, поскольку время на сервере идет точечными скачками, между которыми вселенная находится в замороженном состоянии :) Скорее нет, клиент видит мир таким, каким он был то время назад, которое потребовалось чтобы доставить TCP-пакет с сервера на клиент — это может быть гораздо быстрее, чем 100 мс. И Ping в углу экрана пусть вас не смущает — внутриигровой понг отправляется не сразу, а в конце ближайшего update. Поэтому он часто близок к dt -100 мс. Клиент отправляет данные серверу да, каждые 100 мс.


        1. herr_kaizer
          11.11.2016 00:04

          Ну в WoT сервер все обрабатывает на 15 FPS в секунду, если мне не изменяет память. И это не помешало игре стать киберспортивной дисциплиной.


        1. iOrange
          11.11.2016 00:04

          Получается, клиент видит остальной мир в прошлом, отставая от сервера на 100мс?

          Очень во многих играх так делают специально — все клиенты живут немного в «прошлом». А вот на сервере идет «предсказание» того как себя поведут игроки, и пост-коррекция.
          Так получается замаскировать лаги.


  1. e_fail
    11.11.2016 02:41
    +12

    игра будет без звука (чтобы не привлекать лишнего внимания у начальства на работе)


    Так и просится название plankton.io


  1. botaniQQQ
    11.11.2016 02:57

    Что с нагрузкой, можете показать конфигурацию сервера и какое число игроков онлайн держит? Сколько игроков в одной комнате?
    Тоже есть идеи сделать .io игру, планирую на NodeJS + uWebSockets (судя по бенчмарку, превосходит WebSocket). Однако для реализации нужно держать в одной комнате от 25 000 игроков, это сильно ресурсозатратно?


    1. nyan
      11.11.2016 03:15
      +2

      Intel® Xeon® E3 Quad-Core, 32 GB RAM, точные модели уже не помню. Лимит комнаты 150 игроков, но это вопрос пока открытый. Держит 4к онлайн, но это если боты запущены на том же сервере (а они тоже жрут). 25к онлайн? Смотря какой dt, смотря какие алгоритмы на апдейте. Это все будет равномерно размазано по ядрам?

      > uWebSockets (судя по бенчмарку, превосходит WebSocket)
      Не понял, почему вы сравниваете протокол как таковой с одной из его реализаций? В любом случае — транспорт это далеко не боттлнек.


  1. botaniQQQ
    11.11.2016 03:44

    Не понял, почему вы сравниваете протокол как таковой с одной из его реализаций?

    Нет, имелось ввиду WebSocket не как технология, а чего-то подумал, что Вы используете (github.com/websockets).

    Лимит комнаты 150 игроков, но это вопрос пока открытый.

    Поставив к примеру 5 обновлений в сек. можно будет 300 чел держать в комнате без увеличения ресурсов? Не проводили тесты, сколько может максимум держать людей одна комната для комфортной игры?


  1. nyan
    11.11.2016 03:52
    +1

    а чего-то подумал, что Вы используете (github.com/websockets).

    Не используем :)

    Поставив к примеру 5 обновлений в сек. можно будет 300 чел держать в комнате без затраты на производительность?

    С точностью до небольшой погрешности — да.

    Не проводили тесты, сколько может максимум держать людей одна комната для комфортной игры?

    Комфортность игры зависит от плотности игроков, а не их количества — чтобы было не слишком одиноко, но и не слишком тесно. С теми геометрическими размерами, которые сейчас, 250 игроков в комнате — нагрузка на сервер всего ничего, но играть уже не комфортно, потому что тесновато. Поэтому пока что поставили 150. Слишком большую комнату делать не стали, иначе от края до края плыть очень долго, и мало кто увидит, что у нас отдельно нарисовано красивое дно и водная поверхность :)


    1. dolphin4ik
      11.11.2016 12:53

      А там есть дно? не доплывал ))


    1. botaniQQQ
      11.11.2016 14:25

      Кажется проснувшийся Хабр положил игрушку или как? Вот и стресс-тест провели =)


  1. itforge
    11.11.2016 04:43
    +1

    Прикольная игрушка :)


  1. qmax
    11.11.2016 06:18

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



    1. nyan
      11.11.2016 22:49

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


      1. qmax
        12.11.2016 14:15

        У такого мира чисто гипотетически должно получиться одно интересное свойство: если сгруппировать пользователей по мастерству (типа как в фэйсбучном ангрибёрдс френдс — по «лигам»), и спавнить соответственно, пользователи смогут сами регулировать «степень сложности», просто переплывая в другие области.


  1. qmax
    11.11.2016 06:21

    P.S. всётаки графически рыба — не самое удачное решение, поскольку переориентация делает анимацию дёрганой и ломает непрерывность восприятия.

    (А в качестве решения можно былобы сделать разворачивание рыбы анимированным)


  1. Jamdaze
    11.11.2016 09:38

    У меня в лисе заметно лагают .io игры. Но в хроме на той же машине лагов нет. У вас нет проблем с фпс в .io играх?


  1. MasMaX
    11.11.2016 10:59

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


  1. Blooderst
    11.11.2016 11:01

    Тоже пилю игрушку похожую на www.realmofthemadgod.com.

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


    1. beaverBox
      11.11.2016 11:15
      +1

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


      1. Blooderst
        11.11.2016 12:31

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


        1. beaverBox
          11.11.2016 12:40
          +1

          Править на сервере — сгать себе на ноги, ну или стрелять в них, что не суть. В некоторых редких ситуациях поможет, но я бы в данном случае так не делал.
          Сделайте «ширину» интерполяции длинее максимальной задержки апдейта (100+100=200мс) — и вам даже не придется голову ломать, т.к. всё само исправится на следующем тике. Конечно если у вас не с каждым апдейтом прут разногласия.


          1. Blooderst
            11.11.2016 12:51

            Исправить на следующем тике или на следующих нескольких тиках — это вообще не проблема. Проблема сделать это незаметно для игрока. Пример: игрок стоит на месте и нажимает поворот направо. нажимает в течении 5 тиков и соответственно, клиент его сразу и поворачивает. Но 1 пакет до сервера не доходит, сервер рассчитывает поворот на 4 тика и в итоге при синхронизации клиент начнет крутить игрока в противоположную сторону. А это, на мой взгляд, полный фэйл. Или я чего-то недопонимаю? Как можно исправить такое разногласие незаметно для игрока?


            1. beaverBox
              11.11.2016 13:09

              Не привязывайтесь никогда к вещественным мерилам (метры, градусы, тики, ...). И не поворачиваются персы «на 4 тика». У объектов есть свои свойства, которые, при изменении, отправляются на сервер.
              Например, у него есть:

              { xPos, yPos, rotation, walkSpeed, rotationSpeed }
              
              (поля понятны думаю).
              Вот и оперируйте последними двумя на клиенте (цепляясь ко времени, прошедшему с последнего тика), отправляя первые три на сервер.
              Это как пример реализации, с коленки так сказать. Да и движки наверное предоставляют матричные вычисления, что упростит математику.
              И вообще я не игродел )))
              Нарисуйте на бумажке схему, станет проще понять суть взаимодействия в целом, и интерполирования в частности ;)


              1. Blooderst
                11.11.2016 13:28

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


                1. beaverBox
                  11.11.2016 13:32

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


                  1. Blooderst
                    11.11.2016 13:36

                    К сожалению, в многопользовательских играх категорически нельзя перекладывать работу по просчету мира на клиентов — иначе будет читерство 80 уровня )) Во всех без исключения ММО состояние просчитывается сервером.


                    1. beaverBox
                      11.11.2016 13:47

                      Хм. Соглашусь.
                      Отвечу ниже.


                1. Blooderst
                  11.11.2016 13:33

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


                  1. beaverBox
                    11.11.2016 13:49

                    А это разве не ответ на ваш вопрос? Т.е. клиент отправляет «я поворачиваюсь», сервер принимает и поворачивает, в следующий кадр клиент всё-еще отправляет «я поворачиваюсь», сервер опять соглашается, и так далее. Но ведь сервер в то же время отправляет состояние клиенту, который и «подтягивает» состояние персов интерполяцией. Т.е. на клиенте состояние сервера уже есть, просто мы рисуем плавненько.


                    1. Blooderst
                      11.11.2016 14:31

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

                      Я потому и задал изначально вопрос, как это сделано в данной игре.

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

                      Но тут другая проблема — латенси и как следствие менее отзывчивое управление.

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

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


    1. nyan
      11.11.2016 15:06

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


  1. RabraBabr
    11.11.2016 15:02
    +1

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


    1. nyan
      11.11.2016 15:07

      На главной странице описаны правила игры.


  1. LanSaid
    11.11.2016 15:02

    Спасибо, именно такой статьи мне не хватало.


    1. nyan
      11.11.2016 22:49

      Что пилим? :)


      1. LanSaid
        14.11.2016 07:49

        ну как обычно: мега игру, которая всех порвет :) а если честно, то пока ничего: только голые идеи без реализации.


  1. u010602
    11.11.2016 15:02
    +4

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

    Мне кажется все дело в сложности игра. Змейка слишком легкая и скучная. Танки довольно сложные, и профи имеют очень сильное преимущество, я их не вижу, а они меня убивают случайным потоком из пуль. Чашка петри вышла идеальной, т.к. мелкие игроки не интересные крупным, т.е. можно легко от стариков убегать и попутно понимать суть игры. А ваша оказалась очень сложной, я вообще ни чего не понял, где еда, где враги. По моему там все враги и все меньше меня, и жрут меня моментально. Когда я нападаю на кого-то то жрут тоже меня. Как понять на кого стоит нападать я не понял. Ну и я не из тех кто готов давать второй шанс казуальным играм, т.е. либо любовь с первого взгляда, либо до свиданья.

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

    Но это лишь мнение полного новичка в ИО играх.


  1. sens_boston
    11.11.2016 21:32

    Игра работает хорошо, но вот геймплей какой-то скучный, если честно. Думаю, введение командной игры с дополнительными «фичами» (например, две дружественных стаи могут ускоренно размножаться) и целями (типа CTF), плюс запоминание логина/ника вместе с историей, могут поправить положение.


  1. sens_boston
    11.11.2016 22:08

    Проверил в Microsoft Edge на Windows 10 Mobile (да, читал, что мобильные версии на очереди): почти все работает, за исключением скроллинга страницы на начальном экране (но если перевернуть в portrait, то все OK, можно войти), и управления — срабатывает, но с огромной задержкой. А так живенько все плавает, молодцы!


  1. chabapok
    13.11.2016 02:38

    На фоне затягивающего slither (с плагином, который умеет зум, разумеется), все остальные выглядят довольно блекло. slither с недавних пор — любимая игра, сравнивать ее можно только с теплыми-ламповыми играми времен zx-specrum.


  1. R_o_u_n_d
    18.11.2016 17:33

    Без аппаратного WebGL — 3 FPS, лаги, зато глюки тестировать можно.


  1. botaniQQQ
    18.11.2016 21:33
    +1

    В общем, Меня хватило на неделю. Последние 3 дня играть невозможно. Разница между 1 и последующими местами просто катострафическая. Играя минут 30, набираешь уже огромную стаю, но она по сравнению с 1-м местом «ничто», в несколько десятков раз меньше. Набегает его просто невероятных размеров краб и съедает за секунду-полторы.

    Если с этим что-то не сделать, мало кто будет играть. К примеру в agar, если долго кататься по карте, шар автоматически уменьшается. Может и Вам как-то стаю ТОПа убивать. В общем, думайте.

    Подтверждение слов