Search Unity

Prefab mode not automatically detecting some changes - Best way to enforce a save?

Discussion in 'Prefabs' started by hunterofakara, Oct 6, 2018.

  1. hunterofakara

    hunterofakara

    Joined:
    Oct 4, 2018
    Posts:
    21
    I'm building ontop of the unity UI then hiding/wrapping the complexity so that I'll hopefully end up with simple looking UI prefabs for each control type to work with.

    An issue I've run into is that the prefab editor doesn't seem to detect changes made by my custom inspector via references to nested components.
    No autosave, and save button not lit up when autosave is turned off.


    The only way I've managed to get it to detect changes is to do RecordUndo and RecordPrefabInstancePropertyModifications directly on the component that has the field being modified.

    To do this for private fields requires an annoying chain of editor only functions like SetTextEditor here for every component:

    Code (CSharp):
    1. public abstract class BaseUIElement : MonoBehaviour
    2. {
    3.     //Various layout code and common fields
    4. }
    5.  
    6. public class UILabel : BaseUIElement
    7. {//text
    8.     [SerializeField]
    9.     private UnityEngine.UI.Text textComponent;
    10.    
    11.     public string Text
    12.     {
    13.         get
    14.         {
    15.             return textComponent.text;
    16.         }
    17.         set
    18.         {
    19.             textComponent.text = value;
    20.         }
    21.     }
    22.  
    23.  
    24. #if UNITY_EDITOR
    25.     public void SetTextEditor( string Text )
    26.     {//prefab saving workaround
    27.         if( text.text != Text )
    28.         {//only record undo if changed
    29.             UnityEditor.Undo.RecordObject(text, "Label Text"); //let ctrl+z work
    30.             textComponent.text = Text;
    31.             UnityEditor.PrefabUtility.RecordPrefabInstancePropertyModifications(text); //let unity know a prefab change can be saved
    32.         }
    33.     }
    34. #endif // UNITY_EDITOR
    35. }
    36.  
    37. public class UIButton : BaseUIElement
    38. {
    39.     public string Text
    40.     {
    41.         get
    42.         {
    43.             return buttonLabel.Text;
    44.         }
    45.         set
    46.         {
    47.             buttonLabel.Text = value;
    48.         }
    49.     }
    50.  
    51.     [SerializeField]
    52.     private UILabel buttonLabel;
    53.  
    54. #if UNITY_EDITOR
    55.     public void SetTextEditor( string text )
    56.     {//prefab saving workaround
    57.         buttonLabel.SetTextEditor(text);
    58.     }
    59. #endif // UNITY_EDITOR
    60.  
    61. }
    62.  
    63.  
    64. [CustomEditor(typeof(UIButton))]
    65. public class UIButtonInspector : Editor
    66. {
    67.     private string text;
    68.     private UIButton button;
    69.  
    70.     void OnEnable()
    71.     {
    72.         button = target as UIButton;
    73.         text = button.Text;
    74.     }
    75.     public override void OnInspectorGUI()
    76.     {
    77.         string newText = EditorGUILayout.TextField("Text",text);
    78.         if( text != newText )
    79.         {
    80.             text = newText;
    81.             //button.Text = text; //doesn't get recognized as a change by undo, and FindProperty doesn't find properties.
    82.             button.SetTextEditor(text); //Workaround to allow prefab change to be recognized
    83.         }
    84.     }
    85. }


    Is there something I'm missing that would let me save these prefab changes without modifying my runtime scripts like this?

    HideFlags don't appear to save at all, even with such a workaround, but I guess that could be intended on prefabs?


    A couple of other issues I've run into:
    HideFlags.HideInInspector makes nested prefab components show incorrectly as an overriden removal in the inspector (luckily it doesn't propegate as such, presumably due to the very issue above, but it does take up inspector space where it shows the option of applying/reverting the 'removal')

    You can't change the rect transform (width etc) of a varient, yet it doesn't inherit changes from the original, so it just gets set permanantly at the values it had when you first made it a varient, forcing you to delete it, alter the original's rect transform, remake the varient and all other changes, then un-alter the original.
     
  2. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    xVergilx likes this.
  3. hunterofakara

    hunterofakara

    Joined:
    Oct 4, 2018
    Posts:
    21
    The SerializedProperty system doesn't work with c# properties and the actual component that has the field being set isn't directly accessible from the component being inspected (references an object which holds a private reference to it).

    I tried various combinations of FindPropertyRelative but the resulting SerializedProperty was always null (I assumed it just didn't work for private/nested fields, not that having to duplicate the entire reference chain is really a solution).

    Marking scene as dirty wasn't working for my first test of hideflags, must have forgotten to try it for the other changes.
    EditorSceneManager.MarkSceneDirty seems to be working for everything else - Thanks!
     
  4. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    Something like PrefabUtility.ForceSave(gameObject) would be really great.
     
  5. ELenDiL_Unity

    ELenDiL_Unity

    Joined:
    Sep 29, 2014
    Posts:
    4
    Hi I find the way. Very painful, But I did it.....

    Use this api below

    var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
    if (prefabStage != null)
    {
    EditorSceneManager.MarkSceneDirty(prefabStage.scene);
    }