Unity XR Interaction Toolkit 指南:多平台适配与交互组件详解
安装 XRI 时,记得在 Package Manager 中展开,导入和,这两个示例资源非常实用包含一些初始的示例配置和XR 模拟器 无需打包出来 在unity 即可测试 交互效果。
第一部分: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 Assets 和 XR 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(直接抓取)
适用场景:手部/控制器直接接触物体进行抓取。
配置步骤:
- 在控制器对象上添加
XR Direct Interactor - 添加一个 Collider(通常是 Sphere Collider)并勾选
Is Trigger - 在可抓取物体上添加
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(射线抓取)
适用场景:远距离选择和抓取物体。
配置步骤:
- 在控制器对象上添加
XR Ray Interactor - 添加
XR Interactor Line Visual(可视化射线) - 添加
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 等)
配置步骤:
- 创建 Canvas,设置 Render Mode 为
World Space - 删除 Canvas 上的
Graphic Raycaster - 添加
Tracked Device Graphic Raycaster - 在 EventSystem 上删除
Standalone Input Module - 添加
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 自动禁用,避免冲突。
这里是一个专注于游戏开发的社区,我们致力于为广大游戏爱好者提供一个良好的学习和交流平台。我们的专区包含了各大流行引擎的技术博文,涵盖了从入门到进阶的各个阶段,无论你是初学者还是资深开发者,都能在这里找到适合自己的内容。除此之外,我们还会不定期举办游戏开发相关的活动,让大家更好地交流互动。加入我们,一起探索游戏开发的奥秘吧!
更多推荐


所有评论(0)