VerseGrip 位置控制教程
本教程将演示如何利用VerseGrip的旋转功能,直接控制Inverse3 设备的光标位置,同时绕过Unity的主线程以实现高频更新。
引言
与来自……的方法不同 《入门指南》, 该设备以标准刷新率(60Hz)运行,本教程旨在根据VerseGrip设备的旋转情况,以更高的频率(1~4kHz)动态调整Inverse3 的光标位置。
实现这一功能的方法是通过事件 DeviceStateChanged 该功能由触觉线程触发。
场景设置
首先,请创建一个触觉装置:GameObject >Haply > Haptic Rig(单手),具体操作请参阅“入门指南”中的说明。
组件 VerseGripPositionControl
创建一个名为 VerseGripPositionControl.cs 并将其附加到该 GameObject 上 触觉起源 an.
在类中定义 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:光标移动到的目标位置。
Methode 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 在 OnEnable 并完成注册,请访问 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 包管理器中的“示例教程”进行导入。
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);
}
}
}