[Из песочницы] Создание мультиплеерного 3D-шутера на Webgl без опыта и денег

Эта история о том, как маленькая команда веб-разработчиков разработала мультиплеерный 3d-шутер без опыта в геймдеве, больших денег и штата сотрудников.Дисклеймер: некоторые описанные в статье решения могут вызывать фейспалм.

2011 год. С чего все началосьВ 2011 году мы были маленькой питерской веб-студией из 4 человек. Тимлид/сеошник, программист (вернее считал себя программистом), дизайнер и менеджер по продажам. На тот момент тимлид и программист занимались сайтами уже 3 года, но особых успехов на этом поприще не достигли. Хотя мы имели уже несколько постоянных клиентов на продвижении и обслуживании, но львиную долю дохода съедали налоги, аренда офиса и прочие расходы. Оставшихся копеек на четырех человек явно не хватало. Хроническое безденежье и отсутствие перспектив угнетали. Дизайнер уже навострил лыжи, да и остальные члены команды едва держались. Вот в такой обстановке и пришла в голову потрясающая по наивности мысль: «чего это мы все для всяких дядь делаем проекты, а давайте свое что-нибудь сделаем, например, игру браузерную и запилим её во вконтакте». Все, кроме дизайнера, восприняли идею с воодушевлением.Как большинство «начинающих» геймдевелоперов, решили делать ММОРПГ. Надо сказать, что ни у кого из нас не было опыта в геймдеве. Если не считать небольшой фришард lineage2, который содержал программист и допиливал там самодельные хроники. Допиливание в основном ограничивалось серверной геймдатой, на большее не хватало — ни ума, ни знаний. Так или иначе, некоторые представления об устройстве серверной части это дало. Опыт же администрирования и общения с контингентом игроков твердо убедили в одном: клиенту верить нельзя — все, что можно сделать на сервере, нужно делать на сервере.Вернемся к нашему проекту.Обязанности распределили следующим образом: Тимлид — стал гейм дизайнером; Менеджер — взял на себя звук; Программист — остался программистом.

Изначально предполагалось, что это будет некая смесь фоллаута и мародера (на движке 7.62), с изометрией, но реалтайм.Местом действия должен был стать постапокалиптический Санкт-Петербург.

Первые шаги Техническая реализация представлялась тогда следующим образом: Игровая локация состоит из img и div тегов.Наложение друг на друга задавалось z-indexом, который рассчитывался исходя из положения объекта по оси y.Персонажа не было, его обязанности тогда исполнял белый квадрат.Связь с сервером через comet соединения.Управление через клики мышью.От comet соединений довольно быстро отказались в пользу вебсокетов. Тогда они были еще совсем сырыми и не поддерживались половиной браузеров, для которых был воткнут флеш-костыль (тогда мы наивно полагали, что бета-релиз возможен через полгода-год и кроссбраузерность нужна уже сейчас).

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

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

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

a97e52ede3cf4f668af86f9c27188182.pngОставались замкнутые области внутри объектов, в которые невозможно пройти, но это не сильно смущало

WebGL Дошла очередь до визуализации персонажа. Сложность состояла в следующем: Предполагалось что персонаж может иметь 3 положения — стоя, сидя, лежа и все это умножалось на 8 ориентаций в пространстве.Предполагались различные виды оружия: пистолеты, винтовки, пулеметы и различная броня.Первая мысль — сделать это все на спрайтах, но даже с учетом новомодного css3 transform количество спрайтов представлялось колоссальным, а возможности по кастоматизации весьма ограниченными. Начали искать другие решения. Нужно было что-то, что рисовало бы персонажа налету.Такое решение нашлось — WebGL.Первым опытом стал конструктор игр CopperCube. Сделали экспорт пустой сцены с анимированным персонажем и воткнули ее целиком вместо белого квадрата. При беге включалась соответствующая анимация, персонаж поворачивался в пространстве, как угодно. Неплохо, а может всю сцену построить на WebGL?

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

Нужно было что-то более низкоуровневое, что позволило бы создать свою игровую механику, но при этом избавляло от работы с WebGL напрямую (в 3D-программировании тогда у нас в команде никто ничего не понимал).

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

Остаток года был потрачен на перенос клиента в 3D.

abd2e7f3a0d946578dcd58e36f69661b.png

Так же были портированы редакторы объектов и карт.

7706bca949974c0783ddebba307703b0.png

2012 год. Понижаем планку С момента старта разработки прошел уже почти год, но результаты пока не впечатляли. Геймдиз выдавал идеи, наброски интерфейса, описание персонажей и будущих локаций, но до воплощения дело так и не доходило. Внятного сюжета в начале тоже не было. Была общая идея — в следствии апокалипсиса, или внезапной неизвестной атаки, чистая вода стала на вес золота. А игру на этой почве решили назвать WasteWater (сточные воды).Менеджер, взявший на себя звук, к работе так и не приступил, сайтами он тоже перестал заниматься и в итоге наши с ним пути разошлись. Так от первоначального состава нас осталось двое.

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

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

Боевой режим, куда игрок попадал нажав красную кнопку «В бой».

Вид сверху оставили, а управление решили переделать на WASD. Забегая вперед, скажу, что WASD у нас получился «не как у всех». Если обычно при таком виде W это всегда вверх, а S всегда вниз, то у нас все относительно того, куда смотрит персонаж. Такое управление непривычно, но дает больше тактических возможностей и к нему привыкаешь через пару боев.Геймплей должен был стать более динамичным, поэтому лежачие и сидячие положения персонажа были выкинуты.

Прежней осталась только ключевая фишка -все локации должны основываться на реально существующих в Питере местах.

Проблему отсутствия контента это никак не решало, но значительно сокращало минимально необходимый для старта объем.

Начали переписывать клиент. К лету был готов прототип лобби с рабочим инвентарем. Персонажа пока взяли из action quake (мод для q2, дедушка cs).

90fa6adce4b74f1fa2b02f664abe76a4.png

Все лето ушло на боевую систему. WASD-управление прикрутили поверх старой системы мышко-указательного управления: патчфайдинг для персонажа был отключен, а точка клика эмулирована перед персонажем на определенном удалении. Если игрок перемещал мышь или персонаж достигал расчетных координат, то точка рассчитывалась заново. Патчфайдинг же пригодился для мобов. Их модели тоже были взяты из какого-то q2 мода. Было написано примитивное AI, гонявшееся за игроком, ну, а всю основную работу по достижению цели делала система патчфайдинга. Несмотря на всю примитивность, наблюдение за этой искусственной жизнью завораживало.

2a6209d2232e4f37aab9cb9f43a502c9.png

Была приделана стрельба, пока еще бесшумная и беспламенная, но мобов уже можно было косить очередями, а они научились больно кусаться в ответ.Алгоритм атаки/выстрела так же был основан на отрезках и проверках на пресечение — брался условный отрезок от стрелка до максимальной дальности текущего оружия и проверялись коллизии. «Живые» объекты тоже обозначались в виде перекрещенных отрезков.

В общем к осени прототип был более-менее готов.

620ee2cb14bb454e8a7911aa16291dad.png

Контент Тем временем геймдиз времени зря не терял и прочесывал сайты, где продавались готовые 3D-модели, в поисках подходящего нам контента. Ему удалось кое-что насобирать, но этого было недостаточно.С уменьшением расходов у нас появились кое-какие свободные финансы и решили нанимать фрилансеров.Первым заказом был новый персонаж с анимацией. И первый блин, как водится, вышел комом — взяв аванс исполнитель так и не показал ничего внятного к оговоренному сроку. Знали бы мы тогда с какими сказочными персонажами нам еще предстояло поработать…К концу года мы нашли исполнителей, которые сделали нам персонажа и проанимировали его, но тут всплыла новая проблема. Дело в том что из 3DsMax, в котором была выполнена работа, на тот момент не существовало прямого экспорта анимации в three.js, поэтому пришлось использовать как промежуточный формат md2, который уже перегонялся в нативный json через онлайн-конвертор.

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

4a20d51083c14926945706e906da9011.png

Экспортировать как-то по-другому не удалось получался запредельный объем с которым браузер просто не справлялся, выдавая «опаньки». Тогда мы не были морально готовы все опять переделывать, да и вообще не было еще четкого представления как переделывать, поэтому решили пока оставить как есть.

2013 год. Первые результаты С 3D-моделлерами окружения нам тоже не везло. Сначала взялись за новое лобби, удалось сделать его с горем пополам — только со второго фрилансера.e4bbb2ba42de426aae44132052f4a4a2.png

Первая локация Начали работать над первой картой. Первоначально хотели использовать станцию метро Озерки и прилегающую территорию, но отказались от этой затеи еще на этапе проектирования. Дело в том что эта местность имеет значительный перепад высот, а клиент хоть и стал трехмерным, но серверная логика осталась 2D. Другими словами ситуация, когда персонаж стоит на вершине горы и стреляет в персонажа стоящего у подножья, а пули при этом летят не вниз, а высоко над головой цели, выглядела некрасиво.Нужно было более плоское место и таким местом было выбрано пересечение Ланского шоссе и Омской улицы, примечательной разве что, проходящими по ней высоковольтными ЛЭП.

Разметили план локации по Яндекс.Картам и перешли к реализации. Начали с от рисовки будущего террейна, за эту работу взялся новый веб-дизайнер. Площадь реальной территории была около гектара и вскоре дизайнеру пришлось улучшать компьютер, потому что старенький двух ядерник с 4 гб памяти не справлялся с объемом данных.

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

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

7b5f7d3d898842c2b3fa0bd9fa38b409.png

Приложив титанические усилия, карту все-таки собрали.

Звук Параллельно работе над картой шла работа со звуком. Была приобретена пара треков для лобби и боевого режима.У автора этих треков заказали прочие игровые звуки: выстрелы, шаги и тому подобное.Фоновую музыку просто повесили на тег audio, а игровые звуки реализовали через новомодное web audio api, которое на тот момент было еще сыровато и поддерживалось только в хроме и его родственниках, но уже предоставляло богатые возможности, в частности, позволяло позиционировать звук в пространстве в соответствии с положением его источника в 3D-сцене.

Преальфа 8 мая 2013 года мы запустили преальфа тестирование на живых людях. Подопытных было мало, но собрать какой-то фидбек и поиграть самим все-таки немного удалось.9b8f2c43f41b43e5b56e08dccd8cdbe8.jpg

По итогам преальфы были сделаны следующие выводы 1. Систему анимации надо менять;2. Система препятствий, построенная на стоп-отрезках, работает плохо: игроки зацепляются за малозаметные препятствия;3. Сама по себе ручная обводка трудозатратна и не полностью соответствует в случае со сложными геометриями;4. Отсутствует масштабируемость — приходится перебирать все имеющиеся на карте стоп-отрезки;5. Террейн-мозаика из плейнов просаживает фпс, да и стыки видны повсюду;6. Полное серверсайд движение персонажа на реальном интернете работает не так хорошо, как хотелось бы — визуально видна задержка между нажатием кнопки и реакцией игры;7. Графон в целом «ниочень».Итог Стало понятно, что переделать нужно почти все. Но отрицательный результат — тоже результат. Главным образом все это дало хоть какой-то опыт и понимание, как делать не надо.Следующие полтора года мы перерабатывали нашу игру, но об этом уже в следующей статье.

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

Скриншот из лобби 2015 г:

43d1e550c62e4a968b6229c65f91c170.png

Спасибо за внимание.

© Habrahabr.ru