【问题标题】:Balancing calls inside of a EditorWindow OnGUI method causing problems在 EditorWindow OnGUI 方法中平衡调用会导致问题
【发布时间】:2019-10-11 10:06:35
【问题描述】:

我正在制作一个系统来平衡 EditorWindow 的 OnGUI 方法内部的调用。 我正在执行以下操作:

        public void Update()
        {
            Repaint();
        }

在我的 OnGUI 方法中,我调用了这个 Balancer。我有一个带有回调的列表(列表)。 所以思路很简单,调用vaxc 我正在为具有完整 GUI 的回调跳过一些重绘帧,并为其他回调调用每次重绘(例如,选取框标签或显示 gif)。

由于某种原因,此错误发生“在重新绘制时获取控件 0 在只有 0 个控件的组中的位置”

        private int m_repaintCounter;

        public void Draw()
        {
            Event e = Event.current;
            try
            {
                foreach (var action in m_actions)
                {
                    try
                    {

                            // Test 1
                            // MainAction is a class that inherits from Action (class MainAction : Action)
                            if (action is MainAction)
                            {
                                bool isDesignedType = e.rawType == EventType.Repaint || e.rawType == EventType.Layout;

                                if (isDesignedType)
                                    ++m_repaintCounter;

                                if (!(m_repaintCounter == 20 && isDesignedType))
                                    continue;
                                else
                                    m_repaintCounter = 0;
                            }

                            // Test 2
                            action.Value();
                    }
                    catch (Exception ex)
                    {
                        Debug.LogException(ex);
                    }
                }
            }
            catch
            {
                // Due to recompile the collection will modified, so we need to avoid the exception
            }
        }

但如果我评论“测试 1”,一切正常。

在类的ctor上我们需要指定一个GUI方法的回调,例如:

        public Balancer(Action drawAction)
        {
            m_actions = new List<Action>();
            m_actions.Add(drawAction);
        }

所以我们可以轻松做到(在EditorWindow 内):

    private Balancer m_balancer;

    public void OnEnable() 
    {
        m_balancer = new Balancer(Draw);
    }

    public void Draw() 
    {
        // This block will be called every 20 repaints as specified on the if statment
        GUILayout.BeginHorizontal("box");
        {
            GUILayout.Button("I'm the first button");
            GUILayout.Button("I'm to the right");

            // This marquee will be called on each repaint
            m_balancer.AddAction(() => CustomClass.DisplayMarquee("example"));
        }
        GUILayout.EndHorizontal();
    }
// Inside of the Balancer class we have
// We use System.Linq.Expressions to identify actions that were added already

private HashSet<string> m_alreadyAddedActions = new HashSet<string>();

public void AddAction(Expression<Action> callback) 
{
    if(!m_alreadyAddedActions.Add(callback.ToString()))
        return;

    m_actions.Add(callback.Compile());
}

我想不通。我在互联网上找不到任何信息。谁能帮帮我?

【问题讨论】:

    标签: c# user-interface unity3d balance


    【解决方案1】:

    好吧,OnGui (IMGui) 很难用。如果您不将其用于编辑器脚本,请改用新的 4.6 UI (UGui)。

    那么现在。问题。

    OnGui 被称为至少twice every frame。其中一个是计算布局,另一个是实际绘制东西(“重绘”)。

    如果这两个调用之间事物的数量、事物的大小或其他任何东西发生了变化,那么 Unity 将出错,并显示“在重绘时获取控件 0 在只有 0 个控件的组中的位置”。

    即:在Layout 之后和Repaint 之前,您无法在任何时候更改IMGUI 系统中的UI 状态。

    仅,仅,Event.current == EventType.Repaint期间更改状态(以及正在绘制的对象),并且仅, 更改下一帧的状态(或者,在Event.current == EventType.Layout 期间进行更改,前提是相同的新状态将在重绘期间产生相同的代码路径)。在任何情况下,都不要在重绘期间进行先前布局期间不存在的更改。

    【讨论】:

    • 我以前读过这个。我知道这是为什么造成的...问题比更改“事物的数量,事物的大小或其他任何东西”更糟糕,因为我没有做这样的事情...问题是清楚,如果我评论“测试 1”块,那么只有“action.Value();”被调用,一切正常......所以我不知道这里到底发生了什么。
    • continue; 导致action.Value() 未被调用。这取决于m_repaintCounterisDesignedType。让我们假设计数器为 0,我们为 Layout 调用一次。 isDesignedType 为真,它递增 m_repaintCounter。作为m_repaintCounter != 20,调用continue。这将成对重复(奇数是布局,偶数是重绘)。因此,当 m_repaintCounter 增加到 19(布局)时,将调用 continue。当 m_repaintCounter 增加到 20(以下重绘)时,continue调用。
    • action.Value() is 在第 20 次迭代中调用时,它会调用一个函数 Draw(),该函数调用 GuiLayout 以向 GUI 添加一些内容。这些在之前的布局(第 19 次迭代)中不存在:现在出现了问题。您的状态取决于在 Layout 和 Repaint 之间发生变化的值。
    • 现在我明白了。因此,当达到 Layout 时,我需要等待 Repaint 被调用。谢谢!让我测试一下。
    • 布局和下面的重绘必须总是做同样的事情。这是一个令人讨厌、令人沮丧、无情的系统。有时您只想从“这组控件”切换到“那组控件”,而导致它切换的东西并不是基于用户输入。你只需要接受你必须跳过状态更改并等待下一次通过。我写了几十个if(Event.current != Layout) { skip! } else { change_state() }或类似的东西。
    猜你喜欢
    • 1970-01-01
    • 2019-05-04
    • 1970-01-01
    • 2011-04-13
    • 1970-01-01
    • 2016-11-27
    • 2021-06-23
    • 2019-08-09
    • 1970-01-01
    相关资源
    最近更新 更多