VerseGrip 位置控制教程
本教程演示了如何利用VerseGrip的旋转功能直接控制Inverse3 设备的光标位置,同时绕过Unity的主线程以实现高频更新。
导言
与该文中介绍的方法不同, 入门指南, 该设备以标准刷新率(60Hz)运行,本教程旨在根据VerseGrip设备的旋转情况,以更高的频率(1~4kHz)动态调整Inverse3 光标的位置。
这是通过利用该事件来实现的 DeviceStateChanged,由触觉线程触发。
场景设置
首先创建一个“Haptic Rig”:GameObject >Haply > Haptic Rig(单手),具体操作请参阅《入门指南》。
组件 VerseGripPositionControl
创建一个名为 VerseGripPositionControl.cs 并将它附加到游戏对象上 触觉起源.
在该类中定义以下属性 VerseGripPositionControl :
public Inverse3 inverse3;
public VerseGrip verseGrip;
[Range(0, 1)]
public float speed = 0.5f;
[Range(0, 0.2f)]
public float movementLimitRadius = 0.2f;
private Vector3 _targetPosition;
- inverse3 : 引用在检查器中定义的Inverse3 设备。
- verseGrip:指用于控制光标的VerseGrip设备。
- 速度:光标的移动速度。
- movementLimitRadius:光标从初始位置开始可移动的最大距离。
- _targetPosition:光标移动到的目标位置。
“Awake”方法
在该方法中 Awake, 登记该活动 Ready 要根据设备工作区的中心初始化光标的目标位置,请执行以下操作:Inverse3 :
private void Awake()
{
inverse3.Ready.AddListener((inverse3Controller, args) =>
{
_targetPosition = inverse3Controller.WorkspaceCenterLocalPosition;
});
}
在设备准备就绪之前,工作区中心不可用,因为尚未确定其左右方向。
实现该方法 OnDeviceStateChanged 根据VerseGrip的旋转角度和按钮输入来计算光标的目标位置:
private void OnDeviceStateChanged(object sender, VerseGripEventArgs args)
{
var verseGrip = args.DeviceController;
// Calculate the direction based on the VerseGrip's rotation
var direction = verseGrip.Orientation * Vector3.forward;
// Check if the VerseGrip button is pressed down
if (verseGrip.GetButtonDown())
{
// Initialize target position
_targetPosition = inverse3.CursorLocalPosition;
}
// Check if the VerseGrip button is being held down
if (verseGrip.GetButton())
{
// Move the target position toward the grip direction
_targetPosition += direction * (0.0001f * speed);
// Clamp the target position within the movement limit radius
var workspaceCenter = inverse3.WorkspaceCenterLocalPosition;
_targetPosition = Vector3.ClampMagnitude(_targetPosition - workspaceCenter, movementLimitRadius)
+ workspaceCenter;
}
// Move cursor to new position
inverse3.SetCursorLocalPosition(_targetPosition);
}
注册和注销活动 DeviceStateChanged dans OnEnable et OnDisable.
/// Subscribes to the DeviceStateChanged event.
private void OnEnable()
{
verseGrip.DeviceStateChanged += OnDeviceStateChanged;
}
/// Unsubscribes from the DeviceStateChanged event.
private void OnDisable()
{
verseGrip.DeviceStateChanged -= OnDeviceStateChanged;
}
流程
- 将Inverse3 设备固定好,并确保其有足够的活动空间。
- 启用“播放模式”,并按住Inverse3 键。
- 旋转 VerseGrip,观察光标在 Unity 场景中的移动,这与 VerseGrip 的方向直接对应。
- 按下VerseGrip按钮,Inverse3 滑块将沿VerseGrip的旋转方向移动,从而展示实时控制效果。

该图展示了“光标模型”,为便于理解,其前轴已被突出显示。 有关“光标模型”自定义的更多详细信息,请参阅光标相关文档。
源文件
该示例中使用的最终场景及所有相关文件均可从 Unity 包管理器的“Tutorials”示例中导入。
VerseGripPositionControl.cs
/*
* Copyright 2024 Haply Robotics Inc. All rights reserved.
*/
using Haply.Inverse.DeviceControllers;
using Haply.Inverse.DeviceData;
using UnityEngine;
namespace Haply.Samples.Tutorials._6_VerseGripPositionControl
{
/// <summary>
/// Demonstrates how to control the device cursor position using the VerseGrip.
/// </summary>
public class VerseGripPositionControl : MonoBehaviour
{
public Inverse3Controller inverse3;
public VerseGripController verseGrip;
[Tooltip("Cursor moving speed")]
[Range(0, 1)]
public float speed = 0.5f;
[Tooltip("Maximum radius for cursor movement")]
[Range(0, 0.1f)]
public float movementLimitRadius = 0.075f;
private Vector3 _targetPosition; // Target position for the cursor
private void Awake()
{
inverse3.Ready.AddListener((inverse3Controller, args) =>
{
_targetPosition = inverse3Controller.WorkspaceCenterLocalPosition;
});
}
/// <summary>
/// Subscribes to the DeviceStateChanged event.
/// </summary>
private void OnEnable()
{
verseGrip.DeviceStateChanged += OnDeviceStateChanged;
}
/// <summary>
/// Unsubscribes from the DeviceStateChanged event.
/// </summary>
private void OnDisable()
{
verseGrip.DeviceStateChanged -= OnDeviceStateChanged;
inverse3.Release();
}
private void OnDeviceStateChanged(object sender, VerseGripEventArgs args)
{
var verseGrip = args.DeviceController;
// Calculate the direction based on the VerseGrip's rotation
var direction = verseGrip.Orientation * Vector3.forward;
// Check if the VerseGrip button is pressed down
if (verseGrip.GetButtonDown())
{
// Initialize target position
_targetPosition = inverse3.CursorLocalPosition;
}
// Check if the VerseGrip button is being held down
if (verseGrip.GetButton())
{
// Move the target position toward the grip direction
_targetPosition += direction * (0.0001f * speed);
// Clamp the target position within the movement limit radius
var workspaceCenter = inverse3.WorkspaceCenterLocalPosition;
_targetPosition = Vector3.ClampMagnitude(_targetPosition - workspaceCenter, movementLimitRadius)
+ workspaceCenter;
}
// Move cursor to new position
inverse3.SetCursorLocalPosition(_targetPosition);
}
}
}