Fog.Blade

Fog.Blade

«Story in a game is like a story in a adult movie.
It’s expected to be there, but it’s not that important»
—John Carmack


Fog.Blade был первым моим экспериментом в области создания игр. В те далёкие времена мир был совсем другим: не было бесплатных и простых игровых движков, не было огромных хранилищ готовых компонентов, алгоритмов, моделей и звуков. Это была романтическая эпоха, где инструменты были сложны и хитры, фотореалистичные блокбастеры ещё не выходили десятками, а маленькие студии могли заткнуть за пояс гигантов. В эту эпоху каждая игра была важна, и каждый возводил здание своей игры с нуля.

StereoDestroyer – Progress! (Fog.Blade OST, 2011)


Задумка

Шёл 2011 год. И мы, молодая команда энтузиастов, задумали проект. Fog.Blade должен был стать shoot-em-up наподобие Crimsonlands или Alien Shooter, но трёхмерным и со своими особенностями.

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

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

Карта особняка

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

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

20-мм противотанковое ружьё Lahti L-39 – герой должен был стрелять из такого на весу
Трёхствольный автомат ТКБ-059 – в реальности дальше стадии прототипа не пошёл
Карабин КС-23 – ручная гаубица под патроны устрашающего 4 калибра

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

Турельная установка B-29 – такие можно было разместить на карте

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

Но как при этом сделать игру динамичной, заставив игрока иногда всё же покидать безопасное укрытие? Это было продумано. Во-первых, убитые монстры оставляли разнообразные бонусы – здоровье, патроны, энергию – которые надо было собирать. Во-вторых, расстрелянные башнями монстры не добавляли игроку ни опыта, ни энергии, так необходимых в процессе игры. И, в третьих, некоторые из монстров спокойно доставали игрока, минуя преграды на своём пути – например, скоростные “кузнечики” могли совершать огромные прыжки через баррикады и внезапно сваливаться к игроку, чудовищные пауки спокойно ходили по стенам и потолку, а угрюмые и тяжеловесные, похожие на куски оплавленного металла “солдаты” могли стрелять поверх укреплений, заставляя персонажа остро почувствовать свою уязвимость и… и необходимость быстро найти аптечку!

StereoDestroyer – Flaming Stand! (Fog.Blade OST, 2014)


Разработка

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

Концепт главного героя авторства Sid

– Я (aka Wolf4D) – программист, гейм-дизайнер, идеолог проекта, а заодно и сценарист, тестировщик, да и, в общем, в каждой бочке затычка.
– En Silence – одарённый 3D-дизайнер, создававший для нашей разработки ряд примеров и моделей.
– Zenobian (тогда StereoDestroyer) – начинающий тогда композитор, экспериментировавший с разными стилями, и подаривший игре набор приятных треков.
– Sid – мой друг, пробовавший себя при разработке в разных ролях.
– Другие участники – появлялись и уходили время от времени.

Достаточно скоро был сформирован концепт-документ, где закреплена задумка игры, и начаты основные работы.
Одним из существенных – и, как показала жизнь, крайне спорных – решений было написание собственного игрового движка. На тот момент (2014 год) готовые игровые движки либо были крайне трудны в освоении, либо были весьма дорогостоящи, либо успешно совмещали оба этих недостатка. Имея на тот момент немалый опыт работы с языком Dark Basic (странная разновидность Basic, имеющая интегрированные возможности по работе с 3D), я тогда вёл эксперименты с DarkGDK.

В то время DarkGDK выглядел весьма прогрессивным продуктом. Это была достаточно простая (внешне) библиотека C++, предоставлявшая всем желающим базовые возможности по работе с 3D. Частности реализации 3D – вроде управления порядком рендеринга сцены, чтения файлов разных форматов, механизмов работы с шейдерами – были скрыты от программиста. Тому, кто хотел создать игру, оставалось только выдавать библиотеке простые команды уровня “загрузи из файла модель и назначь ей id равный 345” или “поверни объект с id 789 на угол в 30 градусов по оси X”. Такой подход был прост, нагляден и действенен. альтернативой ему тогда было написание низкоуровневого кода с ручной реализацией всех частностей – простейшие вещи, делавшиеся с использованием DarkGDK в три строки, в больших и умных книгах по Direct3D или OpenGL занимали несколько разворотов перегруженного кода.

Примеры Dark GDK

К DarkGDK прилагалось множество примеров. В частности, с библиотекой в комплекте был пример простейшего 3D-шутера, с уже реализованными загрузкой уровня, управлением персонажем, стрельбой, автоматическими дверями, монстрами, базовым освещением и прочими крайне нужными вещами. Я ухватился за эту возможность. По сути, остаётся ведь немного – там дописать, это доделать, то доработать – и будет готово, так?

Пример шутера из комплекта DarkGDK

Ха! Тогда мы ещё не пытались планировать действия и обозревать объёмы работ, и выпуск игры казался близким, уже виделся где-то на горизонте. Впрочем, аналогия верна – это оказалось похожим на возникающие далеко в пустыне миражи оазисов, когда ты можешь пройти километры в нужном направлении, а расстояние до цели всё не уменьшается.
Движок получил наименование LGB (по названию песни “La Geniue Boheme” – “небритый гений”) – названный так за общую неаккуратность и достижение сложных результатов за счёт простых, но безумных решений. Под разработку довольно скоро были закуплены недостающие инструменты из состава платной “обвязки” бесплатного DarkGDK – продвинутый лайтмаппер DarkLights, система физики DarkPhysics (программная обёртка над казавшимся невероятным в то время PhysX), модуль искусственного интеллекта DarkAI. Что интересно, тогда разработчик продавал большую их часть на DVD, высылая его из Англии после оплаты. Первый раз диск потерялся по дороге, и, после жалобы, разработчиками была бесплатно выслана мне ещё одна копия. Славные ребята работали в TheGameCreators!
Однако, очень скоро выяснилось, что переработки требует код загрузки уровней – объекты размещались только по направляющей сетке и не настраивались. Был написан редактор уровней Jupiter, который был соединён с сопуствующим ему лайтмаппером Ganimede. DarkLights, несмотря на ряд странностей, умел делать крайне качественные и красивые карты освещённости уровня, прорисовывая от статических объектов большие и детальные тени. Цветное освещение выглядело фантастически, чарующе. Приятно было, установив над уровнем розовато-рыжий источник света, отрендерить карты освещённости, а потом передвигаться по уровню тестовым персонажем, любуясь пробивающимися сквозь пустые ещё окна лучами сделанного своими руками рассвета…

Редактор карт Jupiter
Лайтмаппер Ganimede

Однако, стандарты того времени требовали наличия динамического освещения, bump (или лучше parallax) mapping, отличных теней (в те времена тени были предметом отдельной “гонки вооружений” в играх). В то же время, для освещения DarkGDK предлагал использование низкоуровневых подходов – на сцену полагалось только 7 источников света, поддерживаемых самой видеокартой. Для серьёзного уровня этого количества было категорически недостаточно. Потому LGB обзавёлся “менеджером света”, который занимался жонглированием имеющимися “аппаратными” источниками. Менеджер отслеживал, какие из источников на уровне (лампы, пламя, вспышки выстрелов) сейчас могут освещать объекты в кадре – и оперативно перемещал в эти точки и настраивал под нужные параметры те дефицитные “аппаратные” источники. При большом количестве выстрелов и взрывов, конечно, мог наступить момент, что источников просто не хватит – на этот случай менеджер отдавал приоритет или самым близким, или самым крупным из них.
Отдельной проблемой было совмещение bump-mapping’а с картами освещённости. Сам DarkGDK предоставлял вполне неплохой шейдер для реализации bump-а, после пары экспериментов серьёзно украсившего картинку, генерируемую движком. Однако, он был рассчитан на работу с одной текстурой цвета на объекте, а лайт-маппинг предполагал наложение ещё одной поверх! В отсутствие внятной документации по ряду вопросов и большого сообщества вокруг DarkLights, решение даже таких простых проблем занимало недели. Впрочем, удовольствие от созерцания полученного результата компенсировало все страдания.

Работа статической системы освещения

Следующей проблемой были тени. Исходно, в движке присутствовали только примитивные blob-тени – нарисованные “пятна темноты” под монстрами и предметами. Это было, конечно, лучше, чем ничего – однако, по меркам тогдашних технологий это выглядело несерьёзно. DarkGDK предлагал другой вариант – встроенные стенсильные тени. Тени эти отражали силуэт персонажа, реагировали на положение источника света, хорошо ложились на окружение и даже поддерживали сверх-модное самозатенение объектов (объект отбрасывал тень не только на окружение, но и на самого себя) – однако, они были крайне ресурсоёмкими и непременно угольно-чёрными. Выполнены они были по самой простой технологии и, вдобавок, рисовались только для одного источника освещения – с номером 0. Эта проблема, после долгих часов экспериментов, решилась путём внедрения в “менеджер света” уникальной подсистемы – теперь источники с номерами 1-6 были нормальными источниками света, источник с номером 0 имел нулевую интенсивность и назывался “источником тени”. Не создавая видимого освещения, этот источник, тем не менее, бы ответственен за генерацию теней от перемещаемых предметов и персонажей – в соответствии с его положением, тени и рисовались. “Менеджером света” источник плавно перемещался в ту точку, где находился наиболее интенсивный из присутствующих на экране источников – и создавалась видимость того, что тень создаётся именно этим, наиболее мощным световым источником.

Закат, отрабатываемый системой освещения. Тут видны статические и динамические тени, работа системы AI (конусы изображали врагов и атаковали игрока), а также деколи на стенах.

Были с тенями и другие проблемы. Например, после одного из патчей для DarkGDK они начали натурально “съезжать”. Очень напоминало сползание покрывала, накрывающего предмет мебели – тени начали отрисовываться ниже и дальше своих желаемых позиций. Хуже всего выглядела самозатенённая часть объекта – фрагмент “темноты”, предназначенный для наложения на объект, оказывался вытянут по полу. Как выяснилось, ошибка заключалась в шейдере – пришлось переписать.

Позднее, была начата переделка графической составляющей Fog.Blade на замечательные шейдеры от Evolved, а в качестве редактора карт выбран Mapscape от того же разработчика… но это уже была поздняя стадия разработки – и, увы, не спасло проект.

Работа интегрированного PhysX – от силового поля героя бодро разлетаются стулья

Завершение проекта

Шло время, и разработка шла своим ходом. Художник рисовал новые объекты. Композитор писал музыку. Я, как программист, писал движок. Однако, с каждым днём прогресс разработки всё замедлялся.

Причин было несколько:

  1. Вместо того, чтобы сделать простой и понятный вертикальный срез игры, который можно было бы наращивать новым контентом, разработка ушла в сторону горизонтального среза – то есть попытки сделать всю игру от начала до конца в минимальном объёме.
  2. Движок, определённо, опаздывал. Через полтора-два года после начала, были готовы тестовые сцены, где анимированная модель игрока могла пинками разносить по округе стулья. Начинал работать собственный редактор карт, кое-какое освещение и эффекты. Монстры, изображённые конусами, бодро атаковали игрока и союзников. Ценой невероятный усилий реализовывались собственные партиклы и деколи. Но поиграть в игру ещё было нельзя.
  3. Попытка сделать всё самостоятельно и с нуля. Каждое действие требует определённого количества человеко-часов. Даже имея в своём распоряжении профессионалов экстра-класса, невозможно сократить требуемую трудоёмкость в десять раз. Однако, вместо использования готовых решений, библиотек или даже целого движка (что сократило бы трудность работы во много раз) – было принято решение делать всё своё.
  4. Быстрое моральное устаревание движка. Когда весь мир уже вовсю использовал пост-эффекты и теневые карты – мы пытались реализовать стенсильные тени и собственные средства редактирования уровня.
  5. Энтузиазм команды пропадал по причине длительного отсутствия внятных результатов.

В 2013-м году стало очевидно, что проект себя исчерпал. Движок в попытке успеть за современными требованиями вошёл в стадию перестройки под новые технологии, что вылилось в огромный объём переработок. На фоне бума движков Unity и Unreal, стало очевидно, что гонка проиграна, и проиграна безвозвратно.

Один из концепт-промо-артов игры

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