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
相关产品推荐
相关产品推荐

