已有桌面版拖拽代码,如何适配OVR Ray Interactable实现Canvas UI的VR拖拽?
Oculus VR场景下Canvas UI拖拽适配方案
核心问题分析
你的桌面端代码依赖鼠标输入和IPointerDown/UpHandler接口,但VR场景中控制器射线交互需要适配Meta的交互体系:
- 需替换默认的
StandaloneInputModule为VR专用输入模块 - 不能再用
Input.mousePosition,要通过控制器射线计算与Canvas的交点 - 改用VR交互事件替代鼠标点击事件
适配代码(基于Meta Oculus Integration SDK)
using UnityEngine; using Oculus.Interaction; public class TestAsVR : MonoBehaviour { private bool isDragging = false; private RectTransform rectTransform; private Canvas canvas; private Node node; private OVRRayInteractor rayInteractor; void Awake() { // 缓存组件避免重复调用GetComponent rectTransform = GetComponent<RectTransform>(); canvas = GetComponentInParent<Canvas>(); node = GetComponent<Node>(); // 找到场景中的控制器射线交互器(可改为指定左右手) rayInteractor = FindObjectOfType<OVRRayInteractor>(); // 绑定交互事件 var interactable = GetComponent<OVRInteractable>(); if (interactable != null) { interactable.WhenSelectEntered += OnDragStart; interactable.WhenSelectExited += OnDragEnd; } } private void OnDragStart(InteractorArgs args) { if (node.isHighlighting && gameObject.CompareTag("NodeUI")) { isDragging = true; node.isMoving = true; } } private void OnDragEnd(InteractorArgs args) { if (gameObject.CompareTag("NodeUI")) { isDragging = false; node.isMoving = false; // 更新子节点状态 int childCount = rectTransform.childCount; int startIndex = Mathf.Max(0, childCount - 2); for (int i = startIndex; i < childCount; i++) { var point = rectTransform.GetChild(i).GetComponent<PointEventHandler>(); if (point != null && point.isLinked) { point.isMoved = false; } } } } void Update() { if (isDragging && rayInteractor != null) { // 获取射线与Canvas的交点 if (rayInteractor.TryGetHitInfo(out RaycastHit hitInfo)) { // 将世界坐标转换为Canvas本地锚点坐标 Vector2 localPos; RectTransformUtility.ScreenPointToLocalPointInRectangle( rectTransform.parent as RectTransform, RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, hitInfo.point), canvas.worldCamera, out localPos); rectTransform.anchoredPosition = localPos; // 更新子节点状态 int childCount = rectTransform.childCount; int startIndex = Mathf.Max(0, childCount - 2); for (int i = startIndex; i < childCount; i++) { var point = rectTransform.GetChild(i).GetComponent<PointEventHandler>(); if (point != null && point.isLinked) { point.isMoved = true; } } } } } private void OnDestroy() { // 解绑事件避免内存泄漏 var interactable = GetComponent<OVRInteractable>(); if (interactable != null) { interactable.WhenSelectEntered -= OnDragStart; interactable.WhenSelectExited -= OnDragEnd; } } }
必要场景设置
Canvas配置
- 将Canvas的渲染模式改为
World Space,调整合适的大小和位置,确保控制器射线能照射到 - 确认Canvas的
World Camera设置为VR场景的主相机
- 将Canvas的渲染模式改为
交互组件配置
- 给UI元素添加
OVRInteractable组件,勾选"UI Interactable"选项 - 给控制器添加
OVRRayInteractor组件,开启射线可视化(Gizmos)便于调试
- 给UI元素添加
EventSystem替换
- 删除场景中
EventSystem上的StandaloneInputModule,添加OVRInputModule(来自Oculus Integration包)
- 删除场景中
常见问题排查
- 若射线无法命中UI:检查Canvas的碰撞体是否开启,射线交互器的交互层是否包含UI层
- 若拖拽无反应:确认
Node.isHighlighting逻辑在VR场景中正常触发(比如射线悬停时设置为true) - 若位置偏移:检查Canvas的锚点和 pivot 设置,确保坐标转换逻辑正确
内容的提问来源于stack exchange,提问作者YongseoKim
相关产品推荐
相关产品推荐

