Defer GUI events in EditorWindow

Howdy!

Currently i’m trying to build an EditorWindow Solar System Editor. Turns out when you’re creating a solar system at actual scales things get a little dicey kinda quick when it comes to placing objects. So far I’ve been able to get the individual pieces working, now I just have to make it look good and be user friendly.

So here’s where I’m stuck:

I’ve got the window where the actual map editing takes place, I’ve got a scale bar, the scroll bars and all that. I’m currently using Handles.DrawLine to create a dynamic grid. I understand why the performance hit is happening, but I’ve been unable to devise a solution to fix the problem. OnGUI runs every frame, which is cool, that’s it’s job but it’s not cool when it’s expected behavior causes performance issues.

Basically what I want to do is defer or only cause the grid line drawing to happen when the scale changes. It doesn’t need to render EVERY frame, or it only needs to render when the scrollbars are moved, or the scale changed. I suspect that if I can implement a deferred system or whatever you want to call it the performance will go back to where it’s supposed to be.

I would say it’s the 2,000,000 x 2,000,000 pixel rect, but when the grid lines go away performance improves dramatically.

So, is there a way to have the grid only render when desired in OnGUI knowing that OnGUI is doing it’s thing every frame? Also, I know that the way the it’s written with the nested if statements causes some performance hits as well. The specific code is in the DrawMapGrid() function at line 134 and it’s called in the DrawMapEditor() function on line 127.

Anyway, here’s the code, and hopefully that all makes sense and someone can steer me in the right direction!

SystemForgeEditor

using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;

public class SystemForgeEditor : EditorWindow
{
    private Vector2 mousePos;
    private Vector2 mapCoord;   //This stores the pixel position of an object added to the map...
    private Vector2 sysCoord;   //This is the position of the planet relative to the center of the map in AU...

    //Map editor stuff...
    private int         mapScaleSliderValue     = 2;
    private int         mapScaleSliderMinValue  = 0;
    private int         mapScaleSLiderMaxValue  = 3;

    private int         mapSize                 = 200;          //au
    private int         scaledMapSize           = 0;            //pixels
    private int         mapEditorSize           = 800;          //pixels
    private float[]     scaleValues             = { 0.0001f,
                                                    0.001f,
                                                    0.01f,
                                                    0.1f };
    private float[]     gridLineSpacing         = { 0.01f,
                                                    0.1f,
                                                    1.0f,
                                                    10.0f };    //au
    private int         scaledGridLineSpacing   = 0;            //pixels

    //These are for defining  the sizes of labels, sliders, etc...all the cosmetic stuff
    private Vector2     labelSize               = new Vector2(256, 32);             //pixels
    private Vector2     mapScaleSliderSize      = new Vector2(800, 32);             //pixels


    private Vector2     mapEditorPos            = new Vector2(320, 16);
    private Vector2     mapScaleSliderPos       = new Vector2(320, 820);



    private Vector2     mapScrollBarPosition;

    private Rect        mapRect;               //The map is where planet placement takes place.
    private Rect        mapEditorRect;         //The map editor is a scrollable window that houses the map.
    private Rect        mapScaleSliderBarRect;

    Event currentEvent;

    List<Rect>          planetRectList     = new List<Rect>();
    List<GameObject>    planetObjectList   = new List<GameObject>();

    [MenuItem("Window/System Forge")]
    public static void ShowWindow()
    {
        SystemForgeEditor editor = (SystemForgeEditor)EditorWindow.GetWindow(typeof(SystemForgeEditor));
        editor.Init();
        editor.title            = "System Forge";
        editor.minSize          = new Vector2(1124, 840);
        editor.wantsMouseMove   = true;
    }

    void Init()
    {
        //The map editors scroll bars need to be initialized to the center of the map...
        scaledMapSize         = Mathf.RoundToInt(mapSize / scaleValues[mapScaleSliderValue]);
        scaledGridLineSpacing = Mathf.RoundToInt(gridLineSpacing[mapScaleSliderValue] / scaleValues[mapScaleSliderValue]);
        mapScrollBarPosition  = new Vector2((scaledMapSize / 2.0f) - (mapEditorSize / 2.0f), (scaledMapSize / 2.0f) - (mapEditorSize / 2.0f));
    }

    void OnGUI()
    {
        currentEvent = Event.current;

       if (currentEvent.type == EventType.MouseMove)
            Repaint();

        UpdateScaledValues();

        DrawMapEditor();

        MouseInMap();

        GUI.Label(new Rect(8, 8, labelSize.x, labelSize.y), "Map Scale Slider Value: " + mapScaleSliderValue.ToString());
        GUI.Label(new Rect(8, 28, labelSize.x, labelSize.y), "Editor Mouse Position: " + Event.current.mousePosition.ToString());
        GUI.Label(new Rect(8, 48, labelSize.x, labelSize.y), "Pixel Coord: " + mapCoord.ToString());
        GUI.Label(new Rect(8, 68, labelSize.x, labelSize.y), "System Coord in AU : " + new Vector2(sysCoord.x, sysCoord.y).ToString("0.####"));
        GUI.Label(new Rect(8, 88, labelSize.x, labelSize.y), "Scaled Map Size: " + scaledMapSize.ToString());
    }

    void MouseInMap()
    {
        mousePos = currentEvent.mousePosition;
        mapCoord = new Vector2(currentEvent.mousePosition.x + mapScrollBarPosition.x - mapEditorPos.x, currentEvent.mousePosition.y + mapScrollBarPosition.y - mapEditorPos.y);
        sysCoord = new Vector2((mapCoord.x - mapRect.center.x) * scaleValues[mapScaleSliderValue], (mapRect.center.y - mapCoord.y) * scaleValues[mapScaleSliderValue]);

        if (!mapEditorRect.Contains(mousePos))
        {
            mapCoord.x = 0;
            sysCoord.x = 0;
        }

        if (!mapEditorRect.Contains(mousePos))
        {
            mapCoord.y = 0;
            sysCoord.y = 0;
        }
    }

    void UpdateScaledValues()
    {
        scaledMapSize         = Mathf.RoundToInt(mapSize / scaleValues[mapScaleSliderValue]);
        scaledGridLineSpacing = Mathf.RoundToInt(gridLineSpacing[mapScaleSliderValue] / scaleValues[mapScaleSliderValue]);
    }

    void DrawMapEditor()
    {
        mapRect               = new Rect(new Vector2(0.0f, 0.0f), new Vector2(scaledMapSize, scaledMapSize));
        mapEditorRect         = new Rect(mapEditorPos, new Vector2(mapEditorSize, mapEditorSize));
        mapScaleSliderBarRect = new Rect(mapScaleSliderPos, mapScaleSliderSize);

        mapScrollBarPosition = GUI.BeginScrollView(mapEditorRect, mapScrollBarPosition, mapRect, true, true);

        GUI.Box(mapRect, "Map Editor");
        //Since the map has been created, draw the grid...
        DrawMapGrid();
  
        GUI.EndScrollView();

        mapScaleSliderValue = Mathf.RoundToInt(GUI.HorizontalSlider(mapScaleSliderBarRect, mapScaleSliderValue, mapScaleSliderMinValue, mapScaleSLiderMaxValue));
    }

    void DrawMapGrid()
    {
        int numberOfLines = Mathf.RoundToInt(scaledMapSize / scaledGridLineSpacing);

        for (int i = 1; i < numberOfLines; i++)
        {
            if ((i % 5) == 0)
                Handles.color = Color.red;
            else
                Handles.color = Color.grey;

            //Horizontal Lines
            Handles.DrawLine(new Vector3(mapRect.xMin, scaledGridLineSpacing * i, 0.0f), new Vector3(mapRect.xMax, scaledGridLineSpacing * i, 0.0f));
            //Vertical Lines
            Handles.DrawLine(new Vector3(scaledGridLineSpacing * i, mapRect.yMin, 0.0f), new Vector3(scaledGridLineSpacing * i, mapRect.yMax, 0.0f));
        }
    }


    /*public void Init()
{
    mapEditor = new Rect(mapEditorPos, mapEditorSize);

    map = new Rect(mapEditorPos, mapSize);

    lineTexture = new Texture2D(1, 1);
    lineTexture.SetPixel(0, 0, Color.grey);
    lineTexture.Apply();

    sunTexture = Resources.Load("sun_01") as Texture;

    mapScrollBarPosition = new Vector2((mapSize.x / 2.0f) - (mapEditorSize.x / 2.0f), (mapSize.y / 2.0f) - (mapEditorSize.y / 2.0f));
}*/


    /*evt = Event.current;

    mousePos = evt.mousePosition;
    mapCoord = new Vector2(evt.mousePosition.x + mapScrollBarPosition.x - mapEditorPos.x, evt.mousePosition.y + mapScrollBarPosition.y - mapEditorPos.y);
    sysCoord = new Vector2((mapCoord.x - (map.center.x - mapEditorPos.x)) * 0.01f, ((map.center.y - mapEditorPos.y) - mapCoord.y) * 0.01f);

    BeginWindows();

    MouseInMap();

    if(GUILayout.Button("Create System!", GUILayout.Width(312)))
    {
        Debug.Log("planet object #: " + planetRectList.Count);
        for (int i = 0; i < planetRectList.Count; i++)
        {
            planetObjectList.Add(GameObject.CreatePrimitive(PrimitiveType.Sphere));
            planetObjectList[i].name = "Planet " + i;
            planetObjectList[i].transform.position = new Vector3(planetRectList[i].position.x, 0.0f, planetRectList[i].position.y);
        }
    }

    if (Event.current.type == EventType.MouseMove || Event.current.type == EventType.MouseUp)
        Repaint();

    GUILayout.Label("Star Details", EditorStyles.boldLabel);
    //mass = EditorGUILayout.FloatField("Mass " + i + ": ", mass, GUILayout.Width(256));
    //radius = EditorGUILayout.FloatField("Radius " + i + ": ", radius, GUILayout.Width(256));
    //luminosity = EditorGUILayout.FloatField("Luminosity " + i + ": ", luminosity, GUILayout.Width(256));

    mapScrollBarPosition = GUI.BeginScrollView(mapEditor, mapScrollBarPosition, map, true, true);

    GUI.Box(map, "Map");
    DrawMapGrid();

    AddEditorObject(mapCoord.x + mapEditorPos.x - 16, mapCoord.y + mapEditorPos.y - 16);

    DrawObject();

    GUI.EndScrollView();

    EndWindows();

    EditorGUILayout.LabelField("Editor Mouse Position: ", Event.current.mousePosition.ToString());
    EditorGUILayout.LabelField("Pixel Coord: ", mapCoord.ToString());
    EditorGUILayout.LabelField("System Coord in AU : ", new Vector2(sysCoord.x, sysCoord.y).ToString("0.##"));
    EditorGUILayout.LabelField("Rect Object: ", planetRectList.Count.ToString());*/
    //}

    /*void MouseInMap()
    {
        if (!mapEditor.Contains(mousePos))
        {
            mapCoord.x  = 0;
            sysCoord.x = 0;
        }

        if (!mapEditor.Contains(mousePos))
        {
            mapCoord.y  = 0;
            sysCoord.y = 0;
        }
    }

    void AddEditorObject(float _x, float _z)
    {
        if (evt.type == EventType.mouseDown && evt.button == 0 && mapEditor.Contains(mousePos))
        {
            Debug.Log("True");

            Rect temp = new Rect(_x, _z, 32, 32);  
           
            planetRectList.Add(temp);

            GameObject systemObject = new GameObject("Object", typeof(SpriteRenderer));
            systemObject.transform.position = new Vector3(sysCoord.x * 0.01f, 0.0f, sysCoord.y * 0.01f);

            planetObjectList.Add(systemObject);
        }
    }

    void AddPlanetObject(float _x, float _z)
    {
        if (evt.type == EventType.MouseDown && mapEditor.Contains(mousePos))
        {
            Debug.Log("True");

            GameObject temp = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            temp.hideFlags = HideFlags.HideInHierarchy;
            temp.transform.position = new Vector3(_x * 0.01f, 0.0f, _z * 0.01f);

            planetObjectList.Add(temp);
        }
    }

    void DrawObject()
    {
        for (int i = 0; i < planetRectList.Count; i++)
        {
            GUI.DrawTexture(planetRectList[i], sunTexture);
        }
    }

    void DrawMapGrid()
    {
        map = new Rect(mapEditor.xMin, mapEditor.yMin, mapSize.x, mapSize.y);

        int numberOfLines = (int)(mapSize.x / gridLineSpacing);

        for (int i = 1; i < numberOfLines; i++)
        {
            if ((i % 5) == 0)
                Handles.color = Color.red;
            else
                Handles.color = Color.grey;

            //Horizontal Lines
            Handles.DrawLine(new Vector3(map.xMin, mapEditor.position.y + (gridLineSpacing * i), 0.0f), new Vector3(map.xMax, mapEditor.position.y + (gridLineSpacing * i), 0.0f));
            //Vertical Lines
            Handles.DrawLine(new Vector3(mapEditor.position.x + (gridLineSpacing * i), map.yMin, 0.0f), new Vector3(mapEditor.position.x + (gridLineSpacing * i), map.yMax, 0.0f));
        }
    }*/
}

Also, here’s a screen shot of what it looks like at the moment.

Eh, so I’ve decided I’m dumb. I went back and re-ran my excel book scale numbers and figured out i’m drawing 40,000 lines at that small scale…so the solution was to adjust the scale of the lines. So now, at the 0.0001 AU per pixel point, the grids are 0.1 AU per box, which means only 4,000 lines are drawn in total, and performance is a-ok. shrugs turns out it was a whole lot simpler to just not get so finicky about the scale then try a work around…