Цель статьи - перенести персонажа из Makehuman в Unity3d так, чтобы его одежду можно было снимать и надевать прямо во время игры.

Для этого нам понадобится Makehuman, Blender3d, Unity3d и его плагин UMA (они все бесплатные). Статья написана по мотивам этого ютуб канала, повторяя за автором я набил много шишек, и теперь готов изложить своё видение.

Этап 1: Создание персонажа в Makehuman

Это самый легкий и приятный этап, тут необходимо смоделировать персонажа. Makehuman довольно простая программа, поэтому не буду описывать, как в ней работать.

Но есть три важных момента. Во-первых: нужно добавить скелет к модели (добавляется во вкладке Pose/Animate). В этом туториале я использую риг Game Engine. Во-вторых, если на персонаже есть одежда, то нужно отключить опцию удаления полигонов под одеждой (это можно сделать во вкладке Geometris -> Clothes, и снять галочку Hide faces under clothes). В-третьих, нужно экспортировать в fbx и указать единицы метры. Так же, на всякий случай, можно сохранить модель.

Несколько дополнительных моментов

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

Единицы измерения метры, значит что десять клеточек в makehuman будут означать условный метр

Этап 2: Обработка его в Blender3d

Тут нам нужно сделать три вещи:

  • Нормализовать модель

  • Добавить глобальную кость

  • Нарезать части тела

Запускаем Blender3d. Для начала нам нужно удалить свет, камеру и куб и импортировать нашу модель. Затем импортируем нашу модель: File -> Import -> fbx (и выбираем наш fbx файл)

Грабля на которую я наступил

Первый раз когда, потратил время на создание персонажа makehuman, в блендере нажал на экспорт вместо импорта (в результате в мой fbx файл записалась пустая сцена, и пришлось заново создавать персонажа). Тоже самое касается импорта.

Нормализация

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

Очищаем трансформацию позы

Переходим в блендере в режим позы

Переход в режим позы
Переход в режим позы

Выделяем всё, (клавиша a)

Затем нажимаем pose ->clear transform -> all

Очистка трансформации
Очистка трансформации

Чтобы проверить, что трансформации очищены, можно перейти в режим редактирования. Модель не должна при этом измениться.

После этого персонаж развернулся на 90 градусов, нужно вернуть его обратно

Вращение персонажа в блендер

Сначала нужно перейти в объектный режим.

Переход в объектный режим
Переход в объектный режим

Напоминаю что мышь должна находиться на экране с моделькой. Далее нужно нажать R для перехода в режим вращения, затем X для того чтобы вращение было только по этой оси. Затем на клавиатуре набрать -90 и нажать Enter

И наконец, нужно очистить внутреннее масштабирование и вращение всех мешей.

Очистка масштабирования и вращения

Если мы, например, создадим два куба и первый сожмем в объектном режиме, а второй в режиме редактирования, то они сожмутся по разному. На панели трансформации в первом случае scale по всем осям будет 0.500, во втором 1.000. Это значит, что у первого куба меняется атрибут scale, а сам куб, как бы не меняется. То же самое с вращением.

Для модели нашего персонажа нам нужно добиться чтобы все его меши имели вращение 0° и масштабирование 1.000 (без изменения формы самих мешей естественно).
Для этого нужно, в объектном режиме выделять каждый меш (включая арматуру) нажимать на ctrl + a, и выбирать пункт rotation & scale.

Добавление глобальной кости

Теперь нам нужно добиться, чтобы скелет был совместим с UMA. Для этого нужно добиться такой иерархии костей.

Global (Head= 0,0,0 Tail=0,0,0.1) // новая кость
  Position (Head= 0,0,0 Tail=0,0,0.1) // бывшая  Root кость
    весь старый скелет (кость pelvis)
Пошаговая инструкция как этого добиться

Выделим Root кость и переименуем её в Position. Затем выставляем у неё характеристики Transform так же как на картинке.

Далее выделяем в арматуру (у меня это Game_engine) и переходим в режим редактирования. Далее нажимаем на add -> single bone

У нас появится кость с именем bone, и её нужно переименовать в Global и выставить в ней те же характеристики, что и для кости Position.

Далее нужно сделать Global кость предком кости Position. Для этого нужно сначала свернуть список дочерних костей у кости Position.

Далее выделить кость Global, затем зажав shift, выделить кость Position. Перевести мышь на экран с 3d моделью и нажать на ctrl + p, в выпадающем меню выбрать keep offset.

Нарезка персонажа

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

В этом туториале мы разобъём персонажа на следующие слоты

  • Голова

  • Торс

  • Ноги (без ступней)

  • Ступни

  • Глаза

  • Волосы

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

Создание seams mesh

Выделяем главную сетку (в моём случае она выглядит вот так). Прочие сетки (глаза, волосы и одежду) я скрыл.

seams mesh
seams mesh

Нажимаем shift + d, затем RMB

В результате она должна продублироваться.

И называем её seams mesh (либо любым другим названием).

Теперь осталось добиться того, чтобы вместо старого меша были отдельные части.

Нарезка персонажа

Переходим в режим редактирования нашей главной сетки (не seams mesh).
В подрежим редактирование поверхностей.

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

UPD: раньше выделял голову при помощи кругового выделения (клавиша c) и рентгеновского вида, но пользователь ValeryIvanov подсказал более удобный способ:

Нужно скрыть это кольцо (клавиша h), далее навести курсор на голову и нажимаем L (должна выделиться вся голова), затем снова показываем полосу на шеи, нажав на Alt + L. Далее нажимаем на P и в выпадающем меню выбираем SПelection. Всё, голова отделена.

Скрываем полоску на шеи (клавиша H)
Скрываем полоску на шеи (клавиша H)
Нажимаем на L чтобы выделить голову (курсор должен быть на голове)
Нажимаем на L чтобы выделить голову (курсор должен быть на голове)
Покзываем полоску (Alt + L) и отделяем выделенное (P -> Selection)
Покзываем полоску (Alt + L) и отделяем выделенное (P -> Selection)

Не забудем переименовать получившийся меш.

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

Ещё я нарезал одежду и переименовал части тела.

Таким образом, мы имеем seams mesh и множество мешей если собрать которые получиться модель персонажа. Каждый с одинаковой арматурой.

Теперь экспортируем персонажа в формате fbx (желательно в ту же папку куда экспортировали из makehuman потому что там должны лежать текстуры к нему)

Этап 3: Создание UMA расы

Подготовка сцены

Открываем unity, скачиваем вот этот ассет и импортируем его в unity. Это ничто иное как UMA - Unity Multipurpose Avatar, фреймворк который позволяет кастомизировать персонажей. Перетаскиваем префаб UMA/Getting Started/UMA_GLIB в иерархию.

Импорт персонажа

Создаём папку в юнити в которой будем работать, я назвал её characters. Перетаскиваем туда папку с нашим персонажем (fbx и текстуры). Возможно появиться сообщение о том, что некоторые текстуры не помечены как карты нормалей. Можно нажать fix now.

Для удобства при работе с UMA я закидываю всё в одну папку (в characters), потому что при создании и настройки ассетов постоянно приходиться указывать в них ссылки на друг друга. А каждый раз лазить по папкам не всегда удобно. В любом случае, после того как мы всё настроем можно будет навести порядок.

Выделяем в менеджере проекта нашу fbx модельку, в инспекторе снимаем галочку Convert Units, и нажимаем Apply. Далее переходим во вкладку rig в animation type, выбираем Humanoid и так же нажимаем Apply.

Теперь нажимаем в верхнем меню UMA -> Extract T-Pose (в менеджере проекта наша fbx модель должна быть выделена). В результате должна появиться папка TPoses с экспортированной т-позой.

Скриншоты
Импорт персонажа
Импорт персонажа
Генерируем риг
Генерируем риг
Экспортируем T-Pose
Экспортируем T-Pose
Для удобства перетаскиваем t-pose в рабочую папку
Для удобства перетаскиваем t-pose в рабочую папку

Создание слотов и оверлеев

В верхнем меню выбираем UMA -> Slot Builder, появившийся окно я перетаскиваю рядом со вкладкой иерархии. Нажимаем на стрелочку рядом с fbx моделью и находим там часть seams mesh и перетаскиваем её в поле seams mesh в Slot Builder. В UMAMaterial выбираем UMA_defuse (для простоты). В slot destination folder переместите папку куда будут генерироваться слоты.

Теперь перетаскиваем части которые должны выглядеть единым мешем в панель automatic Drag and Drop porcessig. Это голова, торс, ноги и ступни (но не глаза и волосы). После того как закончили, в seams mesh выберите None и подобным образом перетащите глаза, волосы и одежду.

Скриншоты
Открываем Slot Builder
Открываем Slot Builder
Помещам его рядом с иерархией
Помещам его рядом с иерархией
Нажимаем на треугольник, чтобы развернуть модель
Нажимаем на треугольник, чтобы развернуть модель
Перетаскивам сетку швов
Перетаскивам сетку швов
Выбераем материал
Выбераем материал
Указываем папку где должны появиться слоты
Указываем папку где должны появиться слоты
Через ctrl выделяем части, которые связаны между собой
Через ctrl выделяем части, которые связаны между собой
Переносим их на панель "automatic Drag and Drop porcessig"
Переносим их на панель "automatic Drag and Drop porcessig"
Устанавливаем Seams Mesh в None
Устанавливаем Seams Mesh в None
Аналогично переносим части которые могут сниматься/надеваться
Аналогично переносим части которые могут сниматься/надеваться
Переименуем все слоты (как файлы, так и имена в инспекторе) и перенесем их в рабочую папку
Переименуем все слоты (как файлы, так и имена в инспекторе) и перенесем их в рабочую папку

Теперь нужно создать оверлеи. Оверлей это по сути текстура, созданная специально для UMA, которую можно комбинировать с другими такими текстурами, используется например для добавления шрамов, татуировок, макияжа, рисунка на щите (ещё можно добиться чтобы цвет этого рисунка на щите менялся программно). Итак, правый клик мыши в менеджере проекта, Create -> UMA -> Core -> Overlay Asset. В Overlay name впишите имя оверлея (например skin), в material выберете тот материал который указывали при создание слотов (в нашем случае это UMA_defuse) в спойлере количество каналов укажите 1, и перетащите в появившиеся поле _MainTex соответствующую текстуру. Ту же операцию проделайте для других текстур.

В верхнем меню нажмите UMA -> Global Library, и перетащите в левую панельку папку в которой мы работали. Это добавить в глобальный индекс все созданые нами вещи. Ничего страшного если мы не сделаем этот шаг, в таком случае, пердложения добавить расу/слот/оверлей в глобальный индекс, будут показаны при настройке расы и базового рецепта.

Скриншоты
Создаем оверлей (рекоменую в папке с текстурами)
Создаем оверлей (рекоменую в папке с текстурами)
Переименовываем его и задеём имя
Переименовываем его и задеём имя
Выбираем тот же материал, что указали при создани слотов
Выбираем тот же материал, что указали при создани слотов
Указываем количество текстурных каналов 1, и перенести текстуру
Указываем количество текстурных каналов 1, и перенести текстуру

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

Результат
Результат
Откройте Global Library
Откройте Global Library
Перенесите рабочую папку в панель "Drag indexable assets here to Add them to the index"
Перенесите рабочую папку в панель "Drag indexable assets here to Add them to the index"

Создание TextRecipe и RaceData

Снова правый клик мыши в менеджере проекта Create -> UMA -> Core -> Race Data, затем Create -> UMA -> Core -> Text Recipe. Первый файл - это файл расы. Второй - это базовый набор слотов/оверлеев для неё. Придумайте название для них и переименуйте файлы соответствующим образом. Выделите расу и, сначала, в поле Base Race Recipe перетащите рецепт который мы только что создали. В поле Race Name введите её название, в TPose перетащите т-позу которую мы получили ранее.

Теперь нужно назначить Wardrobe Slots, это что-то типа ячейки инвентаря, куда можно надеть шмотку. В них помещается Wardrobe Recipe. Wardrobe Recipe по сути это сама шмотка. В чем же его отличие от обычный слотов? Слот это сменяемая одна из частей модели, вардроп рецепт это информация о том как этот слот (шмотка) будет надеваться на тело. Например если есть шляпа, то слот шляпы будет содержать меш шляпы, (и информацию о том, оверлеи с каким материалом с ним совместимы). А Wardrobe Recipe будет содержать ссылку на этот слот, нужные оверлеи, и информацию о том что нужно убрать волосы (и другую шляпу если она есть), и ещё скрыть часть головы, если её части проходят сквозь шляпу. Поэтому в Wardrobe Slots можно создать примерно вот такой список:

  • None

  • Hair

  • Head

  • Body

  • Legs

  • Feet

Теперь выделите файл с BaseRecipe, переключитесь на вкладку Slots. В поле Race Data перетащите наш файл расы (да, теперь файл расы и базовый рецепт ссылаются друг на друга). Теперь нужно перетащить слоты тела на соответствующую панель (слоты одежды не обязательно перетаскивать). В результате должен получиться список из слотов. Теперь для каждого слота нужно перетащить соответствующий оверлей. В данном случае для всех частей тела используется один и тот же оверлей, и после перетаскивания он будет отображаться в панели shared overlays.

Скриншоты
Создаем Race Data и Text Recipe
Создаем Race Data и Text Recipe

Я назвал их MaleRace и BaseRecipe соответственно

Перемещаем BaseRecipe
Перемещаем BaseRecipe
Перенеситье т-позу, и введите название расы
Перенеситье т-позу, и введите название расы
Задаём Wardrobe Slots
Задаём Wardrobe Slots
Укажите ссылку на расу (и, при необходимости, добавте её в глобальную библиотеку)
Укажите ссылку на расу (и, при необходимости, добавте её в глобальную библиотеку)
Переносим слоты тела
Переносим слоты тела
Если не добавляли слоты в индекс, нажмите на "Add to Global Index (Recommended)".
Если не добавляли слоты в индекс, нажмите на "Add to Global Index (Recommended)".
Перенесём соответвующие оверлеи (и, если попросят, также добавим их в глобальную библиотеку)
Перенесём соответвующие оверлеи (и, если попросят, также добавим их в глобальную библиотеку)
В резуьлтате должно получить вот так
В резуьлтате должно получить вот так

Добавление персонажа на сцену

Всё что мы до этого делали было подготовкой к этому шагу. Настраиваем сцену (например добавляем плоскость, играющую роль земли). Перетаскиваем префаб UMA/Getting Started/UMADynamicCharacterAvatar в сцену (можно переименовать его). В инспекторе в компоненте Dynamic Character Avatar в поле Active Race выбираем нашу расу.

Добавляем анимацию

У меня по умолчанию не запустилась анимация (для базовых рас тоже, поэтому подозреваю что это недоработка). Глубоко этот вопрос пока не изучил, но сумел добиться чтобы она воспроизводилась. Для этого под спойлером Race Animation Controllers в списке Race Animators удаляем все элементы и добавляем новый, в Race указываем нашу расу, в Animator выбираем IdleTest-w-head.

Нажимаем на Play. У вас должен появиться персонаж которого мы собрали.

Скриншоты
Создаем пол
Создаем пол
Переносим префаб UMA/Getting Started/UMADynamicCharacterAvatar на сцену
Переносим префаб UMA/Getting Started/UMADynamicCharacterAvatar на сцену
В поле Active Race выбираем нашу расу, лысый мужик должен смениться нашей моделью
В поле Active Race выбираем нашу расу, лысый мужик должен смениться нашей моделью
Удаляем старые аниматоры
Удаляем старые аниматоры
Нажимаем на кнопку Add Race Animator
Нажимаем на кнопку Add Race Animator
Указываем нашу расу в поле Race
Указываем нашу расу в поле Race
Указываем анимтор IdleTest-w-head
Указываем анимтор IdleTest-w-head
Запустив игру, мы увидим нашу фигуру
Запустив игру, мы увидим нашу фигуру

Добавление одежды

Теперь создадим одежду Create -> UMA -> DCS -> Waredrop Recipe, выделяем созданный ассет назначаем ему расу, задаём имя и назначаем Wardrobe Slot . Затем по аналогии с созданием базового рецепта, перетаскиваем на панель слот и оверлей той шмотки которую мы ходим создать. Затем перетаскиваем этот ассет в глобальную библиотеку, так же как мы делали ранее. Проделываем эту операцию для других шмоток и волос. Теперь выделяем нашего персонажа в иерархии, в спойлере Customization -> Default Recipes перетаскиваем созданные вардроп рецепты на панельку.

Если тело проходит сквозь одежду, то нужно создать сетку сокрытия. Create -> UMA -> Misc -> Mesh Hide Asset, указываем слот с частью тела, который нужно частично скрыть, нажимаем на кнопку Begin Editing и в редакторе выделяем те грани, которые будут скрываться при надевании этой шмотки. Далее в Waredrop Recipe нажимаем на кнопку Add Mesh Hide Asset, и выбираем сетку сокрытия.

Теперь можно надевать и снимать эту вещь. Для этого выбираем наш Dynamic Character Avatar, и в спойлере Customisation -> Default Waredrop Recipes перетаскиваем на панельку наш вардроп рецепт.

Скриншоты
Создаем wardrobe recipe
Создаем wardrobe recipe
Переименовываем его, и указываем Display Value
Переименовываем его, и указываем Display Value
Указываем wardrobe slot в котрой будет надета шмотка
Указываем wardrobe slot в котрой будет надета шмотка
По аналоги с созданем TextRecipe добавлем слот...
По аналоги с созданем TextRecipe добавлем слот...
...и оверлей (не забываем добавить их в глобальный индекс)
...и оверлей (не забываем добавить их в глобальный индекс)
Запускаем игру и петаскиваем сюда наш рецепт
Запускаем игру и петаскиваем сюда наш рецепт

На всякий случай скажу что если надеть их не в игре, а в режиме редактирования, то их не нужно будет каждый раз заново надевать.

Сетка тела, может в некторых местах проступать через одежду. Есть два варанта как поступить: назначить в wardobe recipe слот (слоты) которые будут скрываться при применение этого рецепта. Или можно добавить сетку сокрытия.

В случае на скриншоте можно просто скрывать весь слот

Указываем вардроп-слот, связанные с котором слоты должны скрыться.
Указываем вардроп-слот, связанные с котором слоты должны скрыться.

Но если одежда покрывает не весь слот (то есть скрыть слот не варант) то на помощь придет сетка сокрытия.

Создаём сетку сокрытия
Создаём сетку сокрытия
Выбираем расу
Выбираем расу
Выбираем слот, и нажимает Begin Editing
Выбираем слот, и нажимает Begin Editing

Альтернативный способ, это сразу перенести слот части тела, которую нужно скрывать этой сеткой сокрытия, в поле Slot Data Asset, в моем случае это ноги (не шатаны).

Выделяем те части слота, которорые будем скрывать. Завершив, нажимаем на "Save & Return"
Выделяем те части слота, которорые будем скрывать. Завершив, нажимаем на "Save & Return"
И в нашем вардро рецепте назначам нашу сетку сокрытия
И в нашем вардро рецепте назначам нашу сетку сокрытия
Запускаем игру
Запускаем игру

Запустив игру и надев нужную деатль одежды (в нашем слчае штаны), мы теперь увидим что они не просвечивают.

Добавление и удаление одежды программно

Для этого нужно использовать методы компонента DynamicCharacterAvatar. Для добавления SetSlot и ClearSlot для удаления, после этого нужно вызвать метод BuildCharacter. Первый аргумент SetSlot это имя слота куда необходимо надеть шмотку. Полный список доступных слотов можно посмотреть в файле расы. Второй аргумент это имя файла нашего Waredrop Recipe. В ClearSlot нужно использовать только имя слота.

Первый аргумент в SetSlot излишен (имхо)

На мой взгляд, информация о том куда мы надеваем шмотку излишняя. По сути, значение первого аргумента SetSlot всегда совпадает с полем Waredrop Slot соответствующего Waredrop Recipe.

Вот пример скрипта, вам остается куда куда-нибудь повесить методы AddWardrobe и RemoveWardrobe.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UMA;
using UMA.CharacterSystem;

public class Program : MonoBehaviour
{
	// сюда нужно перетащить наш Dynamic Character Avatar
	public GameObject DCA;
	private DynamicCharacterAvatar avatar;
	
	void Start(){
		avatar = DCA.GetComponent<DynamicCharacterAvatar>();
	}

	public void AddWardrobe(string wardrobeSlot, string wardrobeRecipe){
		avatar.SetSlot(wardrobeSlot,wardrobeRecipe);
		avatar.BuildCharacter();
	}
	
	public void RemoveWardrobe(string wardrobeSlot){
		avatar.ClearSlot(wardrobeSlot);
		avatar.BuildCharacter();
	}
}

Заключение

Спасибо что дочитали до конца, надеюсь, эта статья станет для вас ещё одним шагом к созданию своей игры мечты.