fix(prefab): edit prefab assets without prefab-stage save corruption (set_prefab_property)#45
Open
dehuaichendragonplus wants to merge 1 commit into
Conversation
…corruption
Add set_prefab_property / set_prefab_properties: edit a component field inside a
.prefab asset in place via SerializedObject, without opening a prefab stage.
Because no live isolated scene/Canvas is created, edit-mode drivers (UGUI layout,
ContentSizeFitter, TMP auto-size, Spine SkeletonGraphic) never recompute and
mutate the graph, so only the field you set changes. This is the safe alternative
to open_prefab_stage -> set_component_property -> save_prefab_stage for single-field
edits; value format and Object-reference handling reuse ComponentSerializer.
Also: save_prefab_stage / close_prefab_stage now warn when the open prefab contains
components that recompute in a stage, since PrefabUtility.SaveAsPrefabAsset
re-serializes the whole in-memory graph and freezes those transient values into the
asset (RectTransform/font freezing; Spine skeletonDataAsset zeroed to {fileID:0}).
Adds 10 EditMode tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Editing a single field on a prefab that contains UGUI layout drivers, auto-sizing TMP text, or Spine components via the
open_prefab_stage→set_component_property→save_prefab_stagethree-piece silently corrupts unrelated fields in the.prefabasset.Root cause (reproduced deterministically on Unity
6000.3.13f1, a pure open+save with zero edits already corrupts):open_prefab_stage(PrefabStageUtility.OpenPrefab) instantiates the prefab into a live isolated scene with its own Canvas.[ExecuteAlways]edit-mode drivers then run without Play Mode:LayoutGroup/ContentSizeFitterand TMP auto-size dirty intoCanvasUpdateRegistryand rebuild against the isolated canvas's default metrics, mutatingRectTransform.m_SizeDelta/m_AnchoredPosition/m_LocalScaleand TMPm_fontSizein memory; SpineSkeletonGraphic(which has noIsPartOfPrefabAssetguard) rewrites rects and, when an import/refresh reinitializes it, nulls itsskeletonDataAsset.save_prefab_stage/close_prefab_stagethen callPrefabUtility.SaveAsPrefabAsset(stage.prefabContentsRoot, …), a wholesale re-serialize with no per-field change tracking, which faithfully writes those transient values back to disk.Observed on real prefabs (each pure open+save, no intended edit):
m_AnchoredPosition {155,-27} → {0,-27},m_LocalScale 0.9885 → 1frozen.skeletonDataAsset {fileID:11400000, guid:…, type:2} → {fileID:0}— runtime animation permanently lost (the{fileID:0}is bare with no guid, i.e. the field was genuinely null in memory, not a guid-reassignment churn).There is no way to make
SaveAsPrefabAssetwrite only the intended field, so a single-field edit through the stage is inherently unsafe on these prefabs.What changed
New tools —
set_prefab_property/set_prefab_properties(inComponentPropertyFunctions): edit a component field (or fields) inside a.prefabasset in place, without opening a prefab stage:Because the prefab is never instantiated into a live scene, no layout/TMP/Spine rebuild tick runs, so only the field you set changes. Value format,
[SerializeField] privatesupport, and Object-reference handling ({"fileID":id}/{"assetPath":"Assets/…"}) are reused verbatim fromset_component_property; the response echoes the post-write value (newValue/applied). Structural edits (add/reparent components) still use the stage.Guard on
save_prefab_stage/close_prefab_stage: before the wholesale save, scan the stage root for corruption-prone drivers (UGUIILayoutController/ILayoutGroup, auto-sizing TMP text, Spine components — detected by interface/type name via reflection, no new UI/TMP/Spine assembly dependency). When present, the tool message carries a⚠ WARNINGnaming the risk kinds and steering single-field edits toset_prefab_property. The save still proceeds (structural edits legitimately need it) — the corruption is just no longer silent.Benefits
AssetDatabase.ImportAsset(ForceUpdate)" workaround:SerializedObjectresolves Object references ({fileID, guid, type}) correctly, which hand-written YAML routinely gets wrong.ComponentSerializermachinery, so type coercion / private-field / object-ref behavior stays identical toset_component_property.Compatibility
set_prefab_property/set_prefab_propertiesare new tools; nothing else changes behavior.save_prefab_stage/close_prefab_stagekeep their existing string return and success message verbatim — the warning is appended only when risk drivers are detected, so existing callers andDoes.Contain("Prefab stage saved")-style checks are unaffected.com.unity.ugui, TextMeshPro, or Spine.set_prefab_property— may upgrade a prefab authored by an older Unity/TMP version to the current serialization format. That is Unity's normal, harmless format migration and is orthogonal to this fix.Testing & verification
6000.3.13f1editor via the MCP tools: on the exact layout-heavy Spine prefab that the three-piece corrupts (3RectTransformfields rewritten on a no-op save),set_prefab_propertyproduces a 2-line diff — only the target field — andsave_prefab_stagenow emits the⚠warning listinglayout / Spine / TMP auto-size.PREFAB_NOT_FOUND,PREFAB_GAMEOBJECT_NOT_FOUND,COMPONENT_NOT_FOUND_ON_TARGETwith anavailablecomponent list,PROPERTY_REQUIRED,PROPERTIES_REQUIRED).PrefabPropertyFunctionsTests) covering: only-target-field-changes + no-stage-opened, post-write echo, multi-field apply, root-path resolution, all error paths, and the layout-driver save warning. All pass; no regressions in the rest of the EditMode suite.Changelog
Added under
## Unreleased(Added + Fixed).