FEATURED · 精选文章

Unity XRI手柄交互全指南:从射线抓取到UI点击与震动反馈

发布时间 / 2026/9/16 3:02:57
来源 / 创域科博编辑部
栏目 / 资讯中心
Unity XRI手柄交互全指南:从射线抓取到UI点击与震动反馈 做XR项目手柄交互是我个人觉得最值得花时间啃的一关。很多人一开始用Unity XR Interaction Toolkit下面简称XRI搭Demo觉得“手柄不就是发出一根射线嘛”但真正把抓取、传送、UI点击、震动反馈串起来以后才会发现这套系统的逻辑比你想象得深得多。这篇是XRI系列的第二篇单独把“手柄交互”拎出来拆一遍从最基础的模型接入、射线可视化到抓取、传送、UI交互和震动反馈全部按我实际项目里的调试顺序来讲尽量让没有接触过XRI的人也能照着重现一遍已经写过项目的也能找到值得对标的细节。这篇内容适合两类人一类是刚接触XR开发、准备用Unity做PICO或Quest项目的初学者另一类是已经能跑通简单手势交互但一直被手柄模型不显示、抓取抖动、UI点不中这种问题折磨的开发者。手柄交互是整个XR项目里最贴近“手感”的模块它不只是一个组件挂在场景里就完事里面牵扯到输入系统、射线检测、物理刚体、UI射线命中、动画控制等多个环节。我尽量把每个环节的“为什么”也讲清楚而不只是告诉你“点哪里、拖哪个组件”。1. 动手之前先搞懂XRI手柄交互的几个基本概念1.1 手柄交互在XR项目里到底负责什么我先把这个模块的职责范围说清楚。手柄交互听起来只是“两只手柄的操作”但在实际项目里它往往同时承担四件事第一把用户在真实世界里的手部动作映射到虚拟场景中的手柄模型上也就是姿态跟随第二通过按键输入产生交互意图比如按下扳机表示“我想抓取”拨动摇杆表示“我想移动或者传送”第三生成可视化反馈让用户看到自己当前瞄准的是什么、能不能抓、能不能点第四触发虚拟物体的响应比如物体被抓起来、UI按钮被按下、传送点被激活。这四件事在XRI里并不是一个组件干完的而是拆成了Input System输入映射、XR Controller控制器基础、XR Ray Interactor射线交互器、XR Interactable可交互物体四个层次。很多新手第一次打开XRI的示例场景看到一堆组件叠在一起会懵其实只要把“输入→控制器→交互器→可交互物体”这条链路在脑子里建立起来后面所有配置都有迹可循。1.2 XRI里Interactor和Interactable是怎么回事XRI的核心模型就两个词Interactor和Interactable。可以把它理解成真实世界里的“手”和“物体”。手发出动作物体响应动作两者之间通过“交互层掩码Interaction Layer Mask”配对。如果手和物体不在同一个层就算实际距离再近也交互不了。Interactor有几种主要形态手柄射线XR Ray Interactor、直接触摸XR Direct Interactor、以及用于Socket插槽的XR Socket Interactor。手柄交互最常用的是射线形态因为XR的虚拟空间通常比较大用户不可能走到每个物体面前再用手去摸射线能帮你抓取几米外的物体、点击墙上的UI面板、指向远处传送点这些都是射线交互器的典型场景。Interactable则是指能被交互的物体组件比如XR Grab Interactable负责被抓取Teleportation Area负责被指向后触发传送XR Simple Interactable负责响应点击。在配置的时候最让人容易忽略的其实是“层掩码”这一项。默认情况下所有Interactor和Interactable都在Default层所以示例场景能跑通一旦你为了性能或者逻辑给物体分了层却没同步修改Interactor的Interaction Layer Mask就会出现射线明明照到物体上、却永远无法抓取或点击的诡异问题。这个点我会在第4章里再展开。2. 从零搭一套能跑的手柄交互Demo2.1 环境准备与版本选型在开始配置之前先确认一下你的Unity版本和XRI版本。当前主流方案是Unity 2021 LTS或2022 LTS搭配XRI 2.5.x以及Unity 6配套的XRI 3.0。我个人建议如果是新项目直接看Unity 6加XRI 3.0如果是维护老项目继续用2.5.x也没有问题核心交互逻辑差别不是特别大但组件名称和Inspector布局会有一定差异网上的教程很多是基于2.x的看的时候要留个心眼。所需Packages一般是这几个XR Plugin Management、OpenXR Plugin、XR Interaction Toolkit、Input System。在Package Manager里全部装上以后去Project Settings里的XR Plug-in Management把OpenXR勾上再根据你的目标设备启用对应的Interaction Profiles。比如做PICO 4就启用PICO Controller做Quest就用Oculus Touch Controller Profile不然手柄的按键映射会有一部分失效。接下来要确认输入系统模式。XRI 2.x以上基本依赖Input System PackageProject Settings里Active Input Handling要选“Input System Package (New)”或者两者并用的选项。这一步很容易被忽略但如果不设置XRI的输入绑定会完全无法工作手柄上按什么都没反应。2.2 手柄模型接入OpenXR Controller Model与常见坑手柄模型接入是很多人的第一道坎。XRI本身不带任何厂商的手柄模型它只负责控制器的位置和旋转跟踪。想在虚拟场景里看到手柄目前最省事的方式是用OpenXR的Controller Model组件。操作上在场景里找到XR Origin或者XR Rig下的Camera Offset子物体里面通常已经有一对LeftHand Controller和RightHand Controller的空节点。给每个节点挂上XR Controller组件然后再挂一个OpenXR Controller Model组件把Controller Node分别设为Left和Right把Model Prefab指定为你导入的厂商手柄模型文件。这样手柄模型就会自动跟随真实手柄的位姿。这里有几个坑值得单独提醒。第一手柄模型的FBX网格导入后很多朋友直接拖进去用结果发现手柄在头显里比真实手大一圈或者角度扭了。这是因为模型的单位比例和Unity默认单位不一致导入时需要在Model选项卡里调整Scale Factor并检查模型朝向是否沿Z轴正向。第二手柄模型最好单独放到一个自定义Layer上比如HandLayer然后把这个Layer从XRRayInteractor的Raycast Mask里去掉不然手柄自身的手部追踪射线或者抓取射线可能会被自己的模型挡住导致瞄准线在眼前就断掉了。第三在PICO设备上如果手柄完全不显示很可能是OpenXR的PICO Controller Profile没启用或者在设备上使用的是旧版接口需要升级设备端SDK。2.3 射线可视化Line Visual与手柄控制器配置手柄要发出一根可视化的射线需要三个组件互相配合XR Ray Interactor负责真正的检测与交互XR Interactor Line Visual负责把射线画出来XR Controller则提供扳机、摇杆等输入值。很多初学者以为“看得见的那根线”就是检测用的其实Line Visual只是渲染表现真正的命中判定是由XR Ray Interactor的Raycast逻辑完成的。这也解释了为什么有时候射线看起来已经穿过物体但物体却没有任何反应——因为Line Visual的渲染效果和Raycast命中的结果并不总是一一对应尤其是当Line Visual使用了比较复杂的贝塞尔曲线效果时视觉终点和实际命中点会有偏差。在XR Ray Interactor的Inspector里有几个参数直接影响手感。Max Raycast Distance控制最远检测距离我一般设成10米左右太远了不仅难以瞄准而且每帧的物理射线检测也会增加开销。Line Type建议直接用Straight Line手感最直接曲线模式虽然好看但在实际操作里会让用户很难判断自己到底瞄准了哪里尤其是新手。只有当目标物体可能在遮挡物后面、需要展示“抓取弧线”这种特殊效果时我才会换用Projectile Curve或者Bezier Curve。当手柄射线的可视化配置好以后下一步就是绑定输入动作。XRI提供了一套预设的XRI Default Input Actions资产你可以在Project窗口右键Create → XR → XRI Default Input Actions生成。里面已经定义好了左右手柄的Teleport、Grab、UI Click、Rotate等常用动作配合XR Controller上的Select Action、Activate Action、UI Press Action等字段完成映射。3. 核心交互功能实操与参数调优3.1 抓取距离拾取、跟随模式与稳定性抓取是手柄交互里最有“手感”的一个功能。理论上你只需要给物体挂一个XR Grab Interactable组件再加一个Collider和Rigidbody手柄射线对准物体、按下扳机物体就会被吸过来。但实际做下来你会发现“能抓起来”和“抓起来像那么回事”完全是两码事。最关键的设置是Movement Type。XRI提供三种选择Instantaneous、Velocity Tracking和Kinematic。Instantaneous是用插值直接让物体瞬移到抓取点反应最快但惯性感较弱Velocity Tracking是基于速度跟随的方式会让物体有轻微延迟和惯性更适合模拟真实物体的重量感Kinematic则把物体交给刚体的运动学模式去驱动适合需要和其他物理交互的场景。我常用的是Velocity Tracking但它对参数的敏感度很高调不好就会出现“抓在手里还一直抖”的问题。抓取点的设置也是细节中的细节。XR Grab Interactable默认抓取位置是物体的中心点但这往往不符合直觉——你抓一个水杯总不希望手伸到杯子中心去握。解决方案是给物体创建一个子物体作为Attach Transform比如放在杯子的手柄位置然后把XR Grab Interactable上的Attach Transform指定给这个子物体。这样抓取时手柄会移动到Attach Transform的位置杯子的手柄正好握在手里。这里还要补充一个我踩过的坑给被抓物体添加Rigidbody后Mass不要调太小不然抓取后碰撞检测会变得很“飘”。另外如果物体抓取后需要在手里保持稳定可以考虑把物体的Collider设为不参与射线检测或者把Interactable的Layer设成独立层否则抓住物体后手柄的射线会被手里的物体挡住导致你没法同时用同一只手去点击其他UI。3.2 传送交互Teleportation Area的前后端逻辑传送是VR里避免晕动症的常用移动方式。XRI里实现传送很简单在地面上放一个平面挂上Teleportation Area组件再把Locomotion System拖到场景里指定给XR Origin或XR Rig手柄射线照到传送区域时把手柄的Activate Action设为Teleport Mode Select按下摇杆就会显示目标指示器松开后人物传送到指定位置。但传送功能真正用起来有几个容易被忽视的点。第一传送区域必须能被射线检测到也就是说它所在的Layer必须在XR Ray Interactor的Raycast Mask范围里否则射线只是“穿透”地面永远不显示可传送标记。第二Teleportation Area本身也有一个Interaction Layer Mask默认是Teleport而XR Ray Interactor的Interaction Layer Mask也要包含Teleport两边才能配对。很多朋友把传送区域建好了但射线就是不变绿其实多半是层掩码不匹配。第三是选择Teleportation Area还是Teleportation AnchorArea是整个表面任意位置都能传适合开阔地面Anchor则绑定一个固定位置适合传送到某个精确的点位。还有一个小技巧是传送有效/无效状态的视觉反馈。XRI内置了指尖圆圈颜色的切换绿色表示可传送红色表示不可传送。但前提是XR Interactor Line Visual里开启了Teleport Marker Visual并且你正确配置了传送区域。我第一次做的时候发现无论如何都显示红色排查了很久才发现是场景里同时存在两套Teleportation Area一套在有效层一套在无效层射线同时命中了两者系统判定为不可传送。所以大型场景里多个传送区域的重叠区域一定要处理干净要么用逻辑层分开要么在代码层面控制某些区域在特定时机不可用。3.3 UI交互让射线既能照墙又能点按钮手柄射线点击UI按钮和平时的鼠标点击UI是两套体系。普通UI用的是GraphicRaycaster而XRI里要用Tracked Device Graphic Raycaster这样才能接受来自XR交互器的点击事件。同时EventSystem上也要把默认的Standalone Input Module替换成XR UI Input Module。具体配置步骤是在你的Canvas上添加Tracked Device Graphic Raycaster组件把原来的GraphicRaycaster删掉或禁用然后把World Space Canvas的Event Camera指定为XRRig下Camera Offset里的主摄像机再把Canvas的Plane Distance设成一个合适的值。手柄的XR Ray Interactor上也要勾选UI相关的选项比如当射线命中UI时物体抓取和传送功能要能自动让位这就需要在XR Ray Interactor上设置Block UI On Interactable Select、Block UI On Hover等开关。实际运行里最让人头疼的问题是“射线能照到UI但死活点不了按钮”。这种情况绝大多数是EventSystem配置出了问题。尤其在Unity里如果场景存在多个EventSystem或者旧版本遗留的Standalone Input Module没删干净XR UI Input Module的输入就会被抢掉。还有一个容易被忽略的点Canvas的Render Mode如果还是OverlayXR的射线是无法参与UI检测的必须改成World Space模式。3.4 震动反馈与操作手感提升手柄交互如果缺了震动反馈整个操作会显得很“死”。XRI里做震动很简单在需要反馈的时机调用XR Controller的Send Haptic Impulse方法即可。比如抓取物体的瞬间可以写这样一段逻辑public void OnSelectEntered(SelectEnterEventArgs args) { var controller args.interactorObject.transform.GetComponentXRBaseController(); if (controller ! null) { controller.SendHapticImpulse(0.3f, 0.1f); } }需要注意的是OpenXR的震动需要设备支持对应的Haptic扩展。在PICO和Quest上一般没问题但某些PC VR设备或模拟器上可能没有反馈。另外SendHapticImpulse有一个很隐蔽的特性同一时间内连续多次调用会造成震动信号重叠甚至失效频率太高的时候设备会忽略后发的脉冲所以针对高频事件比如连续扣扳机要做节流一般每50毫秒最多触发一次震动就够了。除了震动手感的提升还可以靠射线宽度、抓取范围、交互特效声音来配合。比如射线命中物体时把Line Visual端点的小圆点从灰色变成高亮色同时播放一个“嘀”的提示音用户的命中感就会强很多。这些细节单独看都不复杂但加在一起整个手柄交互的完成度会明显不一样。4. 实战踩坑记录常见问题与排查4.1 手柄模型在头显里不显示或只显示骨骼这个问题在PICO和Quest设备上都很常见原因通常是这几类里的一种。第一OpenXR Interaction Profile没启用PICO要勾PICO ControllerQuest要勾Oculus Touch检查Plug-in Management里的OpenXR Feature列表。第二手柄模型的网格和材质文件没有正确导入或者导入后被项目设置里的Strip Engine Code裁剪掉了发布前最好在Player Settings里把Managed Stripping Level调低一点。第三手柄模型挂在了错误节点上比如挂在XR Origin根节点下而没挂在Camera Offset下面导致位置跟随错误。如果模型显示的是骨骼而不是皮肤优先检查模型导入设置里的Animation Type确认是不是被识别成了Humanoid并在运行时被动画系统驱动必要时改成Generic模式。4.2 射线穿墙、显示错乱或时断时续射线穿墙本质上是Line Visual只画了线而实际命中点被墙壁之外的东西拦截了。优先检查XR Ray Interactor的Raycast Mask确认是否包含了所有需要命中的层同时确认没有把手柄模型自带的碰撞体算进去。射线时断时续则大多是因为命中的物体有多个Collider而XRI的Raycast只取第一个命中点当物体的Collider在手指和物体之间不断切换时视觉上就会表现为射线闪烁。另外如果你的场景里有较多UI Canvas并且Canvas上没有挂Tracked Device Graphic Raycaster射线会被UI挡住导致穿不过去。这个问题的诡异之处在于你根本看不见那个UI但它依然参与了射线检测所以一旦发现射线“穿透”得莫名其妙先检查场景里有没有隐藏的不可见UI充当了拦路虎。4.3 抓取物体抖动、弹飞或穿模抓取后的抖动原因很多我按概率排一下一是Movement Type用了Velocity Tracking但Rigidbody的Solver Iterations太低可以在Project Settings的Physics里把默认Solver Iterations调高到8、Solver Velocity Iterations调到4二是抓取点的Attach Transform和手柄模型中心偏差过大导致物体一直在“追”手柄的位置三是物体的Collider和手柄的Collider在抓取后产生互斥推力建议抓取时临时禁用被抓住物体的碰撞或者用Layer来隔离碰撞四是Rigidbody的Interpolate设为None时物体跟随会显得很生硬配合Velocity Tracking可以改成Interpolate能让画面顺滑很多。穿模问题多半不是XRI本身造成的而是手柄模型的Collider太大抓取后物体被推开。我给手柄模型挂Collider时一般只用很小的球形碰撞体并且把它的Layer设成Ignore Raycast既保留物理碰撞又不干扰逻辑。4.4 手柄射线点不中UI按钮按钮永远点不中的排查顺序依次是Canvas是否为World Space且Event Camera是否正确指定EventSystem上是否用的是XR UI Input ModuleCanvas上是否挂了Tracked Device Graphic RaycasterXR Ray Interactor的UI Press Action是否绑定到了扳机键以及Interaction Layer Mask是否包含UI层。此外还有一个经常被忽略的细节World Space Canvas的缩放比例不能太夸张。如果你把Canvas的Scale设成0.01射线命中的判定区域会变得极其敏感按钮位置稍微偏一点就点不到。我通常把Canvas的Scale保持在0.001到0.002这个量级再配合Plane Distance来调节面板距离命中会稳定很多。4.5 不同XRI版本差异速查与迁移建议网上教程大多基于XRI 2.x但Unity 6默认使用XRI 3.0组件变化不小。最直观的变化是XR Controller被拆分成了XR Input Modality Manager和XR Controller组件旧版Inspector里那一堆输入绑定字段被移到了Input Modality Manager里统一配置XR Origin的Prefab结构也简化了少了一层嵌套XR Grab Interactable的交互事件从固定的OnSelectEntered等改成了更灵活的XR Interactable Event可以通过UI拖拽配置。功能点XRI 2.xXRI 3.0输入绑定XR Controller上直接配置XR Input Modality Manager统一配置XR Origin结构XR Rig → Camera Offset层级简化的XR Origin交互事件组件上多个Select/Hover事件统一的XR Interactable Event传送区域Teleportation Area/Anchor基本保留但命名和Inspector有调整兼容性多数老教程适用老项目迁移需要重构部分组件如果你是老项目升级最稳妥的做法不是直接替换Package而是先在分支上把XRI升到3.0跑一遍示例场景再把你自己写的交互代码逐个接入新事件系统。直接大版本跳跃很容易遇到“编译过了但交互全失灵”的情况原因是XR Controller的输入地址变了动作绑定其实丢失了。5. 把Demo做成产品级交互之前值得想清楚的事5.1 交互架构设计层、身份与状态机当你不再满足于Demo想把手柄交互做成一个可以交付的产品时你会发现最花时间的其实不是“让手抓一个杯子”而是“让手里拿着的道具在不同场景有不同的行为”。这时候单一的手柄交互脚本会变得非常臃肿。我的个人实践是把一个交互对象拆成三层来管理第一层是“可交互身份”决定这个物体能被哪种Interactor识别、属于哪个Layer、优先级是多少第二层是“交互状态机”记录当前物体是闲置、被悬停、被抓取还是被放进了Socket第三层是“交互规则”比如杯子被抓住时可以倒水但只有在靠近水壶时按下扳机才有效。这套结构在一开始就规划好比后面所有物体都堆一起再重构要省太多时间。具体到代码上可以用接口加状态机的方式比如定义IInteractableItem接口暴露CanInteract、OnGrab、OnRelease等方法抓取时通过组件查找来决定调用哪套逻辑。5.2 性能与手感调优方向手柄交互性能优化的核心是减少每帧不必要的射线检测和刚体跟随。场景里交互物体不多的时候感觉不出来但一旦到了有几十个可抓取物件的生产级场景每帧让所有Interactor都执行物理检测CPU开销很快就上来了。我的建议是射线的Max Raycast Distance不要贪大并且尽量少开多个XR Ray Interactor同时工作那些不太重要的交互物体可以放到单独的Layer里用较低的轮询频率做检测。手感调优则更偏主观每个项目的手感目标都不一样没有一套万能参数。常见手段包括抓取时略微缩小射线命中半径防止用户还没对准就把东西吸过来、抓取后给物体加一定的阻尼和惯性、松开手时给物体施加一个小的向上速度补偿让物体不被手带飞。这些都要靠真机反复测试不要只在编辑器里调头显里感受到的延迟和手柄手感和编辑器里完全是两个世界。我在实际项目里的体会是手柄交互做到“能用”很容易做到“顺手”则要来回打磨很久但只要把Interactor、Interactable、Layer这三件套的机制彻底吃透绝大多数问题都能定位到具体环节不再靠瞎试碰运气。希望这篇能把你在手柄交互上卡壳的地方都疏通一遍后面玩得更顺一些。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻