Unity 触觉设置
HEXR 触觉的工作原理,如何通过代码或无需代码添加触觉,以及如何设计出手感恰当的触觉。
触觉的工作原理
无论由什么触发,每个触觉都经过同样的路径:
- 每只手套有六个通道——五根手指和手掌。每个通道要么按压(持续挤压),要么振动。
- 手部靠近检测会拦截大多数触觉,除非手确实到了物体那里:正在抓取它、即将戳按它,或位于
ProximityCheck范围内。你的装置(rig)中的交互器会自动为你设置。详情见手部靠近检测。
无需代码添加触觉
三个组件即可覆盖大多数物体。像添加其他组件一样,在 Inspector 中添加它们即可。
感受你抓取的物体——HexRInteractableHaptics
建议从这里入手。当你的 XR SDK 抓取或悬停在该物体上时,它会按压手指。
将它添加到可交互组件旁边
添加到已带有 XR Grab Interactable(OpenXR)或 Meta 的 Grabbable 和 Hand Grab Interactable 的物体上。Backend 保持为 Auto 即可。
选择触发时机
将 Fire On 设为 Select(抓取期间)、Hover(手靠近期间)或 Select And Hover。
选择手指和强度
勾选要按压的手指并设置 Strength,范围从 10(几乎感觉不到)到 60(紧实)。开启 Follow Grip 后,强度会根据手指合拢的程度,在 Min Strength 与 Strength 之间缩放。
让表面摸起来像真实的东西——SpecialHaptics
将它放在触发碰撞体上,然后点击 Auto Find Hand Physics。任何碰到该碰撞体的手指,都会感受到你在 Type Of Haptics 中选择的效果——无需抓取,也无需手部靠近检测。
| 效果 | 手感 |
|---|---|
CustomHaptics | 触碰期间持续施加压力 |
CustomVibrations | 以设定的频率和强度振动 |
FountainEffect | 流水 |
RainDropEffect | 雨滴落在手掌上 |
HeartBeatEffect | 心跳,规律或不规律 |
HandSqueezeEffect | 手在该区域内握拢时触发事件 |
对于两种自定义效果,请设置 Haptic Pressure;如果是振动,还需设置 Vibrations Frequency Value。
交互器无法覆盖的物体——ProximityCheck
它本身不触发任何触觉:它告诉 HEXR 手就在附近,从而放行该物体上的其他触觉。将它与触发碰撞体一起添加,然后点击 Auto Set Up。
用指尖按下的按钮、手指弯曲事件等更多用法:我需要哪个组件?
通过代码触发触觉
对于游戏事件、触觉序列或组件无法覆盖的情况,可直接调用该手的 Pressure Controller(压力控制器)。在脚本中添加一个 PressureTrackerMain 字段,然后将场景中的 Left 或 Right Pressure Controller 拖入。
单根手指
// using HaptGlove; using HexR;
// hand: a PressureTrackerMain field with a Pressure Controller dragged in
// Pressure: finger, on/off, intensity 0.1-1, ramp speed 0.1-1, skip hand-near check
hand.CustomSingleHaptics(Haptics.Finger.Index, true, 0.5f, 1f, false);
// Vibration: finger, on/off, intensity 0.1-1, frequency 0.1-40 Hz, skip hand-near check
hand.CustomSingleVibrations(Haptics.Finger.Palm, true, 0.7f, 12f, false);
// The components' 10-60 strength scale, as the 0.1-1 intensity above
float intensity = PressureTrackerMain.StrengthToIntensity(30f);最后一个参数用于跳过手部靠近检测。如果按压是为了响应你自己的事件而非触碰,请传入 true。
多根手指,同一设置
传入六个开/关值,每个通道一个,顺序始终为拇指、食指、中指、无名指、小指、手掌。这些调用没有跳过检测的参数,因此只有在手靠近物体时才会触发。
// One entry per channel: Thumb, Index, Middle, Ring, Pinky, Palm
bool[] grip = { true, true, true, false, false, false }; // thumb, index and middle
hand.TriggerCustomHapticsIncrease(grip, 0.6f, 1f); // pressure 0.1-1, ramp speed 0.1-1
hand.TriggerCustomlVibrations(grip, 0.7f, 12f); // intensity 0.1-1, frequency 0.1-40 Hz
// (the stray "l" is in the real name)
hand.TriggerAllHapticsIncrease(0.5f); // every channel, pressure 0.1-1
hand.TriggerAllVibrations(12f); // every channel, frequency
hand.TriggerPinchPressure(0.6f); // thumb and index together每根手指单独设置
为全部六个通道构建一条命令并直接发送给手套:只需一次蓝牙写入,且不经过手部靠近检测。每个数组都必须为每根手指提供一个条目,顺序相同,否则调用会抛出错误。
HaptGloveHandler glove = HexRManager.Instance.rightHand; // or leftHand
// All six channels, always in this order
Haptics.Finger[] fingers = { Haptics.Finger.Thumb, Haptics.Finger.Index,
Haptics.Finger.Middle, Haptics.Finger.Ring,
Haptics.Finger.Pinky, Haptics.Finger.Palm };
bool[] on = { true, true, true, true, true, true }; // false leaves a channel out
float[] intensities = { 0.3f, 0.9f, 0.9f, 0.6f, 0.4f, 0.8f };
float[] speeds = { 1f, 1f, 1f, 1f, 1f, 1f };
glove.BTSend(glove.haptics.HEXRPressure(fingers, on, intensities, speeds));
// Vibration: here frequency comes BEFORE intensity. 0 Hz is clamped to 0.1 Hz, not off.
float[] freqs = { 3f, 12f, 12f, 6f, 3f, 3f };
glove.BTSend(glove.haptics.HEXRVibration(fingers, on, freqs, intensities));松开
hand.CustomSingleHaptics(Haptics.Finger.Index, false, 0f, 1f, true); // one finger's pressure
hand.CustomSingleVibrations(Haptics.Finger.Palm, false, 0f, 12f, true); // one finger's vibration
hand.RemoveIndexHaptics(); // one finger: RemoveThumbHaptics() ... RemovePalmHaptics()
hand.RemoveAllHaptics(); // every channel's pressure on this hand
hand.RemoveAllVibrations(); // every channel's vibration on this hand
glove.ReleaseAllChannels(); // everything on the glove, in one write手套会持续施加最后收到的压力,直到收到释放命令;手套上没有任何部件会自行松开。自 0.9.4 起,Unity 插件包会在应用退出、暂停或断开手套连接时释放所有通道,但如果应用崩溃或蓝牙连接中断,就没有任何办法释放;Python 库也不会替你释放任何通道。如果手套一直保持按压,请关闭手臂模块。
释放时,可将最后一个参数设为 true,或调用 Remove… 系列方法或 ReleaseAllChannels()。经过手部靠近检测的释放调用,在手移开后会被丢弃——RemovePinchPressure() 就是其中之一,因此请在它之后再调用 RemoveAllHaptics()。
可直接复制的完整脚本:在自定义事件中按压手指和按压触碰到物体的手指。所有方法都列在 PressureTrackerMain 下。
设计触觉
两种模式,以及几项会改变手感的设置:
| 模式 | 设置 | 作用 |
|---|---|---|
| 压力 | 强度(Intensity)0.1–1 | 按压力度:约 15 至 50 kPa |
| 压力 | 速度(Speed)0.1–1 | 达到目标压力的快慢。设为 1 时,约 0.3 s 达到最大按压;设为 0.1 时,慢十倍 |
| 振动 | 0.1–5 Hz | 可以数清的独立脉冲。强度决定每个脉冲的按压力度 |
| 振动 | 5–40 Hz | 持续的嗡嗡振动。强度决定每个脉冲的持续时长 |
如需塑造节奏,还可以使用慢脉冲和计数脉冲——参见通道与参数。
提示
- 先感受,再写代码。HEXR Tester 可以用压力或振动驱动任意手指,让你先找到满意的参数值。
- 只按压接触到的手指。每个指尖和手掌都有各自的触点,因此 HEXR 知道是哪个部位发生了接触。
- 用加压快慢体现物体特性。缓慢加压适合柔软或可挤压的物体;快速加压适合坚硬的接触。
- 随握力缩放。
HexRInteractableHaptics上的 Follow Grip 会让手握得越紧,按压越强。 - 用振动表现纹理和节奏。用嗡嗡振动表现流水或粗糙表面,用慢脉冲表现心跳。
SpecialHaptics的预设是很好的起点。 - 在事件发生时发送,而不是每帧发送。在 Quest 和 PICO 上,命令会被收集起来,最快每 100 ms 发送一次,因此每帧调用并无益处。参见延迟。
- 检查实际发送的内容。HexR 菜单中的 HAPTICS 读数会显示发送到每根手指的最后一个值,Show Colliders 则会绘制出各个触点。