Любимым моим проектом является «Lost Flagship» – стратегическая игра, разработанная на хакатоне Ludum Dare №45 в 2019 году. Это небольшая пошаговая 4X-стратегия, стилизованная под настольную игру. Игра получила хорошие оценки, но, к сожалению, из-за ряда технических проблем (о них я расскажу ниже) не смогла полноценно бороться за победу. И ниже я расскажу о причине этого – а также о том, как мне удалось совместить в этом проекте увлекательность, приятный игровой баланс и сверхмалое время разработки 🙂
В «Lost Flagship» игрок примеряет на себя роль командующего самым большим в галактике космическим кораблём – флагманом Земного флота, славным и могучим боевым гигантом. Правда, дни его величия явно позади – корабль давно преобразован в “плавучий музей”, памятник собственной былой славы. Эпоха межзвёздных войн и исполинских боевых машин прошла – и наше судно, украшенное позолотой и лишённое орудий главного калибра, теперь медленно ржавеет на орбите захолустной планеты, навещаемое разве что редкими группками туристов.
Дни экипажа из трёх человек проходят в скуке, распивании кофе и мелких ссорах. Но однажды привычный распорядок нарушается странным электромагнитным штормом, вырывающим старый флагман из привычной реальности и бросающим его прямо в центр грандиозной межзвёздной схватки. Всё, чем располагают капитан и его помощники – старый флагман и собственная отвага. Чтобы выжить и вернуться домой, теперь им придётся вмешаться в ход таинственной войны, собрать собственный несокрушимый флот и разгадать загадочную природу похищающих корабли “времятрясений”.
Начало разработки
Правила Ludum Dare не слишком строги. На разработку с момента объявления темы конкурса даётся трое суток. При этом допускается использование “домашних заготовок” практически любой сложности. Идея 4X-стратегии про космос зрела у меня довольно давно, задумка для сюжета крутилась где-то рядом, новые рабочие инструменты ждали испытания. Отлично, но… три дня – срок для стратегии утопичный.
Очевидно, что за 3 дня невозможно сделать большую, сложную и разнообразную игру. Времени не хватит ни на реалистичную графику, ни на зрелищные катсцены, ни на многогранный геймплей. Тем не менее, успеть возможно. Всё, что остаётся – это делать игру максимально концентрированной, отжатой. Реализовать только то, что приносит игроку непосредственные ощущения и удовольствие от процесса. Всё остальное – то, что не осядет в памяти игрока – выбросить без сожаления. Сузить перечень работ, чтобы на выходе получился маленький, но готовый продукт.
Я сел набросать список фич и задумок. В итоге был создан план работ – примерно такой:
- Придумать core-механику игры, принципы добычи и распределения ресурсов, строительства юнитов.
- Определиться с визуальным стилем игры.
- Написать код логики игровой карты поля боя, выбора юнита, управления и приказов.
- Воплотить систему сторон конфликта и систему их ходов.
- Определиться с набором игровых юнитов, настроить баланс.
- Реализовать графическую составляющую – эффекты, модели, UI.
- Создать и оттестировать искусственный интеллект противников.
- Сделать начальную и конечную заставки.
- Создать достаточное количество уровней, скрипты, текстовые и сюжетные вставки.
- Провести полировку и выпустить игру в релиз.
Как ни странно, план оказался достаточно хорошей отправной точкой для дальнейшей работы. Однако, один важный этап в плане отсутствует, что ещё не раз аукнулось в дальнейшем…
Игровая механика
Механика ресурсов
В качестве отправной точки я решил взять игры серий Super Robot Wars и Front Mission. Тамошняя core-механика – пошаговый бой, где сначала ходят все “свои” роботы, потом – все “чужие”. Каждый из роботов за ход может или переместиться, или выстрелить (хотя некоторые совмещают и то, и другое), причём атакованный робот тут же отвечает обидчику огнём.
Я решил переиначить геймплей в духе 4X-стратегий, при этом сохранив тактический дух оригинала. Первым делом хотелось добавить возможность строительства юнитов – значит, нужны ресурсы. Наличие ресурсов подразумевает наличие ресурсных точек – мест на карте, которые надо отбивать и оборонять.
Потому в “Lost Flagship” появились подпространственные вышки. Такая вышка принципиально неразрушима и захватывается размещением своего корабля в клетке с ней. Каждый ход захваченная вышка приносит своему обладателю небольшую сумму местных денег. Деньги можно потратить на покупку дополнительных кораблей, которые игрок может разместить… только поблизости от любой из своих вышек. В итоге, вышки выполняют роль одновременно роль источника ресурсов и производящих зданий.
Хочешь нападать? Захвати вышку поближе к врагу и строй подкрепления там. Надо лишить противника притока денег? Отвоюй у него вышки. Нужно дополнительные ресурсы? Ищи и захватывай нейтральные.
Вышки получились идеальными точками интереса. Игроку, вне зависимости от целей миссии и выбранной тактики поведения, обязательно надо их захватить. Их приятно захватить, владение ими удобно. Исследование карты обрело смысл, а сражения – оправданную цель.
Механика ходов
Одна проблема была решена. Но с внедрением системы ресурсов, core-механика тут же начала трещать по швам.
Ещё на этапе первых тестов стало понятно, что возможность быстро и неограниченно строить корабли тут же превращает игру в серию бесконечных баталий “стенка на стенку”. Захват любой ресурсной точки становится огромной проблемой: на место павших можно в мгновение ока плодить всё новые и новые корабли. Такие бои в пошаговом режиме выглядят невероятно однообразно. Требовалось внедрить ограничение – простое в реализации, но эффективное.
Я решил ограничить число действий за ход. В начале каждого из ходов игрок (или компьютер) получает четыре очка действия, которые может потратить на своё усмотрение. Любой приказ – перемещение, атака, покупка корабля – расходует ровно одно очко. Очки нельзя накопить, нельзя заработать.
Ограничение тут же вернуло геймплею тактическую глубину. Стало невозможно накопить пару дюжин кораблей, “обвести их рамочкой” и послать в атаку. Невозможно построить сотню лёгких и дешёвых скаутов – и взять противника наскоком. Теперь армии надо тщательно подбирать, совместно перемещать атакующие группы, и аккуратно лавировать между агрессивным и оборонительным поведением. Игроки отметили эту механику как крайне удачную.
Визуальный стиль
Срок в три дня не оставлял возможность сделать игру сколько-нибудь реалистичной. Времени на исполнение множества деталей не было, игра должна была быть сделана из крупных форм, чётких силуэтов и бросающихся в глаза контрастов. Было принято решение делать её предельно eye-candy – яркой, красочной, “чтобы хотелось облизнуть”. Любой эффект должен быть нарочитым, преувеличенным. Лишь главные формы, все мелочи и частности мозг игрока додумает сам.
В условиях огромной нехватки времени, было принято решение максимально задействовать ресурсы, дающие “быстрый и грязный” результат – полуфабрикаты и заготовки, процедурно сгенерированные элементы, готовые бесплатные ассеты. Так, космос в игре сделан из трёх основных элементов:
- Бесплатный набор планет – со своим шейдером, освещением и кучей вариантов.
- Фоны, созданные замечательным и свободным процедурным генератором.
- Множество бесплатных эффектов для межзвёздного газа, микрометеоритов и проч.
Самое важное в игре – это модели кораблей. Бесплатных кораблей в Asset Store великое множество, но стилистически они отличаются до уровня полной несовместимости. Мой любимый пакет для трёхмерного моделирования, Doga-L3, содержит огромное количество заготовок – деталей космических кораблей всех форм и фасонов. Корабли там делаются за считанные минуты – потому у меня не было особых проблем быстро соорудить с десяток различных моделей, уделив особое внимание хорошо читаемым силуэтам. Внешний вид кораблей подгонялся под их геймплейные задачи – хрупкие и быстрые корабли вытянуты и заострены, прочные и медленные – широкие и утолщённые.
С геометрией проблем не возникло – но вопрос текстурирования встал во весь рост. Мой обычный workflow для моделей предполагает создание всего пакета текстур в Substance Painter. Но (в моих кривых руках) такой процесс занимает вплоть до нескольких часов для каждой модели. Столько времени у меня точно не было.
Плюс, непонятно было, как перекрашивать корабль в зависимости от принадлежности к разным фракциям.
Потому в дело пошла стилизация и намеренный отказ от реализма. Решив назначить моделям одноцветный материал “под пластик”, я мгновенно получил довольно приятное визуально и невероятно быстрое решение. Корабли стали смотреться как фишки настольной игры – очевидно упрощённые, и потому вызывающие желание потрогать их. Оставалось лишь слегка поработать со светом, добавив на каждую из игровых карт рисующий источник сверху, и контровой снизу.
Что интересно, код игры предусматривал для каждого из кораблей свои эффекты выстрела и взрыва. Однако, в начале разработки были созданы эталонные эффекты “плазменного луча” и “взрыва вдребезги”, которые назначались по умолчанию всем новым юнитам. Планировалось придать каждому из юнитов уникальные эффекты – но когда работа уже шла к релизу, выяснилось, что ни один из тестеров не обратил внимания, что абсолютно все корабли стреляют и взрываются одинаково. Решение “нехай остаётся так” удачно вписалось в стиль игры. По тому же принципу была выполнена озвучка – игре удалось обеспечить мощный OST, а отсутствия звуков почти никто и не заметил 🙂
Игровой баланс
Правильному балансу юнитов всегда требуется уделять особое внимание в стратегиях. Я использовал для балансировки “метод Command and Conquer”. В старом-добром C&C каждый юнит мало того что имел своё предназначение – его боевая ценность была напрямую подвязана к стоимости. Три условных танка по 500$ наносили и выдерживали до момента гибели столько же урона, как один супер-танк за 1500$, или как 15 пехотинцев по 100$. Все характеристики юнита складывались геймдизайнером в общий показатель ценности, которая напрямую пересчитывается в стоимость.
В моей игре у юнита есть четыре основные характеристики:
- Прочность
- Сила атаки
- Дальность атаки
- Скорость движения
Изначально, каждый из кораблей должен был иметь способность – кто-то чинил соратников, кто-то мог выдать двойную скорость на форсаже, кто-то на ход блокировал весь входящий урон. Под способность даже есть особое место в интерфейсе свойств корабля. Правда, её я сделать критически не успевал, и пошёл другим путём. Тем не менее, каждый корабль в релизе имеет в описании “Ability: None” – и никто не заметил 🙂
Впрочем, четыре шкалы “прочность–сила–дальность–скорость” сами по себе оказались довольно удобны для создания специализированных юнитов. Все корабли умозрительно были поделены мной на два класса – лёгкие и тяжёлые (а-ля пехота и техника в классических RTS).
Каждый из кораблей в своём классе имеет чётко определённую тактическую роль:
- К примеру, из лёгких кораблей игроку почти с самого начала игры доступен бомбардировщик – непрочный, медленный, зато с атакой, способной уничтожать некоторые тяжёлые корабли с одного выстрела.
- Противостоять ему может чуть более дешёвый штурмовик – не такой мощный, зато куда как более крепкий и с дальнобойной атакой.
- А чуть дороже бомбардировщика уже можно купить шустрый корвет, убийцу лёгких кораблей – его броня выдержит больше одного попадания штурмовика, а вот его выстрел испаряет любой лёгкий корабль.
- Но пушки корвета не так уж хороши против брони фрегата – который сжигает корвет на месте одним выстрелом…
- …правда, этот тяжёлый корабль уже стоит как два штурмовика, и сносится попаданием артиллерийского крейсера – дальнобойного, чудовищно мощного, но едва-едва бронированного…
- …и легко перехватываемого парой бомбардировщиков 🙂
Словом, каждый корабль хорош для своей цели, уязвим перед одними и хорошо бьёт других – и всё это достигнуто за счёт умелой вариации параметров. На протяжении игры этот баланс заставляет игрока вертеться, выбирая нужные виды кораблей для покупки.
Стоимость кораблей была сбалансирована по совокупности параметров. Правда, дело осложняется тем, что в нашем случае ценность двух более слабых юнитов чуть меньше, чем одного сильного – ведь два юнита требуют двух ходов на перемещение. Потому цена на мощные или более полезные юниты дополнительно чуть задирается.
Я по праву горжусь достигнутым игровым балансом – бесполезных и имбалансных кораблей по завершению полировки попросту не осталось, за счёт чего игра создаёт острые боевые ситуации и не даёт заскучать до самого финала.
Искусственный интеллект
Местный AI стал моим opus magnum. Не все из описанных ниже элементов вошли в финальную версию, но реализация оказалась гибкой, жизнеспособной и в целом удачной. Здесь я впервые применил (опять-таки подсмотренную у разработчиков C&C) методику “целевых групп”. Целевой группой называется объединение юнитов, сформированное AI для выполнение некоторой задачи. В игре AI оперирует всего тремя задачами, между которыми переключается по мере роста их приоритета:
- Наиболее высоким приоритетом обычно пользуется задача захвата вышек, и её приоритет растёт, когда AI теряет контроль над своими вышками.
- Менее высокий приоритет получает оборона собственных вышек, и её приоритет растёт по мере появления войск игрока вблизи контролируемых вышек.
- Самый низкий приоритет традиционно у задачи свободного поиска – когда войска AI просто охотятся за произвольными юнитами игрока.
Игровой AI, выбрав задачу, определяет точку удара – вышку или юнита, в направлении которого пошлёт свои силы. После этого он формирует для выполнения задачи целевую группу. Для этого AI произвольно компонует несколько групп из двух-четырёх юнитов, ближе всего находящихся от точки удара. Каждая группа получает оценку, формируемую на основании:
- непосредственно суммарной огневой мощи юнитов;
- живучести юнитов;
- близости юнитов группы друг от друга (чем дальше, тем меньше);
- близости юнитов от точки удара (чем дальше, тем меньше).
В итоге, из всех групп выбирается та, для которой значение оценки выше, чем у прочих. Если при этом даже у лучшей группы оценка меньше некоторого порогового значения (определяемого огневой мощью ближних юнитов игрока) – то на ближайшей контролируемой вышке будет докуплен произвольный юнит.
Когда целевая группа, наконец, встречает войска противника – начинается бой. Для каждого из юнитов группы проводится случайная проверка – атаковать, двигаться или пропустить ход?
- Атакующий юнит просканирует всех врагов в зоне своей досягаемости – и выберет юнит, огонь по которому будет предельно болезненным для игрока. Это будет юнит с самой высокой стоимостью, который обладает самым низким здоровьем. То есть враг будет ранить, а потом целенаправленно добивать ценные корабли игрока. Также в ряд версий попадала логика сохранения собственного здоровья – атакующий корабль AI предпочитал открывать огонь по юниту, которого может уничтожить одним выстрелом, не нарвавшись на ответный огонь.
- Движущийся юнит перемещается по направлению к точке удара, игнорируя всё на своём пути.
- Пропускающий ход заставит AI заказать на ближайшей вышке ещё один корабль.
В итоге, AI создаёт впечатление агрессивного и в целом рационального оппонента. Он выбивает опасные цели в первую очередь, координирует атаки на вышки игрока, не перестаёт клепать подкрепления и активно реагирует на изменение обстановки. Паттерны его поведения напрямую не прослеживаются, и хотя он в некоторых ситуациях своей неуклюжестью производит впечатление “учёного идиота” – но он оказывает бурное и деятельное сопротивление. Игроки отметили, что с ним интересно сражаться.
Итак, я сформировал первую целевую группу из 4 крейсеров (назовем их A, B, C и D). Большие и резкие корабли, похожие на хищную птицу.
Их задача была проста – «осмотритесь, и если у одного из вас есть противник в пределах досягаемости – идите и стреляйте в него».
И они очень хорошо выполнили свою задачу.
Когда противник подошел к крейсеру А… трое его соратников повернулись к бедному А, прогревая свои лазеры, и за секунду обратили его в межзвёздную пыль!
Я был шокирован. Конечно, проблема была в определении «он» – код был написан так, что речь шла не о приблизившемся «враге», а об обнаружившем его «одном из вас».
Упс 🙁
Проблемы разработки
Когда делаешь что-то в большой спешке – мозг начинает уставать. Некоторые решения принимались в панической попытке успеть к сроку. К примеру, катсцены изобиловали хардкодом с подобранными “на глаз” цифрами временных интервалов – так что не исключено, что на некоторых компьютерах эффекты играются не там, не так и не тогда, как этого надо было бы ожидать. Также, код вышел не слишком чистым… да что там – в конце разработки я обнаружил три почти одинаковых скрипта для линейного перемещения объектов, отличающихся только вбитыми коэффициентами. Все три использовались. Срочность не способствует ясному мышлению 🙂
Неожиданный затык отчего-то возник при реализации простейших вещей – таких, как определение зон перемещения и стрельбы. В теории всё просто – корабль может стрелять или ходить на все клетки в определённом радиусе R от себя. Так давайте на дистанцию R во все стороны от корабля проверим клетки – входит ли их центр внутри окружности? Если да – отметим клетку, нет – значит нет.
Просто же? Тем не менее, созданный алгоритм регулярно глючил, добавлял лишние клетки или не строил нужных. Мозг, истощённый ударной работой, заклинило – и какая-то очень простая вещь ускользала от внимания.
Итоговое решение было ужасным – для каждого из значений радиуса стрельбы был создан свой массив размером R x R, который описывал форму зоны стрельбы! Мне было очень стыдно, но решение мгновенно заработало 🙂
Иногда игра попадала в состояние софтлока – игровой процесс зависал на середине хода компьютера. Компьютер считал, что его ходы уже кончились – но при этом процесс передачи хода отчего-то не инициировался. До самого релиза я так и не смог отловить источник проблемы, потому использовал другое “варварское” решение – сторожевой таймер. На ход компьютера было наложено ограничение – порядка 30 секунд, после чего управление у него насильно отнималось и передавалось игроку. Думаю, игроки это замечали – однако, сомневаюсь, что кто-нибудь всерьёз расстроился оттого, что компьютер пропустил два своих действия 🙂
Цейтнот и гамбит
Несмотря на все усилия, я не успевал. Диалоги были написаны где-то в редкие минуты покоя, и хотя сюжет планировался ещё одной фичей, которая вытянет игру – но я критически не успевал реализовать все миссии. Под нож пошли способности, эффекты, звуки – и это было не обидно. Обидно было, что выброшена была настоящая концовка игры – последнее и грандиозное сражение “на два фронта”, раскрывавшее истинную природу войны после довольно неприятного для героев твиста.
В итоге, многие игроки задавали вопрос – зачем в игре столько текста, “диалоговщины”, каких-то “зацепок” – если это ни на что толком не повлияло? Финал пришлось резко оборвать, сделав открытым – и все сцены раскрытия характеров героев (вместе с половиной сюжета) стали эталонным “ружьём Бондарчука” в вакууме. Обидно, обидно.
Впрочем, самая крупная проблема наступила тогда, когда игра вышла в релиз. Я успел-таки опубликовать игру к намеченному времени – до завершения конкурса оставалось ещё 3 часа, а игру уже можно было запустить (и даже немного поиграть). Но потом…
Потом игра висла, крашилась, вылетала, глючила и вообще всячески обижала игрока.
Проблем было не перечесть. Уровни были недоделаны – местами пройти их было почти невозможно. AI иногда с маниакальным упорством начинал преследовать отдельный кораблик, не обращая ни на что вокруг внимания. Управляемые компьютером корабли летели в странные места или вовсе улетали куда-то за пределы карты и отказывались возвращаться. Компьютер иногда игнорировал дистанцию – и стрелял по игроку через пол-карты или заказывал корабли в произвольные места карты. Огромная сетка предпоследнего уровня перегружала любую видеокарту – на ней не был включён instancing. Рипперы (“бешеные табуретки” со зверской атакой и картонной бронёй, делающей их почти одноразовыми камикадзе) внезапно превратились в форменную кару – им не был прописан эффект взрыва, и оттого они нагло отказывались погибать. Впрочем, другие корабли тоже иногда решали ещё немного повоевать после смерти – если AI получал управление над кораблём до окончания анимации взрыва, то последовательность уничтожения прекращалась и корабль… эээ… забывал умереть. По карте летали корабли-призраки. Жуть. А последний уровень вообще загибал неподготовленного игрока буквой “Z” и ломал его об колено – сложность была нечеловеческой. Не было ни одного игрока, которому бы удалось сразу после релиза пройти игру.
Потому следующая неделя адского, нечеловеческого кранча была посвящена экстренному залатыванию дыр, подстройке расстроенного, наладке разладившегося, всяческим ремонтам и мелким доработкам. Это был тот самый случай, когда “доработка напильником превращает пароход в самолёт”.
Впрочем, так и было задумано.
Посмотрите снова на план действий – пункт “тестирование” в нём отсутствовал. Времени на него хватало при любом раскладе, и это было сделано специально. Я провернул гамбит имени Акеллы – правила Ludum Dare предусматривали остановку добавления в игру новых фич по истечении трёх дней… но вот патчить игру не возбранялось 🙂
В итоге, я исправил все критические ошибки, опубликовав Gold-версию, включающую в себя полностью стабильный релиз, а также дополнительные бонусные материалы. Однако, будучи полностью связан по рукам и ногам обязательствами по срочной доработке, я не имел возможности в эту неделю пиарить игру. Все писали новости – а я патчил. Все набирали рейтинг – а я патчил. Все дружили и занимались круглосуточной раскруткой – а я патчил. Активные и деятельные разработчики пробивались в обзоры – а я патчил.
Время после релиза оказалось для хакатона не менее важным, чем время до релиза – но этот аспект я узнал тогда, когда менять планы было уже поздно. Игроки, тем не менее, давали игре второй шанс – и проект в итоге заслужил ряд достаточно лестных оценок. Я завёл ряд знакомств с разработчиками по всему миру, получил предложения доработать проект для Steam, и хотя игра не завоевала призовых мест – но я уложился в срок, выпустив готовый продукт. И доставил людям сотни приятных минут, дав познакомиться с новым, необычным и красочным миром Lost Flagship – к которому, возможно, ещё вернусь 🙂
Где поиграть?
Игру можно скачать с моего сайта:
Скачать “Lost Spaceship [Gold Edition]”
LS_Gold.rar – Загружено 40 раз – 105,29 МБ