Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Prefab editor code from 5.6 no longer working in 2018.3

Discussion in 'Prefabs' started by Alden-R, Jan 28, 2019.

  1. Alden-R

    Alden-R

    Joined:
    Aug 19, 2014
    Posts:
    10
    We have been upgrading code from 5.6.5 to 2018.3.2. We used to be use editor code to call into a MonoBehaviour method to modify the prefab, but that no longer seems to be the case. I haven't been able to track down if this is an expected change or a bug, and haven't seen anyone post about it. Obviously many iterations of Unity have come and gone in this period so it could be expected and I missed it.

    The basic breakdown appears to be that if you use an editor that calls into methods on the target MonoBehaviour directly the changes will not persist. Below is my test case showing two methods of changing a single integer. One uses a Randomize function on the TestScript and the other sets it directly. Both are modified to use SerializedProperty and SerializedObject to apply the changes. If you use the Randomize function the value will appear to change but will not persist when you view another object, whereas any changes made with the IntField do persist. Is this a bug or expected behavior?

    TestScript.cs:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class TestScript : MonoBehaviour
    4. {
    5.     [SerializeField, HideInInspector]
    6.     private int m_integer;
    7.  
    8.     public int Integer { get { return m_integer; } }
    9.  
    10.     public void Random()
    11.     {
    12.         m_integer = new System.Random().Next();
    13.     }
    14. }
    TestScriptEditor.cs:
    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3.  
    4. [CustomEditor(typeof(TestScript), true)]
    5. public class TestScriptEditor : Editor
    6. {
    7.     public override void OnInspectorGUI()
    8.     {
    9.         // Randomize button, uses MonoBehaviour function, works in 5.6 but not in 2018
    10.         EditorGUILayout.BeginVertical("Box");
    11.         DrawIntegerInspector();
    12.         EditorGUILayout.EndVertical();
    13.  
    14.         // Setting the field directly without touching the MonoBehaviour itself directly
    15.         EditorGUILayout.BeginVertical("Box");
    16.         DrawIntegerInspector2();
    17.         EditorGUILayout.EndVertical();
    18.  
    19.         base.OnInspectorGUI();
    20.     }
    21.  
    22.     private void DrawIntegerInspector()
    23.     {
    24.         // Retrieve the actual value
    25.         int value = 0;
    26.         SerializedObject so = serializedObject;
    27.         if (so != null)
    28.         {
    29.             SerializedProperty prop = so.FindProperty("m_integer");
    30.             if (prop != null)
    31.             {
    32.                 value = prop.intValue;
    33.             }
    34.         }
    35.  
    36.         // SelectableLabel doesn't play nicely with BeginHorizontal so manually place controls
    37.         Rect pos = EditorGUILayout.GetControlRect();
    38.         float rightWidth = pos.width - EditorGUIUtility.labelWidth;
    39.         pos.width = EditorGUIUtility.labelWidth;
    40.         EditorGUI.SelectableLabel(pos, value.ToString());
    41.  
    42.         // Add a button to generate a new value using a function on the MonoBehaviour
    43.         pos.x += EditorGUIUtility.labelWidth;
    44.         pos.width = rightWidth;
    45.         if (GUI.Button(pos, "Randomize"))
    46.         {
    47.             RandomizeInteger();
    48.         }
    49.     }
    50.  
    51.     private void DrawIntegerInspector2()
    52.     {
    53.         SerializedObject so = serializedObject;
    54.         if (so != null)
    55.         {
    56.             SerializedProperty prop = so.FindProperty("m_integer");
    57.             if (prop != null)
    58.             {
    59.                 prop.intValue = EditorGUI.IntField(EditorGUILayout.GetControlRect(), prop.intValue);
    60.                 so.ApplyModifiedProperties();
    61.             }
    62.         }
    63.     }
    64.  
    65.     private void RandomizeInteger()
    66.     {
    67.         var testScript = target as TestScript;
    68.         if (testScript != null)
    69.         {
    70.             // Running a function like Random() then using SetDirty used to work in 5.6
    71.             int originalInteger = testScript.Integer;
    72.             do
    73.             {
    74.                 testScript.Random();
    75.             }
    76.             while (testScript.Integer == originalInteger);
    77.  
    78.             // Attempt to set the value directly, changes don't persist
    79.             SetInteger(testScript.Integer);
    80.  
    81.             EditorUtility.SetDirty(testScript);
    82.         }
    83.     }
    84.  
    85.     private void SetInteger(int a_value)
    86.     {
    87.         SerializedObject so = serializedObject;
    88.         if (so != null)
    89.         {
    90.             SerializedProperty prop = so.FindProperty("m_integer");
    91.             if (prop != null)
    92.             {
    93.                 prop.intValue = a_value;
    94.                 so.ApplyModifiedProperties();
    95.             }
    96.         }
    97.     }
    98. }
    The workaround is to move any code like that out of the MonoBehaviour class and apply it using SerializedProperty changes but that is not ideal for complicated MonoBehaviours.
     
  2. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    Alden-R likes this.
  3. Alden-R

    Alden-R

    Joined:
    Aug 19, 2014
    Posts:
    10
    Thanks, sient! Adding a MarkSceneDirty call after after the old SetDirty call made the Randomize function work as desired.