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

Feedback Feature request: Mark as override, Prevent from applying to prefab

Discussion in 'Prefabs' started by Epsilon_Delta, Jul 15, 2020.

  1. Epsilon_Delta

    Epsilon_Delta

    Joined:
    Mar 14, 2018
    Posts:
    258
    Hello,

    working with prefabs it would be very useful to have:

    1. A context menu, like for overridden attributes:
    upload_2020-7-15_11-10-49.png
    but for every not-overridden Inspector attribute with option "Mark as override". Now when I want an attribute to be an override on prefab instance but to have the same value as prefab asset (so that the change of attribute on prefab asset in the future would not affect the value on prefab instance), I have to change it and then change it again to previous value. Btw, is it somehow possible for us users to extend Unity editor this way? I presume this shouldn't be that hard to implement, but couldn't figure it out. This is a nice to have feature, the next one is more important.

    2. Again, same context menu but for both overridden and not-overridden Inspector attributes, with an option "Prevent from applying to prefab asset/base" . There could be a red indicator upload_2020-7-15_11-18-1.png to show that this value will not be applied when using "Apply all" button or even shown in "Overrides" window. I understand that this can be quite complicated to incorporate in existing prefab system.

    Similar discussions:
    https://forum.unity.com/threads/is-it-possible-to-make-a-field-a-default-override.647443/
    https://forum.unity.com/threads/feature-request-attribute-for-marking-fields-variable.636067/

    Somewhat similar:
    https://forum.unity.com/threads/req...-any-modifications-in-scene-instances.740909/
    https://forum.unity.com/threads/prevent-prefab-instance-overrides.689737/
     
    Last edited: Jul 16, 2020
  2. starikcetin

    starikcetin

    Joined:
    Dec 7, 2017
    Posts:
    340
    Bump. Marking specific fields as transient, and therefore excluding them from "apply to prefab" would be very handy. Some fields are instance-specific, they do not make sense for applying to the prefab.
     
    Last edited: Sep 2, 2021
    TV4Fun likes this.
  3. Peter_Olsted

    Peter_Olsted

    Unity Technologies

    Joined:
    Apr 19, 2021
    Posts:
    75
    Epsilon_Delta

    1. While Editor UI can be modded extensively (e.g. the Asset Store has several plugins which reimagine the Hierarchy and offer workflow benefits) they are all still subject the same underlying Unity API's. The features you suggest would require internal development in the Prefab system. As your forum links show, such ideas have been around a while, but the lack of user consensus requires that we carefully assess the impact across a wide user base before adding features which could adversely impact workflow for some users. That said, your suggestions are noted and will certainly be considered in future Prefab system development. Thanks for your input.

    2: We are actually working on something similar to this, so that in the most common use cases the editor will disable the Apply buttons if there is nothing that can be applied, including changing the look of the blue property override when it cannot be applied.

    And I just realized it was posted last year, but hey, it's worked on.
     
    Last edited: Sep 6, 2021
  4. starikcetin

    starikcetin

    Joined:
    Dec 7, 2017
    Posts:
    340
    Just for clarification: Does this mean we will be able to prevent particular fields from being applied to the prefab?
     
  5. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
    @starikcetin no unfortunately not. The way we planned to support what you would like to do is by allowing you to mark properties driven, through the DrivenPropertyManager, which unfortunately is currently not part of the public API.
     
  6. starikcetin

    starikcetin

    Joined:
    Dec 7, 2017
    Posts:
    340
    @SteenLund I see, thank you. Is it under development? Or in the plans for the near future?
     
  7. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
    @starikcetin It is actually all done, it is just that the API is currently not public. There are plans to make the API public, but I don't know when it is scheduled to be done
     
    NotaNaN, TV4Fun and starikcetin like this.
  8. TV4Fun

    TV4Fun

    Joined:
    Jan 10, 2020
    Posts:
    31
    Bump for this as well. Seeing overrides on all my prefabs for every little adjustment they make to themselves makes it hard to see overrides that are actually meaningful.
     
    Marc-Saubion likes this.
  9. WILEz1975

    WILEz1975

    Joined:
    Mar 23, 2013
    Posts:
    374
    Something like that would be of great help in many cases. Screenshot_46.png
     
  10. JessHarvey

    JessHarvey

    Joined:
    Nov 6, 2018
    Posts:
    5
    Any update on this?
     
  11. Cloudwalker_

    Cloudwalker_

    Joined:
    Jan 3, 2014
    Posts:
    140
    Is this still not possible?
     
  12. LazloBonin

    LazloBonin

    Joined:
    Mar 6, 2015
    Posts:
    809
    Just adding my vote to this. Two things would be really useful:
    • Driven properties
    • Defining a field as a default prefab override (like Transform properties) to have it behave like them (no auto apply/revert, auto mark as override on instantiation, etc.). This could potentially as simple as a [DefaultPrefabOverride] attribute for the field definition.
     
    SirIntruder likes this.
  13. PepeOjeda

    PepeOjeda

    Joined:
    Jul 13, 2017
    Posts:
    2
    It really would be great for this to become part of the public API!
    In the meantime, it is still technically possible (although maybe not a great idea!) to do with Reflection:

    Code (CSharp):
    1.  
    2.             var assembly = System.Reflection.Assembly.Load("UnityEngine.CoreModule");
    3.             var type = assembly.GetType("UnityEngine.DrivenPropertyManager");
    4.             var method = type.GetMethod("RegisterProperty");
    5.             method.Invoke(null, new object[]{this, this, "myProperty"} );
    6.  
    Should probably mention that after doing this you will get a failing assertion when the object is destroyed:

    Apparently, an object which is registered as having driven properties should be ungeristered before it is detroyed (citation needed), but manually unregistering it inside of OnDestroy does not help: you get the same error. I believe it is a benign error, but believe is a key word in that sentence.

    A possible workaround is to unregister the property in OnDisable, which does get called right before destroying the object. The problem, however, is that it of course also gets called whenever you disable the object without intending to destroy it, at which point the property will be considered overriden again :(

    Would love to know if anyone has any ideas on this.
     
    mrwellmann and Marcello-MindPort like this.
  14. PepeOjeda

    PepeOjeda

    Joined:
    Jul 13, 2017
    Posts:
    2
    Upon further testing, turns out that there are several problems with this:

    • Does not work in you change the value through the inspector (but works if the value is changed by the script)
    • Having the property be marked as driven prevents it from being serialized correctly, so the changes are lost when the scene is unloaded.
    I've managed to get an example working correctly, but the code is... more convoluted than I would like.

    Code (CSharp):
    1.  
    2.  
    3. using UnityEngine;
    4. [ExecuteInEditMode]
    5. public class Test : MonoBehaviour, ISerializationCallbackReceiver
    6. {
    7.     [SerializeField]
    8.     public int value;
    9.     int value_copy = 0;
    10.     void Awake()
    11.     {
    12. #if UNITY_EDITOR          
    13.         UnityEditor.EditorApplication.playModeStateChanged += ModeStateChanged;
    14.         UnityEditor.SceneManagement.EditorSceneManager.sceneSaving += BeforeSaveSceneCallback;
    15.         UnityEditor.SceneManagement.EditorSceneManager.sceneSaved += AfterSaveSceneCallback;
    16.         RegisterDrivenProperty();
    17.         if(value == 0)
    18.             value = Random.Range(1, 100);
    19. #endif
    20.     }
    21.     protected virtual void OnDestroy()
    22.     {
    23. #if UNITY_EDITOR          
    24.         UnityEditor.EditorApplication.playModeStateChanged -= ModeStateChanged;
    25.         UnityEditor.SceneManagement.EditorSceneManager.sceneSaving -= BeforeSaveSceneCallback;
    26.         UnityEditor.SceneManagement.EditorSceneManager.sceneSaved -= AfterSaveSceneCallback;
    27. #endif
    28.     }
    29.     protected virtual void OnEnable()
    30.     {
    31. #if UNITY_EDITOR
    32.         if (UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode || isRemovingOverride)
    33.             return;
    34.         RemoveOverrideState(true);
    35. #endif
    36.     }
    37.     protected void OnDisable()
    38.     {
    39. #if UNITY_EDITOR
    40.         if (Application.isPlaying || isRemovingOverride)
    41.             return;
    42.         UnregisterDrivenProperty();
    43. #endif
    44.     }
    45. #if UNITY_EDITOR
    46.     void ModeStateChanged (UnityEditor.PlayModeStateChange state)
    47.     {
    48.         if (state == UnityEditor.PlayModeStateChange.ExitingEditMode)
    49.             UnregisterDrivenProperty();
    50.     }
    51.     void RegisterDrivenProperty()
    52.     {
    53.         RemoveOverrideState(false);
    54.         var assembly = System.Reflection.Assembly.Load("UnityEngine.CoreModule");
    55.         var type = assembly.GetType("UnityEngine.DrivenPropertyManager");
    56.         var method = type.GetMethod("RegisterProperty");
    57.         method.Invoke(null, new object[] { this, this, "value" });
    58.         value = value_copy;
    59.     }
    60.     void UnregisterDrivenProperty()
    61.     {
    62.         value_copy = value;
    63.         var assembly = System.Reflection.Assembly.Load("UnityEngine.CoreModule");
    64.         var type = assembly.GetType("UnityEngine.DrivenPropertyManager");
    65.         var method = type.GetMethod("UnregisterProperty");
    66.         method.Invoke(null, new object[] { this, this, "value" });
    67.         value = value_copy;
    68.     }
    69.     void AfterSaveSceneCallback(UnityEngine.SceneManagement.Scene scene)
    70.     {
    71.         RegisterDrivenProperty();
    72.     }
    73.     void BeforeSaveSceneCallback(UnityEngine.SceneManagement.Scene scene, string path)
    74.     {
    75.         if (scene != gameObject.scene)
    76.             return;
    77.         UnregisterDrivenProperty();
    78.     }
    79.     bool isRemovingOverride = false;
    80.     void RemoveOverrideState(bool registerProperty)
    81.     {
    82.         isRemovingOverride = true;
    83.         value_copy = value;
    84.         if (UnityEditor.PrefabUtility.IsPartOfAnyPrefab(gameObject))
    85.         {
    86.             UnityEditor.SerializedObject serializedObject = new UnityEditor.SerializedObject(this);
    87.             UnityEditor.SerializedProperty serializedPropertyMyInt = serializedObject.FindProperty("value");
    88.             UnityEditor.PrefabUtility.RevertPropertyOverride(serializedPropertyMyInt, UnityEditor.InteractionMode.AutomatedAction);
    89.         }
    90.         if (registerProperty)
    91.             RegisterDrivenProperty();
    92.         value = value_copy;
    93.         isRemovingOverride = false;
    94.  
    95.        
    96.         public void OnBeforeSerialize()
    97.         {
    98.             // IMPORTANT: EditorApplication checks must be done first.
    99.             // Otherise Unity may report errors like "Objects are trying to be loaded during a domain backup"
    100.             if (UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode || UnityEditor.EditorApplication.isUpdating) return;
    101.             // Validate the type of your prefab. Useful pre-check.
    102.             if (UnityEditor.PrefabUtility.GetPrefabAssetType(this) != UnityEditor.PrefabAssetType.Regular) return;
    103.             // Override properties only if this is a prefab asset on disk and not any of its scene instances
    104.             if (UnityEditor.PrefabUtility.GetPrefabInstanceHandle(this)) return;
    105.             // Finally, re-set any fields to initial or specific values for the shared asset prefab on disk
    106.             // This protects these fields when "Apply Override" gets called from any of prefab's scene instances
    107.             value = 0;
    108.         }
    109.  
    110.         public void OnAfterDeserialize()
    111.         {}
    112.     }
    113. #endif
    114. }
    115.  
    With this, it at least serializes correctly. However, it does not work for values changed through the inspector (maybe this could be done by catching the change through OnValidate and calling RemoveOverrideState?); and still has a slightly janky interaction with disabling the object: while the object is disabled, the property is considered to be a prefab override, and it is only when you re-enable it that it goes back to being considered a driven property.

    Better than nothing, I guess.

    EDIT: Added a callback for detecting when we are about to enter play mode in the editor, because for some mysterious reason it resets the value otherwise (but only if the scene is open when the mode change happens). From what I can tell, it works correctly now.

    EDIT (again): Turns out there was STILL trouble with serialization (specifically, when applying all other overrides to the prefab if a value for the property was already serialized as part of the scene). I got a really good solution from https://cgicoffee.com/blog/2022/11/protect-unity-prefabs-from-changes , which honestly could be used to make this whole Driven Property business irrelevant... Almost. Driven properties are still required to prevent the changes from showing up as overrides in the inspector, still.
     
    Last edited: Mar 9, 2024
    leohilbert likes this.