16 KiB
name, description
| name | description |
|---|---|
| fish-pipeline | Переносит рыбу из Blender в Unity с физическим скелетом, skinning и независимыми morph/shape-key анимациями. Использовать для FishGen, импорта GLB, ретопологии, настройки Rigidbody/ConfigurableJoint, BlendShape loop, исправления надувания и чрезмерных morph-деформаций. Адаптируется к модели и версиям API и обновляет собственные инструкции только после проверенных результатов. |
Fish Pipeline
Перенос рыбы из Blender в Unity с двумя независимыми уровнями деформации:
- физический скелет двигает тело и хвост через
RigidbodyиConfigurableJoint; - morph-анимация меняет только
blendShape-веса в собственном loop и не управляет костями.
Не считать задачу завершённой только по успешному импорту. Проверять геометрию,
материалы, физику, morph-веса и фактическую деформацию через BakeMesh.
Самообновление и адаптация
При каждом вызове:
- Сначала читать этот файл и
references/experience.md. - Проверять версии Unity,
com.unity.cloud.gltfast,com.unity.pipeline, Unity CLI и Blender. Использовать локальную документацию установленных пакетов и introspection API, а не память. - Не копировать параметры прошлой рыбы вслепую. Определять по данным модели:
- продольную ось и направление головы;
- размеры и масштаб;
- тип
Renderer; - число костей и morph targets;
GetBlendShapeFrameWeightи диапазон значений кривых;- пути
AnimationClipи привязки кривых.
- Если API отличается, сначала найти рабочий вызов через документацию или RNA/командную схему.
- После успешной проверки обновлять этот skill и
references/experience.md:- записывать только подтверждённые симптомы, причины, исправления и способы проверки;
- устаревшие инструкции заменять, а не накапливать противоречия;
- неудачные попытки сохранять только как краткие предупреждения с итогом;
- объединять повторы и поддерживать файл компактным.
- Приглашение пользователя к исправлению считать ошибкой pipeline. Сначала найти причину, затем менять код или настройки. Не маскировать ошибку простым уменьшением анимации.
- После подтверждения пользователем рабочего результата зафиксировать его как текущий baseline.
Ограничения самообновления:
- обновления выполняются только во время вызова skill и в рамках запроса пользователя;
- не запускать фоновые процессы, goals или cron для самообновления;
- не делать commit/push без явной просьбы;
- не менять пользовательские модели и анимации без необходимости;
- перед опасными изменениями сохранять исходники или делать отдельную рабочую копию;
- не записывать секреты, токены и личные данные;
- не отключать телеметрию и не менять настройки приватности.
Текущий проверенный контур
- Unity:
6000.6.8f1. com.unity.cloud.gltfast:6.20.0.com.unity.pipeline:0.10.0-exp.1.- Blender:
5.2.3 LTS. - UPM-пакет:
/home/emil/Projects/Unity-tools/fish-gen. - Тестовый Unity-проект:
/home/emil/Projects/FishingGame. - Текущий модуль привязки:
fish-gen/Editor/FishModelBinder.cs. - Текущая morph-модель:
fish-gen/Models/Mesh0Animated.glb.
Это отправная точка, а не обязательные пути для других проектов. В другом проекте сначала
определить его корень и расположение FishGen через unity status и Package Manager.
1. Подготовка модели в Blender
Сохранять исходник отдельно. Для игровой рыбы предпочтительна чистая топология и UV.
При ручной ретопологии:
- сохранять петли вдоль позвоночника и у оснований плавников;
- строить низкополигональную поверхность поверх исходника;
- использовать Shrinkwrap и перенос/запекание текстур;
- проверять границы, non-manifold рёбра и тонкие плавники.
При автоматической ретопологии не считать Decimate достаточным для анимации. Если QuadriFlow отклоняет mesh, проверять manifold и нормали. Для сложной модели допустим Voxel/Sharp ReMesh, но результат проверять визуально и по топологии.
Shape keys и маски
Basisне редактировать.- Vertex Group ограничивает существующую деформацию, но не создаёт её.
- Для движения глаз и рта создавать отдельные shape keys и плавные маски.
- Для независимого morph-loop не использовать костную анимацию.
- Проверять крайние значения ключей на пересечения и растяжения кожи.
При UV Data Transfer в Blender учитывать направление: для переноса из выделенных исходников
в активный target использовать use_reverse_transfer=True. Предварительно проверять RNA:
имена enum и поведение оператора зависят от версии. При ошибке восстанавливать исходник,
а не продолжать с повреждёнными UV.
2. Экспорт GLB с morph-анимацией
Перед экспортом убедиться, что shape keys имеют кривые. Экспортировать только нужный объект. Пример для проверенной Blender-версии:
import bpy
bpy.ops.object.select_all(action='DESELECT')
obj = bpy.data.objects["Mesh_0"]
obj.select_set(True)
bpy.context.view_layer.objects.active = obj
bpy.ops.export_scene.gltf(
filepath="/home/emil/Projects/Unity-tools/fish-gen/Models/Mesh0Animated.glb",
export_format='GLB',
use_selection=True,
export_animations=True,
export_animation_mode='ACTIVE_ACTIONS',
export_frame_range=True,
export_force_sampling=True,
export_morph=True,
export_morph_animation=True,
export_apply=False)
Для другой модели заменить имя, путь и режим экспорта после чтения её action/NLA. Не запекать shape keys в mesh.
3. Импорт и проверка данных в Unity
Использовать unity-cli, unity-pipeline и unity-package-management, если они доступны.
Пакеты добавлять через Package Manager, не редактировать manifest.json вручную.
unity command script eval 'AssetDatabase.ImportAsset(
"Packages/com.shanaty.fish-gen/Models/Mesh0Animated.glb",
ImportAssetOptions.ForceUpdate); return "Imported";'
Проверить:
SkinnedMeshRendererи материал;mesh.blendShapeCountи имена форм;- наличие
AnimationClip; AnimationUtility.GetCurveBindings(clip);- размеры mesh и направление головы.
glTFast импортирует morph-анимации, но их проигрывание и назначение путей требует отдельной настройки.
4. Физический скелет
Текущий рабочий режим Fish Editor: Tools → Fishing → Fish Editor → Test Hanging.
Для каждого сегмента:
Rigidbody;CapsuleColliderс фактическими размерами сегмента;ConfigurableJointс предыдущим сегментом;- ограничения углов, пружина и damping.
Первый сегмент соединять с kinematic Mouth Anchor. Не задавать непропорциональный
localScale физическому телу: размер задаётся коллайдеру, а визуальный mesh остаётся
отдельным дочерним объектом. Для короткой цепочки отключать self-collision между сегментами.
Tail Impulse должен прикладывать силу к кончику последнего Rigidbody через
AddForceAtPosition, поочерёдно меняя направление.
5. Skinning без повреждения morph targets
Для текущей модели ось +Y соответствует голове, а скелет идёт от головы к хвосту по +X.
Для неё используется поворот Quaternion.Euler(0, 0, 90). Для другой рыбы сначала определить
ориентацию — не применять этот поворот автоматически.
Базовые правила:
- позиции вершин: поворот, равномерный масштаб и перенос начала координат;
- нормали и tangents: только поворот;
- веса вершин: плавная смесь двух ближайших костей по продольной координате;
bindposes[i] = bones[i].worldToLocalMatrix * visual.localToWorldMatrix;- источник может быть как
MeshRenderer, так иSkinnedMeshRenderer.
Для каждого morph target копировать все кадры через GetBlendShapeFrameVertices и
AddBlendShapeFrame:
deltaVertices: поворот и тот же равномерный масштаб, что у базовых вершин;deltaNormalsиdeltaTangents: только поворот;- исходный
AnimationClipне изменять.
Два разных веса — не смешивать
Morph-frame weight и animation-curve weight — разные вещи.
В проверенном glTFast GetBlendShapeFrameWeight возвращал 1, тогда Unity ожидал 100.
Это вызывало экстраполяцию и надувание. При переносе определять диапазон и нормализовать его
к 0–100. Если исходный диапазон 0–1, умножать frame weight на 100; если уже 0–100,
не умножать повторно.
Кривые morph-анимации также могут иметь диапазон 0–1, тогда как Unity BlendShape использует
0–100. Вычислять коэффициент по фактическим ключам. Текущий пользовательский уровень
амплитуды — 20% от исходной, но сначала исправлять нормализацию и только затем настраивать
силу анимации.
6. Независимый morph-loop
Использовать Animation на том же GameObject, где находится SkinnedMeshRenderer.
Для каждого исходного клипа:
- Создать новый
AnimationClipсlegacy = trueиwrapMode = Loop. - Копировать только кривые
SkinnedMeshRendererсblendShape.*. - Transform-кривые исключать, чтобы анимация не боролась с физикой.
- Путь кривой заменить на
"", поскольку renderer находится на том же объекте. - Ключи копировать в новый
AnimationCurve, не мутируя исходный клип. - Установить кривую через
loopClip.SetCurve. - Добавить клип в
Animation, включить loop иplayAutomatically.
Исходный путь вида Mesh_0 нельзя оставлять без проверки: он относится к иерархии Blender,
а не к созданному Fish Visual.
7. Обязательная проверка
После изменения C#:
unity command script recompile --project-path <project>
unity command script recompile_status --project-path <project>
Запустить Test Hanging и проверить:
- создание Rigidbody, joints, mesh и материалов;
blendShapeCountи имена форм;Animation.isPlaying;- все кривые loop указывают на
""и только наblendShape.*; - веса форм меняются во времени;
Tail Impulseдвигает последний сегмент одновременно с morph-loop.
Сравнить BakeMesh до и после morph или импульса. Для текущего baseline при Breath = 10
максимальное смещение вершины должно быть примерно 0.00178 м, а не 0.17772 м.
Проверить консоль:
unity command console status --project-path <project>
unity command console get --level error --project-path <project>
После теста остановить Play Mode, снять временный Run In Background override и удалить временные probe-скрипты и тестовые объекты.
8. Диагностика
| Симптом | Сначала проверить |
|---|---|
| Рыба надувается | GetBlendShapeFrameWeight, экстраполяцию и фактический BakeMesh |
| Анимация почти незаметна | Диапазон ключей 0–1 против Unity 0–100 |
| Глаза/рот не двигаются | legacy, Animation.isPlaying, имена форм и пути кривых |
| Анимация ломает физику | Наличие transform-кривых вместо только blendShape.* |
| Mesh внизу/боком | Ось модели, bindposes и направление головы |
| Текстуры испорчены | UV, направление Data Transfer, активную UV-развёртку |
| Суставы дрожат | Масштаб Rigidbody, массы, solver iterations и self-collision |
9. Фиксация результата
После успешной проверки:
- сохранить
.metaвместе с ассетами; - обновить этот skill и
references/experience.mdпо правилам выше; - убедиться, что временные probe-файлы удалены;
- commit/push выполнять только по явному запросу пользователя.