You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity虚拟鼠标可操作UI,但无法触发2D碰撞体的OnMouse系列事件

解决方案:虚拟鼠标无法触发2D物体OnMouse事件的问题

核心原因

OnMouseEnter/OnMouseDown是旧输入系统的事件,依赖Unity原生硬件鼠标的输入状态,新输入系统的虚拟鼠标不会自动触发这些旧事件。以下是针对性的解决方法:


方法1:迁移到新输入系统的射线检测(推荐)

直接用新输入系统的API结合Physics2D.Raycast实现交互逻辑,完全摆脱旧事件的限制:

步骤1:配置输入动作资产

创建输入动作资产,添加两个动作:

  • MousePosition:类型为Value -> Vector2,绑定虚拟鼠标的位置输出
  • LeftClick:类型为Button,绑定控制器的交互按键(比如Xbox的A键)

步骤2:替换原有脚本

将你的OnMouse事件脚本替换为以下代码:

using UnityEngine;
using UnityEngine.InputSystem;

public class Object2DInteractor : MonoBehaviour
{
    [SerializeField] private InputActionReference mousePositionAction;
    [SerializeField] private InputActionReference leftClickAction;
    
    private Camera _mainCamera;
    private bool _isCursorOverObject;

    private void Awake()
    {
        _mainCamera = Camera.main;
        leftClickAction.action.performed += OnLeftClick;
    }

    private void OnEnable()
    {
        mousePositionAction.action.Enable();
        leftClickAction.action.Enable();
    }

    private void OnDisable()
    {
        mousePositionAction.action.Disable();
        leftClickAction.action.Disable();
        leftClickAction.action.performed -= OnLeftClick;
    }

    private void Update()
    {
        // 检测光标进入/离开
        Vector2 screenPos = mousePositionAction.action.ReadValue<Vector2>();
        RaycastHit2D hit = Physics2D.Raycast(_mainCamera.ScreenToWorldPoint(screenPos), Vector2.zero);

        bool isOverCurrentObject = hit.collider != null && hit.collider.gameObject == gameObject;
        
        if (isOverCurrentObject && !_isCursorOverObject)
        {
            _isCursorOverObject = true;
            Debug.Log("Cursor entered object"); // 替代OnMouseEnter
        }
        else if (!isOverCurrentObject && _isCursorOverObject)
        {
            _isCursorOverObject = false;
            Debug.Log("Cursor exited object"); // 可添加离开逻辑
        }
    }

    private void OnLeftClick(InputAction.CallbackContext context)
    {
        // 检测点击当前物体
        Vector2 screenPos = mousePositionAction.action.ReadValue<Vector2>();
        RaycastHit2D hit = Physics2D.Raycast(_mainCamera.ScreenToWorldPoint(screenPos), Vector2.zero);

        if (hit.collider != null && hit.collider.gameObject == gameObject)
        {
            Debug.Log("Object clicked"); // 替代OnMouseDown
        }
    }
}

方法2:强制同步虚拟鼠标到旧输入系统(不推荐)

如果必须保留OnMouse事件,可以尝试将虚拟鼠标的输入同步到旧输入系统的状态,但注意这种方法依赖Unity未公开API,可能在版本更新中失效:

using UnityEngine;
using UnityEngine.InputSystem;
using System.Reflection;

public class VirtualMouseSync : MonoBehaviour
{
    [SerializeField] private InputActionReference virtualMousePos;
    [SerializeField] private InputActionReference virtualLeftClick;

    private FieldInfo _mousePositionField;
    private FieldInfo _mouseButtonField;

    private void Awake()
    {
        // 反射获取旧输入系统的私有字段
        _mousePositionField = typeof(Input).GetField("mousePosition", BindingFlags.Static | BindingFlags.NonPublic);
        _mouseButtonField = typeof(Input).GetField("mouseButtonStates", BindingFlags.Static | BindingFlags.NonPublic);
    }

    private void Update()
    {
        // 同步位置
        Vector2 pos = virtualMousePos.action.ReadValue<Vector2>();
        _mousePositionField?.SetValue(null, pos);

        // 同步左键点击
        bool isPressed = virtualLeftClick.action.IsPressed();
        if (_mouseButtonField != null)
        {
            byte[] buttonStates = (byte[])_mouseButtonField.GetValue(null);
            buttonStates[0] = isPressed ? (byte)1 : (byte)0;
            _mouseButtonField.SetValue(null, buttonStates);
        }
    }
}

额外检查项

  • 确认虚拟鼠标的Position动作输出的是屏幕空间坐标(0到屏幕宽高)
  • 2D物体的碰撞体必须启用,且EventMask(如果用EventSystem)包含物体所在层
  • 软件光标模式下,确保虚拟鼠标的位置没有被Canvas或其他UI元素遮挡(UI交互正常不代表2D物体射线检测正常)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 09:58:19