Привет, хабровчане. Меня зовут Евгений, по профессии я backend-разработчик и пишу я на языке c# в сегменте enterprise приложений. В этой публикации я хочу рассказать вам о своём опыте в не совсем профильной для меня сфере — разработке видеоигр, а конкретнее — о разработке браузерной онлайн-игры.

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

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



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

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

Основные пункты, от которых я отталкивался:

  1. С клиентской стороны никакого Flash, только html + js;
  2. Одна машина должна тянуть как можно больший онлайн игроков;
  3. Возможность горизонтального масштабирования;
  4. Низкий порог вхождения в игру и быстрый старт;
  5. Чуть более разнообразный геймплей, чем в слитерио;
  6. Красивый и запоминающийся домен;

Далее, подробней по каждому из пунктов.

1) Клиентский код


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

Изначально взгляд упал на pixi.js — это движок, по которому немало документации, о котором положительно отзываются в плане производительности и вообще всячески хвалят.
Однако углубившись в поиски, я остановился на phaser.js (о нём уже были статьи на хабре) — это более высокоуровневая библиотека, которая позволила мне забыть о многих нюансах и сосредоточиться непосредственно на игровой логике.

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

1.1 Главная из проблем — фоновая текстура (tilesprite) жутко тормозит на windows 7
Выяснил я это с рабочего компьютера после первого деплоя на хостинг — ФПС был очень и очень низким — в районе 5. И так было во всех браузерах кроме, на удивление, IE — в нём всё работало вполне прилично, пускай и не идеально.

До того, что тормозит бэкграунд я додумался далеко не сразу — первым делом, я, методом тыка выяснил, что игра резко перестаёт тормозить при уменьшении размера окна браузера. Нагуглить по что-то по таким симптомам мне не удалось, поэтому я, профилактики ради, решил внедрить часть практик, которые советуют ребята из Mozilla — в частности, использование Object Pool (переиспользование игровых объектов). Особых успехов такого рода оптимизациями я не достиг, а профилировщик по-прежнему показывал что больше всего ресурсов съедает рендеринг.

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

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

В попытке решить эту проблему я вынес фон на отдельный канвас с меньшим z-index, чтобы он перерисовывался отдельно, независимо от движущихся объектов — особых результатов это не дало.

В конечном итоге я решил отказаться от phaser.js и работать напрямую с canvas, созданным для отрисовки фона — в результате ФПС вырос примерно до 20.

1.2 Разные версии phaser — разная производительность в разных операционках
После изменения принципа отрисовки фона с производительностью всё стало намного лучше, но 20 ФПС — это всё ещё не желаемые 60 — было над чем поработать. Путём тыканья пальцем в небо было выяснено, что phaser версии 2.4.6 работает быстрее на windows 7, а версии 2.6.2 быстрее на windows 10. На линухе и маке обе версии показали себя одинаково хорошо.

Пришлось добавить условие, которое подключало ту или иную версию библиотеки в зависимости от браузера пользователя — это повысило ФПС на моей рабочей машине до 25-30. Выше поднять ФПС у меня так и не получилось — на этом я решил остановиться, т.к. после опроса друзей/знакомых, у которых стоит семёрка, сложилось впечатление, что проблема редкая, да и уже не такая серьёзная как изначально.

Описанное в этих двух пунктах — это не единственные, но основные и наиболее запомнившиеся проблемы, связанные с phaser.js — всё остальное прошло в общем-то гладко.

Также стоит отметить, что на разных машинах с windows 7 производительность была разной — кое-где и без всех моих телодвижений всё было хорошо, где-то же наблюдались проблемы аналогичные тем, что я наблюдал — какой-либо корреляции я установить не смог


2) Производительность одного инстанса игрового сервера


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

Параллельно от разных игроков принимаются сообщения по websocket и закладываются на обработку основному потоку, который обновляет игровую логику. Основной поток работает итерациями по 40мс, в рамках которых обновляет передвижение, видимость, респавн NPC, прогресс использования способностей и т.п.

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

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

Если отобразить на схеме, то верхнеуровнево серверная архитектура выглядит так:



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

Самым серьёзным бустом к производительности был отказ от использования SignalR, т.к. он не поддерживает бинарный протокол, а на сериализацию в json уходило вычислительных ресурсов больше, чем на всю остальную логику игрового сервера вместе взятую. Остановился в итоге на использовании Fleck, т.к. он поддерживает бинарный формат, а также позволяет отключить алгоритм Нэйгла.

3) Возможность горизонтального масштабирования


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

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

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

4) Низкий порог вхождения в игру и быстрый старт


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

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

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

image

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

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

5) Чуть более разнообразный геймплей, чем в слитерио


Как бывший поклонник игры WoW, я хотел разнообразить игру, внеся в неё такие элементы как набор опыта, рост по уровням, получение новых способностей по мере роста, PvE, PvP.

Игроку доступно к использованию 6 способностей (1-я доступна сразу, 2-4 становятся доступны по мере роста по уровням, а 5-6 оформлены как одноразовые поверапы — их можно поднять на игровом поле):

  1. Удар языком — лягушка выстреливает языком и наносит малый урон первой цели на пути;
  2. Прыжок — лягушка прыгает в указанном направлении и в месте приземления наносит высокий урон. Высокий урон способности компенсируется сложностью попадания, а также длительной задержкой между использованиями;
  3. Щит — в течение 3-х секунд поглощает 2 следующие вражеские атаки;
  4. Плевок — лягушка выплёвывает снаряд, который наносит средние повреждения всем врагам на траектории движения);
  5. Лечение — восстанавливает половину жизни;
  6. Ускорение — увеличивает скорость передвижения на 100% на 4 секунды;



Для возможности немного выделиться была добавлена возможность выбрать другую модель игрового персонажа.

6) Красивый и запоминающийся домен;


Изначально в планах было разместить игру на домене .io, аналогично слитерио, агарио и многим другим играм такого формата. Банальные frog.io и frogs.io были заняты, а чего-то более подходящего в .io подобрать не удалось. Играясь с доменами, содержащими frog, наткнулся на весьма удачный вариант — frogs.world, на котором сейчас проект и живёт. Довольно-таки непривычный домен первого уровня, но зато легко запоминается.

Спасибо за внимание. Надеюсь, что кому-то мой опыт будет полезен.
Поделиться с друзьями
-->

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


  1. lain8dono
    28.06.2017 19:22

    1. taurenstyle
      28.06.2017 22:44

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

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


      1. lain8dono
        28.06.2017 23:58

        счёл приемлемым авторитарный сервер для своей игры

        Там про авторитарные сервера. Реализована ли индексация пространства (на сервере вестимо)? Это высвободит немного свободной производительности для всяких полезных непотребств.


        т.к. у меня не ФПСка

        По динамике — вариация на тему. С большим RTT попасть очень сложно. Фиг с ним, с упреждением. Они телепортируются же. И мухи, и остальные тараканы, и управляемый персонаж.


        пинги по задумке небольшие

        Даже если RTT небольшой, это не значит, что его не требуется компенсировать. Ну допустим там будет около 50 мс в одну сторону. В худшем случае задержка будет более 140 мс (50 в одну сторону, 50 в другую сторону и до 40 мс ожидания обработки). И ещё на обновление экрана и прочие мелочи. Т.е. от 1/8 до 1/4 секунды. Это заметно. Сильно. Если взять в пять раз больше — не только заметно, но и неиграбельно от слова совсем.


        в случае успеха игры

        Не будет у игры успеха, если в неё нереально играть.


        1. taurenstyle
          29.06.2017 06:59
          -1

          Там про авторитарные сервера. Реализована ли индексация пространства (на сервере вестимо)? Это высвободит немного свободной производительности для всяких полезных непотребств.

          Индексация пространства реализована.
          По динамике — вариация на тему. С большим RTT попасть очень сложно. Фиг с ним, с упреждением. Они телепортируются же. И мухи, и остальные тараканы, и управляемый персонаж.

          Если наблюдаются телепортации, то это либо откровенно плохая связь с сервером (например, если он находится где-то в азии (например в Таиланде — оттуда откровенно ужасный коннект до РФ)) или же проблемы с отрисовкой на клиенте — об этом я писал в публикации.
          Не будет у игры успеха, если в неё нереально играть.

          Она играбельна с большинства десктопных компьютеров.


        1. taurenstyle
          29.06.2017 09:18

          Даже если RTT небольшой, это не значит, что его не требуется компенсировать. Ну допустим там будет около 50 мс в одну сторону. В худшем случае задержка будет более 140 мс (50 в одну сторону, 50 в другую сторону и до 40 мс ожидания обработки). И ещё на обновление экрана и прочие мелочи. Т.е. от 1/8 до 1/4 секунды. Это заметно. Сильно. Если взять в пять раз больше — не только заметно, но и неиграбельно от слова совсем.

          50 мс в одну, сторону 50 в другую — это, простите, пинг в 100мс, т.е. заведомо очень много. Это пинг из США до моего сервера. После выхода на иностранные рынки, в тех же штатах будут покупаться сервера и пинг будет в районе 10-20мс (т.е. 5-10мс в каждую сторону, что со временем обработки на сервере в 40мс даёт нам максимум 50мс, которые малозаметны).


  1. m_izmailov
    28.06.2017 20:03

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


  1. HellWalk
    28.06.2017 20:03

    Видел вашу игру, побаловался 10 минут. Интересно было бы услышать ваше мнение по следующим темам:

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


    1. copal
      28.06.2017 21:56
      -5

      1. Собираем. Собираем статистику время потраченного на геймплей, а также касающуюся рациона пользователей. делаем это для выявления зависимости между пониженным употреблением белковой пищи и временем потраченным на охоту на мух.
      2. Мы собираемся добавить туда возможность мутации лягушка->кракодил->птерозавль, а так же магазин с сувенирами мух.
      3. Хотим пойти на кикстартер с идеей создания мушных ферм.


      1. taurenstyle
        28.06.2017 22:58

        Я надеюсь, что никто не воспринял этого товарища всерьёз? Никакого отношения к моему проекту он не имеет


    1. taurenstyle
      28.06.2017 22:57

      — На текущий момент всё, что есть — это Google Analytics. Согласно его данным, среднее время онлайн одного пользователя за сегодня — это 4 с половиной минуты. Но тут есть нюанс — игрок может проводить и 30 минут времени на странице с игрой (при условии что его не убьют), а GA посчитает, что сеанс закончился раньше;
      — Текущий геймплей да, несколько примитивен — я выложил, по сути, первую версию игры, чтобы собрать фидбэки, отловить ошибки.
      В дальнейшем планирую ввести боссов, новые классы лягушек (как минимум будет лягушка-милишник), кастомизацию в рамках класса (сдвиг баланса между характеристиками игрока: скорость, здоровье, повреждения);
      — Одним из источников монетизации однозначно будет контекстная реклама.
      Также можно будет подумать на тему того, чтобы продавать игрокам некоторые бонусы, которые не будут давать преимуществ над другими игроками, но будут экономить время — например, эффект, который позволяет в течении N суток не терять опыт из-за смерти.
      Помимо этого, можно продавать иные модели лягушек — на текущий момент есть скины, которые приобретаются за расшаривание в соц. сетях — в будущем к ним можно добавить модели, которые можно приобрести только за деньги.


  1. oduvan
    29.06.2017 09:57

    > Пришлось добавить условие, которое подключало ту или иную версию библиотеки в зависимости от браузера пользователя — это повысило ФПС на моей рабочей машине до 25-30

    выглядит как очень странное поведение. имеет смысл написать issue в phaser репу


  1. developer7
    29.06.2017 10:06

    Я пробовал играть — меня хватило на 10 секунд. Это потому как управление не удобно. Самое главное — стрелять обязательно кликом мышки. Ходить перемещением мыши. Посмотрите на проект agar.io или spaceone.io. Вот там действительно можно залипать часами.


    1. taurenstyle
      29.06.2017 10:13

      Видится мне, что для моей игры не подойдёт управление аналогичное тому, что в agar.io или spaceone.io.
      Во-первых абилок у меня целых 6 штук, поэтому все их на мышь не подвесишь. Помимо этого, даже будь только одна способность — удар языком — было бы крайне тяжело попадать во врага, т.к. если ты постоянно двигаешься, то придётся нарезать круги вокруг цели — не уверен что это было бы удобно — формат игры малость не тот.


      1. HellMaster_HaiL
        29.06.2017 10:34

        Может использовать следующий стиль управления?
        1. Перемещение на WASD или хотя бы на W (если ходить в одном напрвлении)
        2. Указатель мыши указывает основное направление движения (вперед).
        3. Абилки биндить на кнопки мыши (по выбору юзера) + все абилки на цифровые клавиши.

        Или как альтернатива:
        W — идти
        Space — прыжок
        Q — плевок
        E — щит
        Mouse Left — язык

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


      1. developer7
        29.06.2017 11:42

        А какой формат игры тогда? Я ни разу не геймер, но насколько мне представляется геймплей он может быть либо бродилка с познанием мира, либо «экшен» типа «мортал комбата», где смысл в оттачивание реакции и мастерства. У вас точно не бродилка — мира нету. Да и текстура честно говоря уж очень унылая. Добавьте что ли кувшинки и листья, типа эмитировать поверхность пруда. Заодно и преграда будет которую нужно огибать, уже интереснее.
        Раз вы отрицаете наматывания кругов противниками, «экшен» тоже вычёркивается. Какой тогда смысл игры?