3
votes

This is a follow up to this question

Assume I have a simple MonoBehaviour

public class FooScript : MonoBehaviour {
  public int someValue = 0;
  public int otherValue = 0;
}

I put that on an empty GameObject then drag the GameObject to the Project to create a prefab.

If I select the prefab and edit someValue then if I go to the instance still in the scene I see someValue is updated.

If I edit someValue on the instance then update someValue on the prefab someValue is NOT updated on the instance but if I update otherValue on the prefab it does update on the instance. In other words each instance property has it's own flag on if it has been modified from the source prefab or not.

But, if I make an editor script and lookup FooScript on the prefab and set someValue the instance is NOT updated, only the prefab. I want it to act exactly as if I had done it manually in the editor. In other words, if the instance property has been edited by the user don't update. If instance property has not been edited do update.

The question is what's the correct way to do that? Looking at PrefabUtilily there doesn't appear to be a ApplyPropertiesFromSourcePrefabOnAllUnmodifiedInstancePropreties function.

The only solution I can think of sounds EXTREMELY convoluted. I'd have to

  1. find all instances (walk the entire hierarchy, not sure what happens in unloaded scenes)
  2. for each instance record the modifications are on it by calling PrefabUtility.GetPropertyModifications and storing the results in some Dictionary<GameObject, PropertyModifications[]>. (lots of memory)
  3. Apply my change to the prefab.
  4. Then, for every instance that doesn't have a modification to the field I'm updating update the that property on instance as well.

Is it really that convoluted or is there some easier way?

1
What happens if you use PrefabUtility.MergeAllPrefabInstances()?Isaac van Bakel
Surprisingly that seems to have worked but the docs say "This is done automatically and you will not have to call this function manually." which suggests I'm supposed to do something else. Call tried calling EditorUtilitiy.SaveDirty(prefabGameobject), and that also worked which seems more appropriate?gman
Do you mean EditorUtility.SetDirty(Object)? The docs for that also say that you shouldn't be using it.Isaac van Bakel
Yea, that's what I meant. I don't see where it says not to call it. In fact it says "E.g. if you modify a prefab's MonoBehaviour or ScriptableObject variables, you must tell Unity that the value has changed."gman
To quote the doc: ''Prior to Unity 5.3, this was the primary method of marking objects as dirty. From 5.3 onwards, with the introduction of Multi-Scene Editing, this function should no-longer be used for modifying objects in scenes. Instead, you should use Undo.RecordObject prior to making changes to the object. This will mark the object's scene as dirty and provide an undo entry in the editor.''Isaac van Bakel

1 Answers

1
votes

From a Documentation browse, it looks like Unity expects you to use Undo.RecordObject(Object objectToUndo, string name) in code before you modify a property on the prefab, which will also have the benefit of creating an Undo step in Unity itself so that you can revert any changes.

You can use EditorUtility.SetDirty(Object), but only if you don't want to create an Undo step, and the Unity docs specifically recommend against it.