第一部分:XRI 配置与多平台 SDK 适配

1.1 环境准备

Unity 版本推荐

  • Unity 2022.3 LTS 或更高版本(推荐 2022.3 LTS)
  • XRI 建议使用 2.5.x 或更高版本

必装 Package

通过 Window → Package Manager 安装以下包:

com.unity.xr.interaction.toolkit    # XRI 核心包
com.unity.xr.management             # XR 插件管理
com.unity.inputsystem               # 新版输入系统(XRI 依赖)

安装 XRI 时,记得在 Package Manager 中展开 Samples,导入 Starter AssetsXR Device Simulator,这两个示例资源非常实用包含一些初始的示例配置  和XR 模拟器 无需打

包出来 在unity 即可测试 交互效果


1.2 XR Plug-in Management 配置

进入 Edit → Project Settings → XR Plug-in Management,这是多平台适配的核心入口。

注意Quest 不需要额外导入XDK 即可 运行 因为Unity的XRI 默认适配Quest  PICO 的话SDK(在PICO 官网下载并且通过unity )导入XXXX,json 的形式导入SDK

Android 平台配置
目标设备 勾选的 Plug-in Provider
Meta Quest 系列 Oculus
PICO 系列 PICO XR   
PC/Standalone 平台配置
目标设备 勾选的 Plug-in Provider
Oculus Rift/Link Oculus
PICO PICO 官网下载串流插件

1.3 Meta Quest 适配配置

Step 1:安装 Oculus XR Plugin
Package Manager → Unity Registry → Oculus XR Plugin
Step 2:XR Plug-in Management 设置
Edit → Project Settings → XR Plug-in Management → Android Tab

1.4 PICO 适配配置

Step 1:下载 PICO Unity Integration SDK

从 PICO 开发者平台下载:https://developer-global.pico-interactive.com/

下载后得到 .unitypackage 文件,直接导入项目。

SDK 版本建议 2.3.x 或更高,与 XRI 2.5+ 配合较好。

Step 2:XR Plug-in Management 设置
Edit → Project Settings → XR Plug-in Management → Android Tab
 勾选 PICO XR
 取消勾选 Oculus

1.5 关键差异点对比

配置项 Meta Quest PICO
XR Plugin Oculus XR Plugin PICO Unity Integration SDK
SDK 来源 Package Manager 官网下载 unitypackage
Stereo Rendering Multiview Multiview
推荐 Graphics API Vulkan Vulkan
手柄震动 API OVRInput.SetControllerVibration PXR_Input.SetControllerVibration
Passthrough API OVRPassthrough PXR_MixedReality
手势追踪 OVRHand PXR_HandTracking

这里Meta Quest 的SDK 还有一个 Meta quest 的交互SDK  如果使用 这个sdk ,但它们之间并不完全兼容。但都是可用的。 Meta quest 的交互SDK 的就是另外一套配置和使用流程了。

第二部分:XRI 常用交互组件详解

2.1 核心架构概览

XRI 的交互体系基于三个核心概念:

┌─────────────────────────────────────────────────────────┐
│                  XR Interaction Manager                 │
│              (全局交互管理器,必须存在)                  │
└─────────────────────────────────────────────────────────┘
                            │
            ┌───────────────┼───────────────┐
            ▼               ▼               ▼
    ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
    │  Interactor  │ │  Interactor  │ │  Interactor  │
    │   (交互者)    │ │   (交互者)    │ │   (交互者)    │
    │  左手控制器   │ │  右手控制器   │ │   射线/手势   │
    └──────────────┘ └──────────────┘ └──────────────┘
            │               │               │
            └───────────────┼───────────────┘
                            ▼
    ┌─────────────────────────────────────────────────────┐
    │                  Interactable                       │
    │                   (可交互对象)                        │
    │          可抓取物体 / UI / 传送区域 / Socket          │
    └─────────────────────────────────────────────────────┘

2.2 XR Origin(XR 相机系统)

作用:定义 XR 空间的原点、相机、控制器的层级结构。

Hierarchy 结构

XR Origin
├── Camera Offset
│   ├── Main Camera(头显相机)
│   ├── LeftHand Controller(左手控制器)
│   └── RightHand Controller(右手控制器)
└── Locomotion System(可选,传送/移动系统)

XR Origin 组件属性

属性 说明
Camera Floor Offset Object 相机偏移对象(Camera Offset)
Camera 主相机引用
Tracking Origin Mode Floor(地面追踪)/ Device(设备追踪)
Camera Y Offset Device 模式下的相机高度偏移

2.3 XR Controller(控制器组件)

作用:桥接物理手柄与 XRI 交互系统。

组件构成(一个完整的控制器对象):

LeftHand Controller
├── XR Controller (Action-based)    # 控制器核心组件
├── XR Direct Interactor            # 直接抓取
├── XR Ray Interactor               # 射线交互
└── XR Interaction Group            # 交互组(管理多个 Interactor)

XR Controller 关键属性

属性分类 属性名 说明
Tracking Position/Rotation Action 位置/旋转追踪输入
Input Select Action 选择/抓取按键(通常是 Grip)
Input Activate Action 激活按键(通常是 Trigger)
Input UI Press Action UI 点击按键
Haptics Haptic Device Action 震动反馈输出

Action 绑定示例(使用 XRI Default Input Actions)(xri自带示例绑定直接用示例即可):

Position Action → XRI LeftHand/Position
Rotation Action → XRI LeftHand/Rotation
Select Action → XRI LeftHand/Select
Activate Action → XRI LeftHand/Activate

代码控制震动

using UnityEngine.XR.Interaction.Toolkit;

public class HapticFeedback : MonoBehaviour
{
    [SerializeField] private XRBaseController controller;
    
    public void TriggerHaptic(float amplitude, float duration)
    {
        // amplitude: 0-1, duration: 秒
        controller.SendHapticImpulse(amplitude, duration);
    }
}

2.4 抓取系统(Grab Interaction)

抓取是 VR 最核心的交互,XRI 提供了两种抓取方式。

2.4.1 XR Direct Interactor(直接抓取)

适用场景:手部/控制器直接接触物体进行抓取。

配置步骤

  1. 在控制器对象上添加 XR Direct Interactor
  2. 添加一个 Collider(通常是 Sphere Collider)并勾选 Is Trigger
  3. 在可抓取物体上添加 XR Grab Interactable

XR Direct Interactor 属性

属性 说明
Interaction Manager 引用场景中的 XR Interaction Manager
Interaction Layer Mask 交互层级筛选
Attach Transform 抓取时的吸附点
Select Action Trigger State(按住)/ State Change(按一下)
2.4.2 XR Ray Interactor(射线抓取)

适用场景:远距离选择和抓取物体。

配置步骤

  1. 在控制器对象上添加 XR Ray Interactor
  2. 添加 XR Interactor Line Visual(可视化射线)
  3. 添加 Line Renderer(渲染射线)

XR Ray Interactor 关键属性

属性 说明
Max Raycast Distance 射线最大长度
Raycast Mask 射线检测层级
Hit Detection Type Raycast(直线)/ Sphere Cast / Cone Cast
Hover To Select 悬停自动选择
Anchor Control 射线命中后可旋转/平移目标
Line Type Straight(直线)/ Projectile(抛物线)/ Bezier
2.4.3 XR Grab Interactable(可抓取对象)

必要组件

Grabbable Object
├── XR Grab Interactable    # 可抓取组件
├── Rigidbody               # 刚体(必须)
└── Collider                # 碰撞体(必须)

关键属性

属性 说明
Interaction Manager 交互管理器引用
Interaction Layer Mask 可被哪些层级的 Interactor 交互
Movement Type 抓取时的移动方式
Track Position/Rotation 是否跟踪位置/旋转
Throw On Detach 释放时是否有抛出效果
Attach Transform 自定义抓取吸附点

Movement Type 详解

类型 说明 适用场景
Instantaneous 瞬间跟随,无物理 UI 元素、道具
Kinematic 运动学移动,穿透碰撞 大多数可抓取物体
Velocity Tracking 速度追踪,有物理碰撞 需要物理交互的物体

代码监听抓取事件

using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;

public class GrabEventHandler : MonoBehaviour
{
    private XRGrabInteractable grabInteractable;
    
    void Awake()
    {
        grabInteractable = GetComponent<XRGrabInteractable>();
        
        // 注册事件
        grabInteractable.selectEntered.AddListener(OnGrabbed);
        grabInteractable.selectExited.AddListener(OnReleased);
        grabInteractable.activated.AddListener(OnActivated);
    }
    
    private void OnGrabbed(SelectEnterEventArgs args)
    {
        Debug.Log($"被 {args.interactorObject.transform.name} 抓取");
        
        // 获取抓取的控制器
        XRBaseInteractor interactor = args.interactorObject as XRBaseInteractor;
    }
    
    private void OnReleased(SelectExitEventArgs args)
    {
        Debug.Log("被释放");
    }
    
    private void OnActivated(ActivateEventArgs args)
    {
        // 抓取状态下按 Trigger 触发
        Debug.Log("被激活(如:开枪)");
    }
}

2.5 UI 交互(XR UI)

XRI 提供了完整的 VR/AR UI 交互方案,支持射线点击和直接触碰。

2.5.1 场景配置

必要组件

场景中需要:
├── EventSystem
│   └── XR UI Input Module(替换默认的 Input Module)
│
└── Canvas
    ├── Canvas 组件(Render Mode: World Space)
    ├── Tracked Device Graphic Raycaster(替换默认 Raycaster)
    └── UI 元素(Button, Slider 等)

配置步骤

  1. 创建 Canvas,设置 Render Mode 为 World Space
  2. 删除 Canvas 上的 Graphic Raycaster
  3. 添加 Tracked Device Graphic Raycaster
  4. 在 EventSystem 上删除 Standalone Input Module
  5. 添加 XR UI Input Module
2.5.2 Tracked Device Graphic Raycaster 属性
属性 说明
Ignore Reversed Graphics 忽略背面 UI
Check For 2D Occlusion 检测 2D 遮挡
Check For 3D Occlusion 检测 3D 遮挡
Blocking Mask 遮挡层级
Ray Interactor 对应哪个类型的射线
2.5.3 代码绑定 UI 事件
using UnityEngine;
using UnityEngine.UI;

public class VRUIExample : MonoBehaviour
{
    [SerializeField] private Button myButton;
    [SerializeField] private Slider mySlider;
    
    void Start()
    {
        // Button 点击
        myButton.onClick.AddListener(() => {
            Debug.Log("VR 按钮被点击");
        });
        
        // Slider 值变化
        mySlider.onValueChanged.AddListener(value => {
            Debug.Log($"Slider 值: {value}");
        });
    }
}

2.6 传送系统(Teleportation)

传送是 VR 中最常用的移动方式,可有效减少晕动症。

2.6.1 系统架构
XR Origin
└── Locomotion System
    └── Teleportation Provider
    
场景中:
├── Teleportation Area(区域传送)
└── Teleportation Anchor(定点传送)
2.6.2 Locomotion System 配置

Locomotion System 属性

属性 说明
Timeout 移动超时时间
XR Origin XR Origin 引用

Teleportation Provider 属性

属性 说明
System Locomotion System 引用
Delay Time 传送延迟(用于过渡效果)
2.6.3 传送目标类型

Teleportation Area(区域传送)

Ground Plane
├── XR Teleportation Area
├── Collider(地面碰撞体)
└── Teleport 层级(Layer 设置)
属性 说明
Interaction Manager 交互管理器
Teleportation Provider 传送提供者
Match Orientation 传送后朝向(World Up / Target Up / None)
Teleport Trigger 传送触发方式

Teleportation Anchor(定点传送)

用于传送到固定点位,常用于楼梯、电梯、兴趣点。

Anchor Point
├── XR Teleportation Anchor
├── Collider
└── 可视化指示器(可选)
属性 说明
Teleport Anchor Transform 传送目标位置
Match Orientation Target Up / Target Up And Forward
2.6.4 射线配置(抛物线)

传送通常使用抛物线射线,配置方式:

// XR Ray Interactor 设置
Line Type: Projectile
Velocity: 10 (抛物线初速度)
Acceleration: 9.81 (重力加速度)
Sample Frequency: 20 (采样频率,影响平滑度)
2.6.5 传送视觉反馈

Teleport Reticle(传送准星)

// 在 XR Ray Interactor 上设置
Custom Reticle: 传送目标点 Prefab

传送过渡效果(黑屏过渡):

using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
using System.Collections;

public class TeleportFade : MonoBehaviour
{
    [SerializeField] private TeleportationProvider teleportProvider;
    [SerializeField] private CanvasGroup fadeCanvas; // 覆盖屏幕的黑色 Canvas
    [SerializeField] private float fadeDuration = 0.2f;
    
    void OnEnable()
    {
        teleportProvider.beginLocomotion += OnBeginTeleport;
        teleportProvider.endLocomotion += OnEndTeleport;
    }
    
    private void OnBeginTeleport(LocomotionSystem system)
    {
        StartCoroutine(FadeOut());
    }
    
    private void OnEndTeleport(LocomotionSystem system)
    {
        StartCoroutine(FadeIn());
    }
    
    private IEnumerator FadeOut()
    {
        float elapsed = 0f;
        while (elapsed < fadeDuration)
        {
            fadeCanvas.alpha = elapsed / fadeDuration;
            elapsed += Time.deltaTime;
            yield return null;
        }
        fadeCanvas.alpha = 1f;
    }
    
    private IEnumerator FadeIn()
    {
        float elapsed = 0f;
        while (elapsed < fadeDuration)
        {
            fadeCanvas.alpha = 1f - (elapsed / fadeDuration);
            elapsed += Time.deltaTime;
            yield return null;
        }
        fadeCanvas.alpha = 0f;
    }
}

2.7 Socket 交互系统

Socket 用于实现"物体放入指定位置"的交互,如:把钥匙插入锁孔、物品归位、装备系统等。

2.7.1 XR Socket Interactor 配置
Socket Point
├── XR Socket Interactor
├── Collider (Is Trigger)
└── 可视化预览模型(可选)

关键属性

属性 说明
Show Interactable Hover Meshes 悬停时显示预览网格
Hover Mesh Material 预览网格材质
Socket Active Socket 是否激活
Recycle Delay Time 物体被取走后多久可再次放入
Starting Selected Interactable 初始就放置的物体
2.7.2 Socket 筛选器

通过 Interaction Layer 筛选

// 设置不同的 Interaction Layer
// Socket 的 Interaction Layer Mask 只勾选对应层级

通过 Tag 筛选

using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
using UnityEngine.XR.Interaction.Toolkit.Filtering;

public class TagSocketFilter : MonoBehaviour, IXRHoverFilter, IXRSelectFilter
{
    [SerializeField] private string requiredTag = "KeyItem";
    
    public bool canProcess => true;
    
    public bool Process(IXRHoverInteractor interactor, IXRHoverInteractable interactable)
    {
        return interactable.transform.CompareTag(requiredTag);
    }
    
    public bool Process(IXRSelectInteractor interactor, IXRSelectInteractable interactable)
    {
        return interactable.transform.CompareTag(requiredTag);
    }
}
2.7.3 完整 Socket 示例
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;

public class InventorySocket : MonoBehaviour
{
    private XRSocketInteractor socket;
    
    void Awake()
    {
        socket = GetComponent<XRSocketInteractor>();
        socket.selectEntered.AddListener(OnItemPlaced);
        socket.selectExited.AddListener(OnItemRemoved);
    }
    
    private void OnItemPlaced(SelectEnterEventArgs args)
    {
        var item = args.interactableObject.transform.GetComponent<InventoryItem>();
        if (item != null)
        {
            Debug.Log($"物品 {item.itemName} 放入背包");
            // 可以在这里禁用物体的物理,缩小尺寸等
        }
    }
    
    private void OnItemRemoved(SelectExitEventArgs args)
    {
        Debug.Log("物品被取出");
    }
    
    // 检查 Socket 是否有物品
    public bool HasItem()
    {
        return socket.hasSelection;
    }
    
    // 获取 Socket 中的物品
    public GameObject GetItem()
    {
        if (socket.hasSelection)
        {
            return socket.GetOldestInteractableSelected()?.transform.gameObject;
        }
        return null;
    }
}

2.8 XRI 事件体系

XRI 的事件系统是其灵活性的核心,理解事件流程对开发至关重要。

2.8.1 事件类型总览
事件类型 触发时机 常用场景
Hover Enter/Exit 射线/手进入/离开物体范围 高亮提示、音效
Select Enter/Exit 开始/结束抓取 抓取逻辑、状态切换
Activate/Deactivate 抓取状态下按/松 Trigger 使用物品(开枪、喷水)
Focus Enter/Exit 对象获得/失去焦点 UI 焦点系统
2.8.2 事件参数详解

BaseInteractionEventArgs(基类)

args.interactorObject    // 交互者(Interactor)
args.interactableObject  // 被交互对象(Interactable)
args.manager             // XR Interaction Manager

SelectEnterEventArgs

args.interactorObject    // 执行抓取的 Interactor
args.interactableObject  // 被抓取的 Interactable
2.8.3 三种事件监听方式

方式一:Inspector 面板绑定

直接在 XR Grab Interactable 组件的 Interactable Events 中拖拽绑定。

方式二:代码动态绑定

using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;

public class EventBindingExample : MonoBehaviour
{
    private XRGrabInteractable interactable;
    
    void Awake()
    {
        interactable = GetComponent<XRGrabInteractable>();
    }
    
    void OnEnable()
    {
        // 添加监听
        interactable.hoverEntered.AddListener(OnHoverEnter);
        interactable.hoverExited.AddListener(OnHoverExit);
        interactable.selectEntered.AddListener(OnSelectEnter);
        interactable.selectExited.AddListener(OnSelectExit);
        interactable.activated.AddListener(OnActivate);
        interactable.deactivated.AddListener(OnDeactivate);
    }
    
    void OnDisable()
    {
        // 移除监听(重要!防止内存泄漏)
        interactable.hoverEntered.RemoveListener(OnHoverEnter);
        interactable.hoverExited.RemoveListener(OnHoverExit);
        interactable.selectEntered.RemoveListener(OnSelectEnter);
        interactable.selectExited.RemoveListener(OnSelectExit);
        interactable.activated.RemoveListener(OnActivate);
        interactable.deactivated.RemoveListener(OnDeactivate);
    }
    
    private void OnHoverEnter(HoverEnterEventArgs args) 
    {
        // 高亮物体
        GetComponent<Renderer>().material.color = Color.yellow;
    }
    
    private void OnHoverExit(HoverExitEventArgs args) 
    {
        GetComponent<Renderer>().material.color = Color.white;
    }
    
    private void OnSelectEnter(SelectEnterEventArgs args) 
    {
        Debug.Log("被抓取");
    }
    
    private void OnSelectExit(SelectExitEventArgs args) 
    {
        Debug.Log("被释放");
    }
    
    private void OnActivate(ActivateEventArgs args) 
    {
        Debug.Log("激活(Trigger 按下)");
    }
    
    private void OnDeactivate(DeactivateEventArgs args) 
    {
        Debug.Log("取消激活(Trigger 松开)");
    }
}

方式三:继承并重写(适合复杂逻辑)

using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;

public class CustomGrabInteractable : XRGrabInteractable
{
    protected override void OnSelectEntered(SelectEnterEventArgs args)
    {
        base.OnSelectEntered(args);
        // 自定义逻辑
        Debug.Log("Custom grab logic");
    }
    
    protected override void OnSelectExited(SelectExitEventArgs args)
    {
        base.OnSelectExited(args);
        // 自定义逻辑
    }
}
2.8.4 Interactor 端事件

Interactor 也有对应的事件,用于监听"我抓到了什么":

using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;

public class InteractorEvents : MonoBehaviour
{
    private XRDirectInteractor directInteractor;
    
    void Awake()
    {
        directInteractor = GetComponent<XRDirectInteractor>();
        
        // Interactor 端事件
        directInteractor.selectEntered.AddListener(OnGrabbedSomething);
        directInteractor.selectExited.AddListener(OnReleasedSomething);
    }
    
    private void OnGrabbedSomething(SelectEnterEventArgs args)
    {
        Debug.Log($"我抓到了: {args.interactableObject.transform.name}");
    }
    
    private void OnReleasedSomething(SelectExitEventArgs args)
    {
        Debug.Log($"我放开了: {args.interactableObject.transform.name}");
    }
}

2.9 XR Interaction Group(交互组)

当一个控制器上有多个 Interactor(如同时有 Direct 和 Ray)时,需要 Interaction Group 管理优先级。

配置

Controller
├── XR Interaction Group
├── XR Direct Interactor (优先级高)
└── XR Ray Interactor (优先级低)

属性

属性 说明
Starting Group Members 组内的 Interactor 列表(顺序即优先级)
Interaction Override Configuration 高优先级激活时是否中断低优先级

规则:当 Direct Interactor 悬停在物体上时,Ray Interactor 自动禁用,避免冲突。

Logo

这里是一个专注于游戏开发的社区,我们致力于为广大游戏爱好者提供一个良好的学习和交流平台。我们的专区包含了各大流行引擎的技术博文,涵盖了从入门到进阶的各个阶段,无论你是初学者还是资深开发者,都能在这里找到适合自己的内容。除此之外,我们还会不定期举办游戏开发相关的活动,让大家更好地交流互动。加入我们,一起探索游戏开发的奥秘吧!

更多推荐