Решили: структуру префаб-варианта правим на PrefabUtility.LoadPrefabContents, а близнецов
различаем по GetCorrespondingObjectFromSource, не по имени. Перенос узла между двумя точками,
стоящими в одном месте, идёт с сохранением ЛОКАЛЬНЫХ координат, а не мировых.
Почему: мировые координаты через SetParent(worldPositionStays: true) пересчитываются, и при
совпадающих родителях дают тот же результат с точностью до float — но уже не тот же, а «почти».
Локальный перенос при совпадающих родителях точен по построению, и это проверяемо одним числом:
мир кисти 0.16771, -0.03415 до и после.
Грабли:
- API оверрайдов не работает на ассете, загруженном
LoadAssetAtPath.GetAddedGameObjectsиGetRemovedGameObjectsтребуют живой инстанс и падают с «not a Prefab instance». Живым инстансом считается кореньLoadPrefabContents— у варианта онConnectedи знает свой источник; на самом ассете тот же вызов бросает исключение. RevertRemovedGameObjectпринимает РОДИТЕЛЯ в инстансе первым, узел из базы вторым. Обратный порядок компилируется и падает в рантайме уже на проверке, а не на сигнатуре.IsAddedGameObjectOverrideпри этом работает и на contents — из-за этого расхождение легко принять за случайный сбой инструмента, а не за правило «нужен инстанс».
Владелец правды: BoneUnit_Storybook.prefab (структура варианта), RigMigrate.cs (перенос путей).