Разработка ремейка методами чайника. Часть 3

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

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

Должен сказать что код выглядит весьма запутанно, и очевидные простые вещи решаются неочевидным образом. Например вот этот код просто получает из локатора ropeb1 > ropee1:

if fnmatch.fnmatchcase(obj.name, "ropeb*"): rig_type = 'rope' rope_start_name = obj.name print ('Rope start name:', rope_start_name) rope_num = rope_start_name.split('b', 1)[1] print('Rope number:', rope_num) rope_end_name = rig_type + 'e' + rope_num

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

Я решил начать с чего попроще, и мой выбор пал на генерацию тросов, ведь её логика не составляет проблем:
От ropeb1 трос должен идти к ropee1, ropeb2 > ropee2, … ropeb* > ropee*.
От falb1 к fale1, falb2 > fale2, … falb* > fale*.
Тут важно отметить, что эти локаторы как правило соединяют разные части корабля, что гипотетически может добавить проблем при генерации на UE.

Разобравшись с теорией, пора приступать к практике! Но лучше всего начать разработку с какого-нибудь маленького корабля. Так что импортируем тартану со всеми частями в Blender с помощью плагина.

Разработка ремейка методами чайника. Часть 3

И вот тут встал вопрос: а каким образом импортировать в UE локаторы? В Blender-е они представляют собой загадочную сущность Empty, соответственно я пошел гуглить вопрос по типу: “unreal engine import blender empty”. И разумеется не нашел ничего мне подходящего…

Я долго рыл интернет, но безрезультатно. И тогда проснулся он… Костыльщик… и придумал максимально абсурдную вещь: если мы не можем импортировать в движок неизвестную сущность, то просто превратим её в известную и импортируем! Таким образом, путем модификации скрипта импорта модели корабля, вместо Empty мы создаем маленькие сферы, которые затем без проблем импортируются в UE, а от игрока мы их просто скроем.

Надо ли говорить к какому бардаку это привело…?

Поняв что я зашел куда то не туда, я вернулся к гуглению, но теперь с другой стороны. Я решил узнать, можно ли в UE создать точку на модели и взаимодействовать с ней в процессе игры. И да! Имя ему - Socket! И теперь меняем наш вопрос гуглу на: “blender unreal engine socket”, и в первой же ссылке получаем ответ на вопрос! Модифицируем плагин чтобы все Empty начинали название с Socket_, и импортируем в движок.

На самом деле при импорте сокетов я столкнулся с еще одной проблемой. По какой-то причине сокеты имели в 100 раз больший размер, чем мне было необходимо. Путем долгих мучений, было выяснено что самый простой способ это поправить - уменьшить размер локаторов до 0.01 в Blender-е.

Разработка ремейка методами чайника. Часть 3

Наконец-то есть продвижение и модели помеченные сокетами!

Разработка ремейка методами чайника. Часть 3

Теперь возникает вопрос: а чем соединить нужные мне точки? Понятно что какой-то линией, но как эту линию сгенерировать? Как задать ей текстуру?
Иронично, но мне снова сходу не попался простой ответ. Я встречал даже вредные советы по типу использования дебаг-инструментов для отрисовки, которые на боевом билде разумеется не работают. Впрочем путем кропотливого гугления я вышел на видеоролик о Cable Component.

Что еще более прекрасно - он из коробки прикрепляется не только к объектам, но к сокетам этих объектов! В общем идеальное сочетание с моей задачей, можно приступать к работе.

Немного архитектуры Pawn-объекта корабля:

Разработка ремейка методами чайника. Часть 3

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

Что касается генерации тросов, я приведу поверхностный пример на Blueprint с небольшими комментариями:

Разработка ремейка методами чайника. Часть 3

А также код вращения мачт в зависимости от направления ветра(без учета направления корабля пока что):

Код, уверен, можно написать лучше через какую-нибудь геометрическую формулу, но я в этом не силен…
Код, уверен, можно написать лучше через какую-нибудь геометрическую формулу, но я в этом не силен…

Все готово, размещаем тартану на карте и тестируем что у нас получилось:

При смене направления ветра через дев-панель наблюдаются небольшие моргания, но это вина дев-панели :)

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

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

2828
8 комментариев

Очень крутая работа, чувак

2

Спасибо, но будет еще круче, поэтому придержи похвалу)

Интересно наблюдать за работой - пиши еще!

1

можно ведь содержать всё в акторе корабля (а не превращать это в смесь акторов)
– сделай мачты static mesh компонентами корабля

так логичнее получается, возможно на оптимизации в дальнейшем скажется

И получить условный супер-класс на сотни методов и переменных? Ну не знаю... что-то мне не очень нравится эта идея

1

ну или даже кастомный компонент написать, в зависимости от функционала мачт