Search code examples
c#unity-game-engineuser-interfaceunity3d-editor

Unity CreateInspectorGUI Redraw to display buttons


I am working on a Unity program that has a grid based system in it. I am trying to make a custom editor for the grid system, basically you can change the size of the grid, have it display a set of buttons representing the grid, and each time a button is clicked it iterates through some set of tiles.

I have most of it working, I just can't get the inspector the redraw the grid when the width/height changes. If I run the game it works, but it won't do it automatically. From what I can find it seems like recalling CreateInspectorGUI can't happen. Is there a way I can do this without using OnInspectorGUI? I am trying to stay in the UIElements system but I am not sure if I have a choice here.

My inspector code is shown below:

using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine.UIElements;


[CustomEditor(typeof(GridHolder))]
[CanEditMultipleObjects]
public class Grid_Inspector : Editor
{
SerializedProperty m_Grid_Width;
SerializedProperty m_Grid_Height;
SerializedProperty m_Grid_Size_X;
SerializedProperty m_Grid_Size_Y;

private void OnEnable()
{
    m_Grid_Width = serializedObject.FindProperty("gridWidth");
    m_Grid_Height = serializedObject.FindProperty("gridHeight");
    m_Grid_Size_X = serializedObject.FindProperty("gridXSize");
    m_Grid_Size_Y = serializedObject.FindProperty("gridYSize");
}

// public override void OnInspectorGUI()
// {
//     EditorGUILayout.PropertyField(m_Grid_Width, new GUIContent("Grid Width"), GUILayout.Height(20));

//     serializedObject.ApplyModifiedProperties();
// } 

public override VisualElement CreateInspectorGUI()
{
    VisualElement container = new VisualElement();

    Label title = new Label("Grid Inspector Element");

    container.Add(title);

    VisualElement gridDimensions = new VisualElement();
    gridDimensions.style.flexDirection = FlexDirection.Row;

    PropertyField gridWidth = new PropertyField(m_Grid_Width, "Grid Dimensions (X,Y):");
    PropertyField gridHeight = new PropertyField(m_Grid_Height);

    gridDimensions.TrackPropertyValue(m_Grid_Width, _ => {
        UnityEngine.Debug.Log("Hit");
        Repaint();
    });

    gridDimensions.Add(gridWidth);
    gridDimensions.Add(gridHeight);

    VisualElement gridElementSize = new VisualElement();
    gridElementSize.style.flexDirection = FlexDirection.Row;

    PropertyField gridXSize = new PropertyField(m_Grid_Size_X, "Grid Size (in meters):");
    PropertyField gridYSize = new PropertyField(m_Grid_Size_Y);

    gridElementSize.Add(gridXSize);
    gridElementSize.Add(gridYSize);

    container.Add(gridDimensions);
    container.Add(gridElementSize);

//     // randomColorButton.style.width = new Length(50, LengthUnit.Pixel);
//     // randomColorButton.style.height = new Length(50, LengthUnit.Pixel);

    container.Add(DrawGrid());

    return container;
}

private VisualElement CreateButton(string text, int sizeX, int sizeY)
{

    Button b = new Button() { text = text };
    b.style.width = new Length(sizeX, LengthUnit.Pixel);
    b.style.height = new Length(sizeY, LengthUnit.Pixel);
    return b;
}

private VisualElement DrawGrid()
{
    VisualElement container = new VisualElement();
    try
    {
        for(int i = 0; i < m_Grid_Height.intValue; i++)
        {
            VisualElement row = new VisualElement();
            row.style.flexDirection = FlexDirection.Row;
            for(int j = 0; j < m_Grid_Width.intValue; j++)
            {
                //TODO Connect this to the bool array in GridHolder
                string text = true?" ":"X";
                row.Add(CreateButton(text, 50, 50));
            }
            container.Add(row);
        }
    }
    catch(System.Exception e)
    {
        UnityEngine.Debug.LogWarning(e.Message);
    }
    return container;
}
}

Thank you for any help you can give! Worst case I switch to the IMGUI system, but I would rather not have to do that


Solution

  • Instead of just calling Repaint you would have to recreate the grid.

    So you could probably do something like e.g.

    public override VisualElement CreateInspectorGUI()
    {
        ...
        
        var gridContainer = new VisualElement();
    
        gridDimensions.TrackPropertyValue(m_Grid_Width, _ => {
            UnityEngine.Debug.Log("Hit");
            DrawGrid(gridContainer);
    
            // Afaik Repaint is only to cause a new call to OnInspectorGUI
            // it shouldn't be relevant for UIElements as any change
            // on the VisualElements should already cause a repaint of that container anyway
        });
    
        ...
    
        container.Add(gridContainer);
        
        DrawGrid(gridContainer);
        
        return container;
    }
    
    // instead of creating and returning the container rather just
    // take a container as parameter and generate the grid into it
    private VisualElement DrawGrid(VisualElement container)
    {
        // remove any previous content
        container.Clear();
    
        ...
    }
    

    so instead of creating the grid only once in CreateInspectorGUI you recreate it every time the size is modified, passing in the root container you want to place it in.