1

I am working on a Unity tool and I want to create a custom GUI for it.

In this simple script, I am checking if the mouse has hovered over a button and if so I change a GUI color. This is just a really basic example, but the same principles apply to something that I want to make.

The problem is that the change is really delayed. I tried implementing isDirty state, where it only repaints when it needs to. Although it doesn't seem to work. Same delay issue... I could just repaint the window every frame, but that is really non-performant.

private bool isDirty = false;

Rect _hoveredRect;
Rect HoveredRect
{
    get { return _hoveredRect; }
    set
    {
        if (_hoveredRect != value)
        {
            isDirty = true;
        }
        _hoveredRect = value;
    }
}

void Update()
{
    if (isDirty)
    {
        Repaint();
        isDirty = false;
    }
}
void OnGUI()
{
    Rect button = new Rect(25, 25, 100, 35);
    DrawButton(button, "Label");
}
bool DrawButton(Rect rect, string label)
{
    var e = Event.current;
    bool hovered = rect.Contains(e.mousePosition);

    if (hovered)
    {
        HoveredRect = rect;
    }
    if (!hovered && HoveredRect == rect)
    {
        HoveredRect = Rect.zero;
    }

    var defaultColor = GUI.color;

    GUI.color = hovered ? Color.red : defaultColor;

    bool pressed = GUI.Button(rect, label);

    GUI.color = defaultColor;

    return pressed;
}

Then I came up with another solution, which should work, but I need to get mouse position and I can't use Event.current outside of OnGUI function.

Dictionary<Rect, bool> hoverableRects = new Dictionary<Rect, bool>();

private void OnEnable()
{
    EditorApplication.update += UpdateMe;
}
private void OnDisable()
{
    EditorApplication.update -= UpdateMe;
}

void UpdateMe()
{
    var mousePos = ??; List<Rect> rects = new List<Rect>(hoverableRects.Keys);
    foreach (var rect in rects)
    {
        hoverableRects[rect] = rect.Contains(mousePos);
    }
}
void OnGUI()
{
    Rect button = new Rect(25, 25, 100, 35);
    DrawButton(button, "Label");
}
bool DrawButton(Rect rect, string label)
{
    if (!hoverableRects.ContainsKey(rect))
    { hoverableRects.Add(rect, false); }

    var defaultColor = GUI.color;

    GUI.color = hoverableRects[rect] ? Color.red : defaultColor;

    bool pressed = GUI.Button(rect, label);

    GUI.color = defaultColor;

    return pressed;
}

Is there a way to get mouse position every frame outside the OnGUI method?

  • Does this answer your question? [Unity - Custom Editor - data refresh](https://stackoverflow.com/questions/63684241/unity-custom-editor-data-refresh) – Ruzihm Jan 28 '21 at 21:55
  • No. I mentioned this approach in my question. I could Repaint the window every frame, but that would destroy the performance – GameDevNerd Jan 28 '21 at 21:58
  • As mentioned in my answer there `OnInspectorUpdate` isn't called every frame .. additionally you can lower the performance impact slightly again by only check the one rect you are currently hovering and only check all rects once you left the current one – derHugo Jan 29 '21 at 07:54

1 Answers1

0

So when you say it's slow. Do you mean the colour change is slow to react, or the actual colour changing process is slow?...

Because if your colour is slow to initially change. Like your cursor is there but it's not doing anything that's because you're not actually checking that the cursor is over the button every frame, or whatever...

You might have if ('isDirty'?) in the update, which you do... But, the method that calculates it is NOT in the update, meaning 'isDirty' is being updated less than once a frame.