You need to enable JavaScript to run this app. Unity自定义Editor GUI开发:如何在OnGUI函数外获取鼠标位置?
优惠活动
大模型
产品
解决方案
定价
更多

Unity自定义Editor GUI开发:如何在OnGUI函数外获取鼠标位置?

Unity自定义编辑器OnGUI外逐帧获取鼠标位置的实现方案

你遇到的核心限制本质是:Event.current是Unity在OnGUI管线启动时才注入的事件上下文对象,脱离OnGUI的调用生命周期自然无法直接读取。你要做的悬停状态统一检测逻辑,完全可以通过低性能损耗的方式落地,不需要依赖每帧强制重绘:


方案核心:OnGUI阶段缓存鼠标坐标,跨阶段复用

这是最匹配你现有“在EditorApplication.update统一做悬停检测”架构的实现,没有额外性能开销:

  • 在你的EditorWindow类中定义全局缓存字段,用来存跨阶段复用的数据
    // 缓存当前GUI坐标系下的鼠标位置
    private Vector2 _cachedGuiMousePos;
    // 缓存当前窗口的全局根矩形,用于坐标换算
    private Rect _currentWindowRect;
    // 存所有可悬停元素的矩形和历史悬停状态
    private readonly Dictionary<Rect, bool> _hoverableElementStateMap = new Dictionary<Rect, bool>();
    
  • 在OnGUI方法的最开头(所有自定义GUI绘制逻辑之前)同步缓存数据,不需要等任何按钮事件触发:
    private void OnGUI()
    {
        // 只要当前事件是和鼠标、绘制、布局相关的,就同步最新的鼠标位置
        if (Event.current.isMouse 
            || Event.current.type == EventType.Repaint 
            || Event.current.type == EventType.Layout)
        {
            _cachedGuiMousePos = Event.current.mousePosition;
        }
        _currentWindowRect = position;
    
        // 后续所有原来的按钮、自定义GUI绘制逻辑保持不变
    }
    
  • 在EditorApplication.update回调中执行统一的悬停状态检测,只有状态真正变化时才触发精准重绘,完全避免无效重绘开销:
    private void OnEnable()
    {
        // 挂载全局update回调
        EditorApplication.update += OnEditorTick;
    }
    
    private void OnDisable()
    {
        // 退出时解绑,避免内存泄漏
        EditorApplication.update -= OnEditorTick;
    }
    
    private void OnEditorTick()
    {
        bool needRepaint = false;
        // 遍历所有你注册的可悬停元素矩形
        foreach (var element in _hoverableElementStateMap.Keys)
        {
            // 用缓存的鼠标位置做碰撞检测,不需要依赖Event.current
            bool newHoverState = element.Contains(_cachedGuiMousePos);
            // 对比历史状态,只有真的变化了才标记需要重绘
            if (newHoverState != _hoverableElementStateMap[element])
            {
                _hoverableElementStateMap[element] = newHoverState;
                needRepaint = true;
            }
        }
        // 仅当状态真的变化时才重绘,比每帧强制重绘的性能损耗低两个数量级
        if (needRepaint)
        {
            Repaint();
        }
    }
    

常见坑点提醒

  • 不要在OnGUI外直接读取Event.current:这个对象是Unity在OnGUI管线初始化时才会实例化的,在update阶段调用会拿到空值或者上一帧的残留事件,反而会引入逻辑bug和新的延迟。
  • 如果你需要获取屏幕坐标系下的鼠标位置(比如判断鼠标是否移出当前窗口),可以直接用GUIUtility.GUIToScreenPoint(_cachedGuiMousePos)做坐标转换,这个API不需要依赖OnGUI的事件上下文。
  • 这套方案为什么能解决之前的延迟问题?因为你把悬停检测从原来的“等OnGUI收到鼠标事件才执行”改成了“逐帧检测所有元素的状态,变化时才主动触发重绘”,完全不会出现鼠标移到按钮上、因为窗口没收到事件而等很久才变色的问题。

内容的提问来源于stack exchange,提问作者GameDevNerd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.07 03:06:10