Разработка ремейка методами чайника 6. Пушки и система повреждений

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

Единственное что, по мне сильно ударило то, что мой проект сравнили со Skull and Bones! Я целых 15 минут “кашлял кровью” от такого оскорбления!

Концепт

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

Что мне не нравится в системе оригинала, так это то, как ручная наводка реализована. Мне приходилось прямо в разгар сражения снижать скорость игры, переключаться в другой режим, доходить до середины палубы чтобы лучше чувствовать выбранное направление, затем тратить несколько секунд на подбор высоты… В общем это совершенно ломает динамику игры, и хотелось бы увидеть что-то похожее на систему наводки из BF(Black Flag).

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

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

Поиск решения для подбора траектории пушечного ядра

Сложно найти то, что ты не знаешь как называется. Даже если нужный тебе термин проскакивает в источниках которые ты изучаешь, далеко не факт что твой взгляд за него зацепится, и ты пойдешь гуглить информацию именно по нему. Так и получилось со мной, изучая информацию как бросить объект в определенную точку, я натыкался лишь на размытые мысли и слово “projectile”(ну а чего еще ожидать от размытого поискового запроса). И вот в определенный чудесный момент я вышел на это видео. Автор рассказывает как можно реализовать логику с предпросмотром траектории как в BF. Это конечно хорошо, но ручная наводка - лишь вспомогательная функция, да и AI должен будет наводиться автоматически. Поэтому нужно либо думать как переделать код на автоматический подбор вектора траектории, либо искать другое решение.

После изучения этого видео, получаем новую ветку для изучения - “projectile prediction”, и выходим в открытый интернет:

Я еще не говорил о том, что очень жалею, что плохо учился математике?
Я еще не говорил о том, что очень жалею, что плохо учился математике?

В общем, стало понятно, что с моими знаниями нужно искать реализацию непосредственно для UE, и я решил немного поиграться с новомодным ChatGPT. Указав что мне нужно, я попросил его расписать логику Blueprint-а. Как и ожидалось от передовой нейросети самого Илона, она дала мне ответ какие функции использовать и какие операции с векторами производить. Но вот только одна проблемка есть… то что она предложила - работало неправильно, у вектора получалась слишком большая сила, и шар улетал в соседнее село на сверхзвуковой скорости. Я разумеется просил её пофиксить эту проблему, но решения я так и не получил.

В общем покопавшись еще, я нашел статьи с ручным расчетом необходимого мне вектора, но мне было очень лень все это делать. Неужели в таком крутом движке нет нужной мне функции из коробки? Чисто из любопытства я ввел в поиске функций ключевое слово projectile, и нашел её: SuggestProjectileVelocity! Задаешь куда надо попасть, скорость снаряда, и она просто выдает тебе нужный вектор! Я решил посмотрел как её реализовывают другие разработчики, и вышел на видео, где еще и объясняют как сделать учет траектории движения цели. Отлично, можно приступать к написанию кода!

Реализация стрельбы из пушек

Тут все достаточно просто: пушки у нас помечены сокетами с префиксами “cannonl__”, “cannonr__” и т.д. Все что нам нужно, это их всех перебрать при инициализации, и распределить между группами переменных отвечающих за направление. Затем проверяем есть ли вражеские корабли в пределах зоны досягаемости, и если есть, то выбираем нужную группу переменных с пушками и делаем выстрел. Это все в общем-то неинтересно, интересно другое…

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

Лучший игровой движок ever!
Лучший игровой движок ever!

Пока что не навешивал таймеров перезарядки, и не добавлял визуальных эффектов вроде дыма и всплесков воды. Все визуальные эффекты будут покрыты специальным плагином, когда у него появится официальная интеграция с Oceanology(да и жабка за 170 бачей пока что поддушивает…).

Система повреждений

И так, у нас теперь есть стрельба, но нет никаких эффектов от попадания по кораблю. И если реализовать отметину от попадания на корпусе - не проблема, то вот как реализовать порванные от ядер паруса? Пока я разрабатывал паруса, я немного разобрался с логикой текстурирования материала, и способами добавления прозрачности на него. Оставался только вопрос, как нарисовать зону прозрачности в нужной точке паруса. В процессе поиска я вышел на функцию “Find collision UV”, которая возвращает позицию на текстуре, в которую было произведено попадание. Казалось бы, дальше дело техники, но… UE постучалась, и сказала что со Skeletal Mesh эта функция больше не работает “By Design”...

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

Загружаем в проект нашу модельку паруса, вносим минорные правки в код, чтобы вместо краски мы наносили прозрачность и дело в шляпе!

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

Но решив включить морф симуляции ветра, я почти полностью потерял возможность рисовать прозрачность на парусе. При чем только на местах деформации, статичные части паруса по прежнему нормально рисуются.

Решив протестировать аналогичную логику через WPO(World Position Offset) в текстуре, я такой проблемы уже не наблюдал, но возникло странное смещение относительно моего выстрела. Если пострелять по парусу сбоку, становиться очевидно - проблема в коллизии.

Проблемы с коллизией паруса

Внимание вопрос: как заставить коллизию учитывать деформацию через морф таргеты или WPO? Ответ: никак! На этом можно заканчивать разработку игры. Спасибо что читали эти бесполезные статьи!

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

Вот такая вот чесалочка получается в блендере
Вот такая вот чесалочка получается в блендере

Импортируем модель и анимации в UE, создаем физическую оболочку, и получаем ЭТО…

Видите парус? И я не вижу. А он есть!
Видите парус? И я не вижу. А он есть!

Почти 500 боксов с коллизиями роняют фпс до неадекватно низких значений. Ну может это только в редакторе так, и в игре проблем не будет? Разместив объект на карте, я действительно не заметил дропов фпс. До тех пор пока не выстрелил…

В общем стало очевидно что коллизию нужно делать условно на каждую 5-ю, если не 10-ю кость. Но через редактор UE это быстро не сделать под несколько сотен парусов… Возможно получится объединить морф таргеты с костями в одну анимацию, и тогда не будет такого перегруза. Но история об этом будет уже в следующей статье.

Мешаем анимации

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

Был большой вопрос: как написать Animation Blueprint так, чтобы он делал смешивание правильно, и у меня была легкая возможность менять анимации для разных парусов. И этим вопросом разумеется задавался не только я, и в ответах до 2021 года говорилось, что нельзя сделать абстракцию для разных анимаций. Но к счастью, в 5.0 для этого были созданы шаблоны. И с помощью переменных можно указывать анимации которые ты хочешь использовать. Вот тестовый пример:

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

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

6565
21 комментарий

Ну хоть кто-то решил доделать Skull n Bones...

4
Ответить

БАН

2
Ответить

Так может не гнаться за реализмом, а сделать несколько вариантов текстуры с различными степенями дырявости и между ними переключаться при достижении некоторого порога урона?

3
Ответить

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

1
Ответить

Чел, если ты таки сделаешь игру, то я буду первым покупателем.

До сих пор жду игру по пиратам на мотив Корсаров.

2
Ответить

Вода просто ахуй

1
Ответить

Продолжай в том же духе и не сдавайся , ты красавчик , спасибо за надежду свежего глотка воздуха в этот пиратский шедевр

1
Ответить