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

Unity VR开发中如何通过VR控制器触发UI Button的onClick()事件

Unity VR 控制器主键触发UI Button onClick事件实现方案

以下方案基于目前Unity官方推荐的OpenXR + XR Interaction Toolkit(2.0及以上版本)实现,兼容绝大多数主流VR头显,不需要额外引入第三方SDK。

前置配置先做对,不然写了代码也跑不通

  • 项目先导入XR Plugin Management、OpenXR、XR Interaction Toolkit三个官方包,完成XR运行时配置,给左右控制器挂好对应的XR Controller组件。
  • 场景里的EventSystem删除默认的Standalone Input Module,换成XR UI Input Module;所有需要交互的UI Canvas把Render Mode改成World Space,挂上Tracked Device Graphics Raycaster组件,这是VR下UI能被射线识别的基础。
  • 默认OpenXR输入映射里,左右控制器的主键(比如Quest手柄的X/A键、Index手柄的对应主键)已经绑定到Primary Button输入动作,不需要自己重新做输入绑定。

最推荐方案:零侵入实现,不改动现有Button的事件绑定

这个方案不需要你改之前给Button.onClick绑的任何逻辑——不管是Inspector里拖的方法,还是代码里动态加的监听,都能正常触发,和鼠标点击的效果完全一致。核心逻辑就是:主键按下时,检测当前控制器的UI射线是不是悬停在可交互的Button上,如果是就手动触发按钮的点击事件。
把下面的脚本挂到左右控制器对象上:

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.UI;
using UnityEngine.XR.Interaction.Toolkit;

public class VRPrimaryButtonUI : MonoBehaviour
{
    [Header("把对应组件拖到这几个字段里")]
    [SerializeField] private XRRayInteractor uiRayInteractor;
    [SerializeField] private InputActionReference primaryButtonAction;

    private void OnEnable()
    {
        primaryButtonAction.action.performed += TriggerButtonClick;
    }

    private void OnDisable()
    {
        primaryButtonAction.action.performed -= TriggerButtonClick;
    }

    private void TriggerButtonClick(InputAction.CallbackContext context)
    {
        // 检测当前射线是否命中UI元素
        if (!uiRayInteractor.TryGetCurrentUIRaycastResult(out var uiHit)) return;
        // 从命中的对象往上找对应的Button组件,适配按钮上挂了子物体文本、图标的情况
        Button targetButton = uiHit.gameObject.GetComponentInParent<Button>();
        // 只有按钮存在且处于可交互状态才触发
        if (targetButton != null && targetButton.interactable)
        {
            targetButton.onClick.Invoke();
            // 可选:触发按钮的点击状态反馈,和鼠标点击的视觉效果一致
            targetButton.OnPointerClick(new PointerEventData(EventSystem.current));
        }
    }
}

脚本挂好之后的配置步骤:

  • 左右控制器各挂一份脚本,把控制器自身挂载的、用来做UI交互的XR Ray Interactor拖到uiRayInteractor字段
  • primaryButtonAction字段分别选对应左右手的Primary Button输入动作即可

简化场景方案:不需要射线悬停,按键直接触发固定按钮

如果你的需求是不管射线指在哪,按下主键就固定触发某个按钮(比如全局确认键),直接用更简单的逻辑就行,不需要做射线检测:

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;

public class FixedButtonBind : MonoBehaviour
{
    [SerializeField] private Button targetButton;
    [SerializeField] private InputActionReference primaryButtonAction;

    private void OnEnable()
    {
        primaryButtonAction.action.performed += _ =>
        {
            if (targetButton.interactable) targetButton.onClick.Invoke();
        };
    }

    private void OnDisable()
    {
        primaryButtonAction.action.performed -= _ =>
        {
            if (targetButton.interactable) targetButton.onClick.Invoke();
        };
    }
}

几个容易踩的坑

  • 不要直接在输入回调里写业务逻辑,一定要走Button的onClick.Invoke()调用,这样按钮的禁用状态、事件触发顺序、点击反馈都会和正常点击保持一致,不会出现按钮灰了按了还触发逻辑的问题。
  • 如果项目用的是SteamVR SDK,只要把输入检测的部分换成SteamVR对应的按键按下判定即可,核心的Button查找、onClick触发逻辑完全通用。
  • 检查Tracked Device Graphics Raycaster的事件遮罩设置,不要把UI所在的层给屏蔽了,不然射线检测不到按钮。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 13:06:30