Search code examples
c#unity-game-engineeditorinspectorunity-editor

Unity EditorGuiLayout change slider properties depending on other values


I want to make a custom inspector for my script. I need two gameobjects and a slider. However, the sliders max value is depending on the distance between the two gameobjects. I am using EditorGUILayout (Editor). Until now i tried to change it with an EditorGUI.BeginChangeCheck() and an if statement if (EditorGUI.EndChangeCheck()){...}. However, that doesn't really work and the slider does not show up. I am doing everything in the OnInspectorGUI() method. I am new to this, so I also don't really know what other things i can do.

Here is the code:

public override void OnInspectorGUI()
{
    UILineRenderer renderer = (UILineRenderer) target;
    EditorGUI.BeginChangeCheck();
    EditorGUILayout.BeginHorizontal();
    EditorGUILayout.LabelField("Points");
    renderer.startTR = (GameObject) EditorGUILayout.ObjectField(renderer.startTR, typeof(GameObject), true);
    renderer.endTR = (GameObject)EditorGUILayout.ObjectField(renderer.endTR, typeof(GameObject), true);
    EditorGUILayout.EndHorizontal();
    if (EditorGUI.EndChangeCheck())
    {
        if(renderer.startTR != null && renderer.endTR != null)
        {
            float val = Mathf.Abs(renderer.startTR.GetComponent<RectTransform>().anchoredPosition.x - renderer.endTR.GetComponent<RectTransform>().anchoredPosition.x);
            renderer.radius = EditorGUILayout.Slider("Radius", renderer.radius, 0, val);
        }
    }
}

Is there a way to save a createt EditorGUILayout element and change the properties of it afterwards? If not, how can I solve my problem? Thank you for your help in advance!


Solution

  • You are making a huge but typical mistake ;)

    You are accessing and setting values directly via the target. But this does not mark the object and changed fields as dirty which means

    • these values are not saved
    • the changes don't work with Undo/redo
    • prefabs will not be correctly overwritten

    The most severe being of course already the first one ;)

    Unless you know exactly what you are doing and mark the object manually as dirty you always rather want to go through the SerializedObject and SerializedPropertys.

    This is a bit more effort to set it up but then it handles all the mentioned things automatically:

    SerializedProperty startTr;
    SerializedProperty endTr;
    SerializedProperty radius;
    
    private void OnEnable ()
    {
        // Once link up the serialized properties whith their desired underlying fields
        startTr = serializedObject.FindPropery(nameof(UILimeRenderer.startTr));
        endTr = serializedObject.FindPropery(nameof(UILimeRenderer.endTr));
        radius = serializedObject.FindPropery(nameof(UILimeRenderer.radius));
    }
    
    public override void OnInspectorGUI()
    {
        // Load current values into the serialized object
        serializedObject.Update();
    
        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.LabelField("Points");
    
         // Use the default drawer for these properties
         // GUIContent.None for omitting the label
        EditorGUILayout.PropertyField(startTR, GUIContent.None, true);
        EditorGUILayout.PropertyField(endTR, GUIContent.None, true);
        
        EditorGUILayout.EndHorizontal();
        
        if(startTR.objectReferenceValue && endTR.objectReferenceValue)
        {
            var maxValue = Mathf.Abs(((GameObject)startTR.objectReferenceValue).GetComponent<RectTransform>().anchoredPosition.x - ((GameObject)endTR.objectReferenceValue).GetComponent<RectTransform>().anchoredPosition.x);
            radius.floatValue = EditorGUILayout.Slider("Radius", radius.floatValue, 0, maxValue);
        }
    
        // Write back changed values. This handles all the mentioned dirty marking
        serializedObject.ApplyModifiedProperties();
    }
    

    I would also suggest to use actually RectTransform fields instead of GameObject and then get rid of the GetComponent.