Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Undo and Prefabs

Discussion in 'Prefabs' started by CDF, Jul 18, 2019.

  1. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,307
    Hey, so recently discovered that you can no longer just record undo on objects anymore with the new prefab system. Just wanted to get some clarification on best practices with Undo and prefabs.

    To perform a simple change and record, I believe this is all you need:
    Code (CSharp):
    1. EditorGUI.BeginChangeCheck();
    2.  
    3. var newValue = EditorGUI.DoSomeGUIControl(targetObject.value);
    4.  
    5. if (EditorGUI.EndChangeCheck()) {
    6.  
    7.     Undo.RecordObject(targetObject, "Set Value");
    8.  
    9.     targetObject.value = newValue;
    10.  
    11.     PrefabUtility.RecordPrefabInstancePropertyModifications(targetObject);
    12. }
    What I don't understand is do checks need to be put in place to determine if target object is a prefab, prefab instance or non-prefab? It would appear not, but want to make sure.

    Also, what about:
    1. Adding child game objects?
    2. Adding components?
    3. Removing child game objects?
    4. Removing components?
    And other methods of Undo like:
    1. Undo.RegisterCompleteObjectUndo
    2. Undo.RecordObjects
    3. Undo.RegisterCreatedObjectUndo
    4. Undo.RegisterFullObjectHierarchyUndo
    It would seem like there's a requirement for lots of branching code checking if object is prefab, instance or regular game object, but not sure.

    Would be super awesome to get a blog post or something about how to handle Undo with new prefabs. Like comparing the old Undo style with the new. Outlining new approaches to Undo and any possible pitfalls / limitations.

    For the most part it's a non issue, because we can use SerializedObjects. But in some cases you can't, like modifying objects in OnSceneGUI.
     
  2. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    Sorry for reviving, but found this via google search and there's little info in this.

    Well, I'm trying to modify a prefab inside the scene via code at edit-time, and it seems the changes aren't seen as overrides, and they disappear after reloading the scene from disk.

    I'm calling Undo.RegisterFullObjectHierarchyUndo(go),
    then I modify some scripts on its children objects, without SerializedObject,
    then I call PrefabUtility.RecordPrefabInstancePropertyModifications(go).
    The properties get modified in the scene, but they don't appear as an override
     
  3. Mads-Nyholm

    Mads-Nyholm

    Unity Technologies

    Joined:
    Aug 19, 2013
    Posts:
    217
    You should call PrefabUtility.RecordPrefabInstancePropertyModifications(component) on all the individual components you modify.
     
    xucian likes this.
  4. Mads-Nyholm

    Mads-Nyholm

    Unity Technologies

    Joined:
    Aug 19, 2013
    Posts:
    217
    There is nothing special that needs to be done for prefabs, this is handled by the undo system.
     
  5. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    I'm working on a minimalist dependency injection framework, and I register an undo point before attempting to traverse the whole object tree having go as the root.

    As you can imagine, this is really intensive. So what you're saying is that it's not enough to just call PrefabUtility.RecordPrefabInstancePropertyModifications(go), but I should also call it on each of its components + traverse all its descendants and do the same for them? Maybe I'm missing something.

    I could track all the changes, and register them individually but it's a lot of work because the traverser algorithm can be used at both edit- and runtime, and I'd prefer to avoid additional logic inside it, especially because the prefab stuff is only necessary in editor, and editor performance is cheap (this only happens on a click)
     
  6. Mads-Nyholm

    Mads-Nyholm

    Unity Technologies

    Joined:
    Aug 19, 2013
    Posts:
    217
    You should only call
    PrefabUtility.RecordPrefabInstancePropertyModifications(component)
    after you have changed properties on the component. For undo you record the object before making changes to it.
    Hope this helps.
     
    xucian likes this.
  7. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    thanks, that clarifies the high-level part. I just wanted to know what to do in case a whole hierarchy of objects was changed, and I don't know which nodes and which components on those nodes.
    would I have to blindly call that
    PrefabUtility.RecordPrefabInstancePropertyModifications(component)
    on everything?