Search Unity

PrefabUtility.prefabInstanceUpdated delegate (why not event?)

Discussion in 'Prefabs' started by liortal, Sep 14, 2018.

  1. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    Not sure this is new functionality in 2018.3, but still:
    In PrefabUtility we have this static delegate field:
    Code (CSharp):
    1.  public static PrefabUtility.PrefabInstanceUpdated prefabInstanceUpdated;
    Why was this made public static, and not public static event? Basically this can be nulled or accessed from outside of this class.
     
  2. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    1,188
    I don’t think this was introduced in the beta. The same pattern (delegate but no event) is repeated throughout the editor API. Maybe performance reasons or the way these are called from C++? I would also assume that they override the behaviours, so we can’t break it by setting it null (edit: this assumption is wrong, we can break these delegates). I would be interested to know as well why this was chosen and not events.
     
    Last edited: Sep 22, 2018
  3. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    You can break it by setting it to null. For example - my code sets the delegate to something, but some other plugin or 3rd party codes deletes that.
     
  4. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    1,188
    I can confirm this and that it's also true for
    EditorApplication.delayCall
    . I agree, that those should rather be events or implemented to not allow breaking Unity callbacks from user code.
     
  5. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    I know, so when creating new APIs, it's better to not repeat past mistakes all over again.
     
  6. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
    Hi, this callback existed before 2018.3 so for backwards compatibility we can't change it to an event (sadly)
     
    liortal likes this.
  7. jayatubi

    jayatubi

    Joined:
    Dec 9, 2013
    Posts:
    143
    This event no longer works on 2018.3. Any alternatives?
     
  8. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
    that would be a bug we need to fix asap.
     
  9. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
    @jayatubi

    We had a look at it and it works as expected. Can you share some details of where it does not work for you?
     
  10. jayatubi

    jayatubi

    Joined:
    Dec 9, 2013
    Posts:
    143
    @SteenLund

    Version 2018.3.0f2 Windows

    Here is my code under Editor folder:
    Code (CSharp):
    1. [InitializeOnLoad]
    2. public static class MyEditorTool
    3. {
    4.     static MyEditorTool()
    5.     {
    6.         PrefabUtility.prefabInstanceUpdated += OnPrefabInstanceUpdated;
    7.     }
    8.  
    9.    
    10.     static void OnPrefabInstanceUpdated(GameObject instance)
    11.     {
    12.         Debug.Log("Prefab update: " + instance);
    13.     }
    14. }
    Nothing output when I was going to save my prefab via the 'Save' button at the top of scene window.
    It used to be working on the previous version with the old 'Apply' button on the inspector window.
     
  11. jayatubi

    jayatubi

    Joined:
    Dec 9, 2013
    Posts:
    143
    I found a way to make it work.
    Only the
    PrefabUtility.SaveAsPrefabAssetAndConnect
    will fire this event.
    PrefabUtility.SaveAsPrefabAsset
    or click 'Save' button in the scene window or Ctrl + S won't fire this event.
     
  12. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
    This is expected. The callback is only supposed to execute for actual instances. The GameObjects loaded in Prefab Mode are part of a prefab instance, except for nested prefabs.

    PrefabUtility.SaveAsPrefabAsset
    does not connect to the GameObjects to prefab asset, thus the GameObjects does not become a prefab instance and thus you are not getting a callback.

    There is nothing unexpected in what you are experiencing.
     
  13. jayatubi

    jayatubi

    Joined:
    Dec 9, 2013
    Posts:
    143
    Any alternatives for SaveAsPrefabAsset?
     
  14. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
    What are you looking for?
     
  15. n8allan

    n8allan

    Joined:
    Dec 11, 2012
    Posts:
    1
    @SteenLund, I can't speak for other users, but I've been looking for a hook when a prefab is saved that gives me the actual prefab object.

    I tried OnWillSaveAssets in a custom AssetModificationProcessor class, as I've done for scenes, but I haven't been able to find the equivalent of EditorSceneManager.GetSceneByPath for a prefab.

    I intuited that prefabInstanceUpdated would be called at save, but discovered, as discussed above, that it is not.

    To others: the best practice when hooking a single-cast delegate is to store the old value, and call it (if it exists) internally:
    Code (CSharp):
    1.     [InitializeOnLoad]
    2.     public class PrefabChanged
    3.     {
    4.         private static PrefabUtility.PrefabInstanceUpdated _oldUpdated;
    5.  
    6.         static PrefabChanged()
    7.         {
    8.             if (!Application.isPlaying)
    9.             {
    10.                 _oldUpdated = PrefabUtility.prefabInstanceUpdated;
    11.                 PrefabUtility.prefabInstanceUpdated = InstanceUpdated;
    12.             }
    13.         }
    14.  
    15.         private static void InstanceUpdated(GameObject instance)
    16.         {
    17.             _oldUpdated?.Invoke(instance);
    18.             //... other stuff
    19.         }
    20.     }
     
    andy_unity945 and Vedran_M like this.