на чем пишутся браузерные игры
На каких языках программирования пишут игры
Новички часто спрашивают, на каком языке программирования можно создать игру. Поставим точку в этом вопросе.
Ориентироваться лучше на то, что хотите реализовать и на какой платформе:
Если создаёте игру впервые, воспользуйтесь каким-нибудь движком:
Пишет о программировании, в свободное время создает игры. Мечтает открыть свою студию и выпускать ламповые RPG.
В чём отличие языков для создания игр
У каждого языка свои преимущества и назначение, поэтому не стоит думать, что какой-то лучше остальных — все они для решения разных задач. Многие, например, пишут большую часть проекта на одном, а высоконагруженную — на другом.
Чтобы выбрать, какой язык подойдёт вам, давайте разберемся в нескольких моментах. А для совсем новичков мы предлагаем курс «Профессия Разработчик игр на Unreal Engine 4».
Браузерные игры
Они хоть и не такие крутые, как игры для консолей и компьютеров, но тоже затягивают. Причина в умелой работе гейм-дизайнеров — они продумывают механики так, чтобы вы тратили на игру больше времени и денег.
Если вы играли хотя бы в одну крупную браузерную игру, то знаете: игровой процесс дозируют, чтобы игроку не наскучило. А чтобы продолжить игру, приходите на следующий день либо платите.
Тут уже можно подключить JavaScript — он позволяет хранить в переменных данные персонажа, а графику обрабатывать с помощью Canvas. Если прикрутить PHP, получится создать базу данных, построить защиту и реализовать многопользовательский режим. А это уже полноценная браузерная игра.
Многие из таких игр создаются на Flash, который работает на языке ActionScript. Мы не рекомендуем изучать эту технологию, потому что скоро её поддержка будет прекращена, а на HTML5 появится ещё больше возможностей, чтобы полностью её заменить.
Примеры браузерных приложений
Игры для мобильных устройств
Мобильные игры превосходят браузерные, но не сильно. Маленький экран и небольшая мощность не позволяют создавать крутую графику. И ещё нельзя реализовать такое же удобное управление, как на компьютерах и приставках.
Это компенсируется простотой разработки. Можно скачать популярный движок и за несколько недель выпустить готовое приложение — это программы, которые предоставляют готовые решения для работы с графикой и физикой. Разработчику остается только добавить спрайты или модели, а потом прописать несколько скриптов на одном из предложенных языков. Можно даже не заморачиваться из-за всех ресурсов — они скачиваются или покупаются в интернете.
Unity, один из самых популярных движков, даёт возможность писать на C# и JavaScript. Подключаете скачанные файлы, пишете несколько команд — и простенькая игра готова.
Примеры мобильных игр
Компьютерные и консольные игры
Тут настоящий размах. Реалистичная графика, VR, большой игровой мир, поддержка огромного количества игроков онлайн и так далее. Можно создавать проекты вроде Limbo или Super Meat Boy в одиночку, а можно в команде разрабатывать новый Fortnite.
Но и сложность возрастает. Чем масштабнее вы мыслите, тем больше работы предстоит проделать. Вот неполный список аспектов разработки, над которыми следует потрудиться:
В таких играх ведется работа над освещением, тенями, частицами, разрушаемостью — всем, что важно для конкретного проекта. Один человек не потянет всё это за год или даже два, поэтому такое под силу только крупным студиям. Конечно, многое решается в движках, но работа всё равно колоссальная.
Без движков тоже можно обойтись: World of WarCraft был написан на C++, а MineCraft создан одним человеком на Java, после чего игру купила компания Microsoft за 2,5 миллиарда долларов.
Даже Super Mario написали на ассемблере, когда о движках никто и не задумывался.
Создание браузерных 3d-игр с нуля на чистом html, css и js. Часть 1/2
Современная вычислительная техника позволяет создавать классные компьютерные игры! И сейчас, достаточно популярны игры с 3d-графикой, так как, играя в них, ты окунаешься в вымышленный мир и теряешь всякую связь с реальностью. Развитие интернета и браузерных технологий сделало возможным запускать головоломки и стрелялки в любимом Хроме, Мозилле или еще в чем-то там (про Эксплорер помолчим) в онлайн-режиме, без загрузки. Так вот, здесь я расскажу о том, как создать простую трехмерную браузерную игру.
1. Инструменты для разработки
Я использую для проверки сайтов и игр только 2 браузера: Chrome и Mozilla. Все остальные браузеры (кроме того самого Эксплорера) построены на движке первого, поэтому использовать их я не вижу смысла, ибо результаты точно такие же, как и в Chrome. Для написания кода достаточно Notepad++.
2. Как реализуется трехмерное пространство в html?
Посмотрим на систему координат блока:
По умолчанию, дочерний блок имеет координаты (left и top) 0 пикселей по x и 0 пикселей по y. Смещение (translate), также 0 пикселей по всем трем осям. Покажем это на примере, для чего создадим новую папку. В нем создадим файлы index.html, style.css и script.js. Откроем index.html и запишем туда следующее:
В файле style.css зададим стили для элементов “container” и “world”.
Сохраним. Откроем index.html c помощью Chrome, получим:
Попробуем применить translate3d к элементу “world”:
Как вы поняли, я перешел в полноэкранный режим. Теперь зададим смещение по оси Z:
transform:translate3d(200px,100px,-1000px);
Если вы снова откроете html-файл в браузере, то никаких изменений вы не увидите. Чтобы увидеть изменения, нужно задать перспективу для объекта “container”:
Квадрат отдалился от нас. Как работает перспектива в html? Взглянем на картинку:
Теперь повернем “world” вокруг какой-нибудь оси. В сss можно использовать 2 способа вращения. Первый – вращение вокруг осей x,y и z. Для этого используются transform-свойства rotateX(), rotateY() и rotateZ(). Второй – вращение вокруг заданной оси с помощью свойства rotate3d(). Мы будем использовать первый способ, так как он больше подходит для наших задач. Обратите внимание, что оси вращения выходят из центра прямоугольника!
Заметно смещение против часовой стрелки. Если же мы добавим rotateY(), то получим смещение уже по оси Y. Важно заметить, что при вращении блока оси вращения также поворачиваются. Вы также можете поэкспериментировать с различными значениями вращения.
Теперь внутри блока “world” создадим еще один блок, для этого добавим тег в html-файл:
В style.css добавим стили к этому блоку:
То есть, элементы внутри блока “world” будут трансформироваться в составе этого блока. Попробуем повернуть “square1” по оси y, добавив к нему стиль вращения:
transform: rotateY(30deg);
«Где вращение?» — спросите вы? На самом деле именно так выглядит проекция блока “square1” на плоскость, образуемую элементом “world”. Но нам нужна не проекция, а настоящее вращение. Чтобы все элементы внутри “world” стали объемными, необходимо применить к нему свойство transform-style:preserve-3d. После подстановки свойства внутрь списка стилей “world” проверим изменения:
Отлично! Половина блока “square” скрылась за голубым блоком. Чтобы его полностью показать, уберем цвет блока “world”, а именно, удалим строку background-color:#C0FFFF; Если мы добавим еще прямоугольников внутрь блока “world”, то мы можем создать трехмерный мир. Сейчас же уберем смещение мира “world”, удалив строку со свойством transform в стилях для этого элемента.
3. Создаем движение в трехмерном мире
Для того, чтобы пользователь мог по этому миру передвигаться, нужно задать обработчики нажатия клавиш и перемещения мыши. Управление будет стандартным, какое присутствует в большинстве 3д-шутеров. Клавишами W, S, A, D мы будем перемещаться вперед, назад, влево, вправо, пробелом мы будем прыгать (проще говоря – перемещаться вверх), а мышью мы будем менять направление взгляда. Для этого откроем пока еще пустой файл script.js. Сначала впишем туда такие переменные:
Изначально клавиши не нажаты. Если мы нажмем клавишу, то значение определенной переменной изменится на 1. Если отпустим ее, то она снова станет 0. Реализуем это посредством добавления обработчиков нажатия и отжатия клавиш:
Номер 32 – код пробела. Как видите, тут появилась переменная onGround, указывающая на то, находимся ли мы на земле. Пока разрешим движение вверх, добавив после переменных press… переменную onGround:
Итак, мы добавили алгоритм нажатия и отжатия. Теперь необходимо добавить само передвижение. Что, собственно, мы передвигаем. Представим, что у нас есть объект, который мы двинаем. Назовем его “pawn”. Как и принято у нормальных разработчиков, для него мы создадим отдельный класс “Player”. Классы в javaScript создаются, как ни странно, с помощью функций:
Вставим этот код в script.js в самом начале файла. В конце же файла создадим объект данного типа:
Распишем, что означают эти переменные. x, y, z – это начальные координаты игрока, rx, ry – углы его поворота относительно осей x и y в градусах. Последняя записанная строка означает, что мы создаем объект “pawn” типа “player” (специально пишу тип, а не класс, так как классы в javascript означают несколько другие вещи) с нулевыми начальными координатами. Когда мы двигаем объект, координата мира изменяться не должна, а должна изменяться координата «pawn». Это с точки зрения переменных. А с точки зрения пользователя, игрок находится на одном месте, а вот мир двигается. Таким образом, нужно заставить программу изменять координаты игрока, обрабатывать эти изменения и двигать, в конце концов, мир. На деле это проще, чем кажется.
Итак, после загрузки документа в браузер мы запустим функцию, которая перерисовывает мир. Напишем функцию перерисовки:
В новых браузерах world будет соответствовать элементу с однако надежнее ее присвоить перед функцией update() с помощью следующей конструкции:
Мы будем изменять положение мира каждые 10 мс (100 обновлений в секунду), для чего запустим бесконечный цикл:
Запустим игру. Ура, теперь мы можем двигаться! Однако мир вылазит за пределы рамок элемента «container». Чтобы этого не происходило, зададим css-свойство для него в style.css. Добавим строку overflow:hidden; и посмотрим на изменения. Теперь мир остается в пределах контейнера.
Вполне возможно, что вы не всегда понимаете, куда нужно записывать те или иные строчки кода, поэтому сейчас я вам представлю файлы, которые, как я полагаю, у вас должны получиться:
Если у вас что-то по-другому, обязательно поправьте!
Мы научились двигать персонажа, однако мы еще не умеем поворачивать его! Поворот персонажа, конечно же, будет осуществляться с помощью мыши. Для мыши к переменным состояния клавиш press… мы добавим переменные состояния движения мыши:
А после обработчиков нажатия-отжатия вставим обработчик движения:
В функцию update добавим поворот:
Обратите внимание на то, что движение мыши по оси y вращает pawn по оси x и наоборот. Если мы посмотрим на результат, то ужаснемся от увиденного. Дело в том, что если смещения нет, то MouseX и MouseY остаются прежними, а не приравниваются к нулю. Значит, после каждой итерации update смещения миши должно обнуляться:
Уже лучше, мы избавились от инерции вращения, однако вращение происходит все равно странно! Чтобы понять, что все-таки происходит, добавим div-элемент «pawn» внутрь «container»:
Зададим ему стили в style.css:
Проверим результат. Теперь все ровно! Единственное — синий квадрат остается впереди, но пока оставим это. Чтобы сделать игру от первого лица, а не от третьего, нужно приблизить мир к нам на значение perspective. Сделаем это в script.js в функции update():
Теперь можно делать игру от первого лица. Скроем pawn добавив строку в style.css:
Отлично. Сразу скажу, что ориентироваться в мире с одним квадратом крайне тяжело, поэтому создадим площадку. Добавим в «world» блок «square2»:
А в style.css добавим стили для него:
Теперь все четко. Ну… не совсем. Когда мы нажимаем по клавишам, мы движемся строго по осям X и Z. А мы хотим сделать движение по направлению взгляда. Сделаем следующее: в самом начале файла script.js добавим 2 переменные:
Градус — это pi/180 от радиана. Нам придется применить синусы и косинусы, которые считаются от радиан. Что нужно сделать? Взгляните на рисунок:
Когда наш взгляд направлен под углом и мы хотим пойти вперед, то изменятся обе координаты: X и Z. В случае перемещения в сторону тригонометрические функции просто поменяются местами, а перед образовавшимся синусом изменится знак. Изменим уравнения смещений в update():
Внимательно просмотрите все файлы полностью! Если у вас что-то оказалось не так, то потом обязательно буду ошибки, из-за которых вы сломаете голову!
С движением мы почти разобрались. Но осталось неудобство: курсор мыши может двигаться только в пределах экрана. В трехмерных шутерах можно вращать мышью сколь угодно долго и сколь угодно далеко. Сделаем также: при нажатии на экран игры (на “container”) курсор будет пропадать, и мы сможем вращать мышью без ограничений на размер экрана. Активируем захват мыши при нажатии на экран, для чего перед обработчиками нажатия клавиш поставим обработчик нажатия мыши на “container”:
Теперь совсем другое дело. Однако лучше вообще сделать так, чтобы вращение производилось только тогда, когда курсор захвачен. Введем новую переменную после переменных нажатия клавиш press…
Добавим обработчик изменения состояния захвата курсора (захвачен или нет) перед обработчиком захвата курсора (извините за тавтологию):
А в update() добавим условие вращения “pawn”:
А сам захват мыши при клике по контейнеру разрешим только тогда, когда курсор еще не захвачен:
С движением мы полностью разобрались. Перейдем к генерации мира
4. Загрузка карты
Мир в нашем случае удобнее всего представить в виде множества прямоугольников, имеющих разное местоположение, поворот, размеры и цвет. Вместо цвета также можно использовать текстуры. На самом деле, все современные трехмерные миры в играх – это набор треугольников и прямоугольников, которые называют полигонами. В крутых играх их количество может достигать десятков тысяч в одном только кадре. У нас же их будет около сотни, так как браузер сам по себе имеет невысокую графическую производительность. В предыдущих пунктах мы вставляли блоки “div” внутрь “world”. Но если таких блоков много (сотни), то вставлять каждый из них в контейнер очень утомительно. Да и уровней может быть много. Поэтому пусть эти прямоугольники вставляет javaScript, а не мы. Для него же мы будем создавать специальный массив.
Откроем index.html и удалим из блока “world” все внутренние блоки:
Теперь создадим массив прямоугольников (запихнем его, примеру, между конструктором player и переменными press… в script.js):
Можно было это сделать в виде конструктора, но пока обойдемся чисто массивом, так как запуск цикла расстановки прямоугольников проще реализовать именно через массивы, а не через конструкторы. Я же поясню, что означают цифры в нем. Массив map содержит одномерные массивы из 9 переменных: [. ]. Я думаю, вы понимаете, что первые три числа – это координаты центра прямоугольника, вторые три числа – углы поворота в градусах (относительно того же центра), затем два числа – его размеры и последнее число – фон. Причем фон может быть сплошным цветом, градиентом или фотографией. Последнее очень удобно использовать в качестве текстур.
Массив мы записали, теперь запишем функцию, которая переделает этот массив в собственно прямоугольники:
Поясню, что происходит: мы создаем новую переменную, которая указывает на только что созданный элемент. Ему мы присваиваем id и css-класс (именно это и имеется ввиду под словом класс в языке javaScript), задаем ширину с высотой, фон и трансформацию. Примечательно, что в трансформации помимо координат центра прямоугольника мы указываем смещение на 600 и 400 и половины размеров для того, чтобы центр прямоугольника точно оказался в точке с нужными координатами. Запустим генератор мира перед таймером:
Теперь мы видим площадку с розовыми стенами и серым полом. Как видите, создание карты технически несложно реализовать. А в результате ваш код в трех файлах должен получиться примерно таким:
Если все хорошо, переходим к следующему пункту.
5. Столкновения игрока с объектами мира
Мы создали технику движения, генератор мира из массива. Мы можем передвигаться по миру, который может быть красивым. Однако наш игрок еще никак не взаимодействует с ним. Чтобы это взаимодействие происходило, нам необходимо проверять, сталкивается ли игрок с каким-нибудь прямоугольником или нет? То есть, мы будем проверять наличие коллизий. Для начала вставим пустую функцию:
А вызывать ее будем в update():
Как это происходит? Представим себе, что игрок – это шар с радиусом r. И он движется в сторону прямоугольника:
Очевидно, что если расстояние от шара до плоскости прямоугольника больше r, то коллизии точно не происходит. Чтобы узнать это расстояние, можно перевести координаты игрока в систему координат прямоугольника. Напишем функцию перевода из мировой системы в систему прямоугольника:
И обратную функцию:
Вставим эти функции после функции update(). Я не буду объяснять, как это работает, потому что мне не хочется рассказывать курс аналитической геометрии. Скажу, что есть такие формулы перевода координат при вращении и мы просто ими воспользовались. С точки зрения прямоугольника наш игрок расположен вот так:
В этом случае условие коллизии становится таким: если после смещения шара на величину v (v – это вектор) координата z между –r и r, а координаты x и y лежат в пределах прямоугольника или отстоят от него на величину, не большую r, то объявляется коллизия. В этом случае координата игрока по z после смещения будет составлять r или – r (в зависимости от того, с какой стороны придет игрок). В соответствии с этим, смещение игрока изменяется. Мы специально вызываем коллизию перед тем, как в update() координаты игрока будут обновлены, чтобы вовремя изменить смещение. Таким образом, шар никогда не пересечется с прямоугольником, как бывает в других алгоритмах коллизии. Хотя физически игрок будет представлять собой, скорее, случае куб, мы не будем обращать на это внимание. Итак, реализуем это в javaScript:
x0,y0 и z0 – начальные координаты игрока в системе координат прямоугольника (без поворотов. x1,y1 и z1 – координаты игрока после смещения без учета коллизии. point0, point0, point1 и point2 – начальный радиус-вектор, радиус-вектор после смещения без коллизии и радиус-вектор с коллизией соответственно. map[i][3] и другие, если вы помните, это углы поворота прямоугольника. Заметим, что в условии мы к размерам прямоугольника прибавляем не 100, а 98. Это костыль, зачем, подумайте сами. Запустите игру и вы увидите довольно качественные столкновения.
Как видим, все эти действия происходят в цикле for для всех прямоугольников. При их большом количестве такая операция становится очень дорогой, так как тут и так есть 3 вызова функций преобразований координат, которые тоже производят достаточно много математических операций. Очевидно, что если прямоугольники находятся очень далеко от игрока, то коллизию считать не имеет смысла. Добавим это условие:
Итак, с коллизиями мы разобрались. Мы спокойно можем взбираться и по наклонным поверхностям, а возникновение багов возможно только на медленных системах, если, конечно, возможно. По сути, вся основная техническая часть на этом закончилась. Нам осталось лишь добавить частные вещи, такие как гравитация, вещи, меню, звуки, красивую графику. Но это достаточно легко сделать, а к самому движку, который мы только что сделали, это отношения не имеет. Поэтому об этом я расскажу в следующей части. А сейчас проверьте то, что у вас получилось с моим кодом:
Разработка браузерной онлайн-игры
Привет, хабровчане. Меня зовут Евгений, по профессии я backend-разработчик и пишу я на языке c# в сегменте enterprise приложений. В этой публикации я хочу рассказать вам о своём опыте в не совсем профильной для меня сфере — разработке видеоигр, а конкретнее — о разработке браузерной онлайн-игры.
Я привык относить себя к тем везучим людям, у которых хобби совпадает с работой — я люблю разработку ПО. Поэтому для меня абсолютно нормально, вернувшись домой, вновь сесть за компьютер, открыть Visual Studio и продолжить что-то разрабатывать — отдых от этой деятельности мне не нужен. Проблема лишь одна — нужен проект, который мне интересен и который я смог бы осилить один в свободное время — по вечерам и в выходные дни.
Примерно год назад мне показали довольно популярную браузерную онлайн-игру — слитерио. После ознакомления у меня появилась навязчивая идея — мне захотелось сделать что-то похожее по подходу, но с чуть более продвинутым геймплеем. Спустя пару месяцев идея сформировалась в тему этой публикации — игру World of Frogs.
Суть игры — вы управляете лягушкой, можете нападать на других игроков, а также на управляемые компьютером объекты — мух, тараканов, болотных лягушек. Мухи не умеют нападать и умирают с одного удара, тараканы нападают лишь обороняясь, болотные же лягушки нападают как на мух, так и на игроков.
Побеждая врагов вы получаете опыт, растёте по уровням, изучаете новые способности и становитесь сильнее.
Основные пункты, от которых я отталкивался:
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) Низкий порог вхождения в игру и быстрый старт
Мне не раз приходилось видеть игры, которые просто обязаны иметь наиужаснейшую конверсию из-за перегруженности интерфейса или же из-за необходимости вводить миллион полей для того, чтобы зайти в игру.
Я хотел обеспечить вход по одному клику мыши с главной страницы, т.е. никакой обязательной регистрации. В том же слитерио используется похожий подход, за одной небольшой разницей — от игрока всё же хотят, чтобы он ввёл ник.
Т.к. онлайн игра как правило предполагает возможность отличать одного игрока от другого, я решил использовать подход никогенерации — при входе в игру берётся случайное прилагательное из заранее заданного списка и комбинируется с случайным существительным, что выдаёт ники вида Неспящий Бугай, Жадный Бурундук, Могучий Валенок и т.п…
В дальнейшем, если игроку понравилась игра, ему предоставляется возможность зарегистрироваться, сохранив за собой прогресс, а также сменив ник на что-то более вменяемое.
Для более быстрого погружения на главную страницу было добавлено обучающее видео, а в саму игру были добавлены тултипы, выплывающие при изучении новых способностей.
5) Чуть более разнообразный геймплей, чем в слитерио
Как бывший поклонник игры WoW, я хотел разнообразить игру, внеся в неё такие элементы как набор опыта, рост по уровням, получение новых способностей по мере роста, PvE, PvP.
Игроку доступно к использованию 6 способностей (1-я доступна сразу, 2-4 становятся доступны по мере роста по уровням, а 5-6 оформлены как одноразовые поверапы — их можно поднять на игровом поле):
Для возможности немного выделиться была добавлена возможность выбрать другую модель игрового персонажа.
6) Красивый и запоминающийся домен;
Спасибо за внимание. Надеюсь, что кому-то мой опыт будет полезен.