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
  4. Dismiss Notice

Serialized properties in custom editor changing different properties in other objects

Discussion in 'Scripting' started by Joselito123, Oct 21, 2020.

  1. Joselito123

    Joselito123

    Joined:
    Aug 12, 2014
    Posts:
    8
    Hi, I've been struggling with this for a while and would appreciate any help or indication as to what I'm missing or doing wrong. I have two similar components:

    Code (CSharp):
    1. public class Test1 : MonoBehaviour
    2. {
    3.     public string string1;
    4. }
    and

    Code (CSharp):
    1. public class Test2 : MonoBehaviour
    2. {
    3.     public string string2
    4. }
    For which I defined custom inspectors:

    Code (CSharp):
    1. [CustomEditor(typeof(Test1))]
    2. public class Test1Editor : Editor
    3. {
    4.     SerializedProperty string1;
    5.  
    6.     void OnEnable()
    7.     {
    8.         string1 = serializedObject.FindProperty("string1");
    9.     }
    10.  
    11.     public override void OnInspectorGUI()    {
    12.        
    13.         serializedObject.Update();
    14.  
    15.         EditorGUILayout.PropertyField(string1);
    16.  
    17.         GUI.SetNextControlName("Clicked in test 1");
    18.         EditorGUILayout.SelectableLabel("Click here", GUILayout.Height(EditorGUIUtility.singleLineHeight));
    19.  
    20.         string selectedMember = GUI.GetNameOfFocusedControl();
    21.  
    22.         if (!string.IsNullOrEmpty(selectedMember))
    23.         {
    24.             string1.stringValue = selectedMember;
    25.         }
    26.  
    27.         serializedObject.ApplyModifiedProperties();
    28.     }
    29. }
    And an equivalent one for Test2 (swapping the "1"s for "2"s).

    I get the following in the Unity inspector for an object with the Test1 component:



    And as expected, when I click on "Click here", I get



    The problem is, when I now inspect an object with the Test2 component, this one is also showing the same string in its string2 field:



    Which I'm not expecting at all since I haven't even manipulated the Test2 component yet. The same thing also happens in reverse; clicking on "Click here" in the Test2 component also changes the string in the Test1 component to "Clicked in test 2".

    I'm using Unity 2020.1.3, same thing happened in 2019.4. Any help or guidance would be greatly appreciated. Thanks in advance!
     
  2. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    664
    I believe both
    OnInspectorGUI
    methods are running for each of your classes when you click on the label. Both methods are getting the same string at Line 20, and both are setting the text value with that string at Line 24. I suspect you are thinking that
    OnInspectorGUI
    only gets called for the class you are editing, but it gets called at times you wouldn't expect. To see if that's what's happening, put
    print("Test1");
    and
    print ("Test2");
    right after Line 11 in each of your editor classes. I predict you'll see both being called when you only expected one.
     
  3. Joselito123

    Joselito123

    Joined:
    Aug 12, 2014
    Posts:
    8
    Thanks for taking the time to reply! I just tried what you said, but all print outputs are as expected. What else could it be?
     
  4. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,538
    GUI.GetNameOfFocusedControl() is a static method that returns the name of the selected control whenever the control that is selected is named. In both of your inspectors you just blindly check if the selected control name is not empty and you just assign this name to your string variable. To me this looks like you try to abuse the static focusedcontrol name like a button for pre selected texts. If you want a button that you can press to set the string to a certain predefined text, use a button. Do something like this:

    Code (CSharp):
    1.  
    2. public override void OnInspectorGUI()
    3. {
    4.     serializedObject.Update();
    5.     EditorGUILayout.PropertyField(string1);
    6.     if (GUILayout.Button("Clicked in test 1"))
    7.         string1.stringValue = "Clicked in test 1";
    8.     serializedObject.ApplyModifiedProperties();
    9. }
    If you don't want the button to look like a button but like a label, just do
    Code (CSharp):
    1.     if (GUILayout.Button("Clicked in test 1", "label"))
    2.  
     
  5. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    664
    It's what I told you. Here's the output from a single click, after adding two lines:
    upload_2020-10-21_11-5-1.png

    Here are my editor scripts, with one new line each added to your code:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3.  
    4. [CustomEditor(typeof(Test2))]
    5. public class Test2Editor : Editor
    6. {
    7.     SerializedProperty string2;
    8.  
    9.     void OnEnable()
    10.     {
    11.         string2 = serializedObject.FindProperty("string2");
    12.     }
    13.  
    14.     public override void OnInspectorGUI()
    15.     {
    16.         serializedObject.Update();
    17.  
    18.         EditorGUILayout.PropertyField(string2);
    19.  
    20.         GUI.SetNextControlName("Clicked in test 2");
    21.         EditorGUILayout.SelectableLabel("Click here", GUILayout.Height(EditorGUIUtility.singleLineHeight));
    22.  
    23.         string selectedMember = GUI.GetNameOfFocusedControl();
    24.  
    25.         if (!string.IsNullOrEmpty(selectedMember))
    26.         {
    27.             Debug.LogFormat("Test2Editor setting it to [{0}]", selectedMember);
    28.             string2.stringValue = selectedMember;
    29.         }
    30.  
    31.         serializedObject.ApplyModifiedProperties();
    32.     }
    33. }
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3.  
    4. [CustomEditor(typeof(Test1))]
    5. public class Test1Editor : Editor
    6. {
    7.     SerializedProperty string1;
    8.  
    9.     void OnEnable()
    10.     {
    11.         string1 = serializedObject.FindProperty("string1");
    12.     }
    13.  
    14.     public override void OnInspectorGUI()
    15.     {
    16.         serializedObject.Update();
    17.  
    18.         EditorGUILayout.PropertyField(string1);
    19.  
    20.         GUI.SetNextControlName("Clicked in test 1");
    21.         EditorGUILayout.SelectableLabel("Click here", GUILayout.Height(EditorGUIUtility.singleLineHeight));
    22.  
    23.         string selectedMember = GUI.GetNameOfFocusedControl();
    24.  
    25.         if (!string.IsNullOrEmpty(selectedMember))
    26.         {
    27.             Debug.LogFormat("Test1Editor setting it to [{0}]", selectedMember);
    28.             string1.stringValue = selectedMember;
    29.         }
    30.  
    31.         serializedObject.ApplyModifiedProperties();
    32.     }
    33. }
     
  6. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    664
    That can't quite be the case, because I told you something dumb on my part: I told you to add a call to
    print
    in a class derived from
    Editor
    . That won't work, as
    print
    is a method of the
    MonoBehaviour
    class, not Editor. If you used it and it compiled, it wasn't where I suggested you try it.
     
  7. Joselito123

    Joselito123

    Joined:
    Aug 12, 2014
    Posts:
    8
    Thanks, I'll give this a try!

    Edit: noticed I had quoted the wrong post here
     
    Last edited: Oct 21, 2020
  8. Joselito123

    Joselito123

    Joined:
    Aug 12, 2014
    Posts:
    8
    Yeah, I noticed; I explicitly used MonoBehaviour.print() to get the pintouts, and also tried it with Debug.Log() for good measure :p
    Maybe I didn't explain my problem correctly, the Test1 and Test2 components are on different objects. Putting them on the same object indeed leads to the outputs you're saying.
     
  9. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    664
    It is still what I (and Bunny83) have told you. If I put the Test1 script on a GameObject called Cube 1 and the Test2 script on a GameObject called Cube 2, select Cube 1, click on the label, then select Cube 2, here is the output:

    upload_2020-10-21_11-27-38.png
     
  10. Joselito123

    Joselito123

    Joined:
    Aug 12, 2014
    Posts:
    8
    I had put the print call at the beginning of the OnInpectorGUI method as you had initially said. In that case, the output is as expected. Regardless, the printout calls in the if block make it clear that the problem is with the static nature of GetNameOfFocusedControl.... I will try replacing my labels with buttons.

    I greatly appreciate both of you taking the time to help me with this!
     
  11. Joselito123

    Joselito123

    Joined:
    Aug 12, 2014
    Posts:
    8
    Yup, using buttons worked like a charm! Thanks a lot, Stevens and Bunny! :)
     
  12. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    664
    Well, I had hoped that would make it clear that both methods were running, but I guess it wasn't obvious that this would mean both were getting the same string at your original Line 20. Anyway, if you've got it sorted out now, then all's well in the world.

    Custom editors can be a challenge, particularly if you are used to an event-driven GUI model, such as Java's Swing. Unity uses an immediate-mode, polling approach. It's kind of like leaping back about 30 years in computer programming, but that's gamedev for you.