VerseGrip 位置控制教程
本教程将介绍如何利用VerseGrip的旋转值直接控制Inverse3 设备的光标位置。该方法绕过了Unity的主线程,以实现高频更新。
简介
入门指南虽然之前介绍的方法是在标准更新周期(60Hz)下运行的,但本教程旨在通过VerseGrip设备的旋转,以更高的频率(1~4kHz)动态调整Inverse3 光标的位置。
这是由触觉线程触发的 DeviceStateChanged 通过事件来实现。
场景设置
首先创建一个Haptic Rig:GameObject >Haply > Haptic Rig(单手)。详情请参阅入门指南。
VerseGripPositionControl 组件
VerseGripPositionControl.cs生成一个名为的新 C# 脚本,并将它 触觉起源 将其连接到游戏对象。
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;
});
}
在设备准备就绪之前,由于手部方向尚未确定,因此无法使用工作空间中心。
基于VerseGrip的旋转和按钮输入来计算光标的目标位置 OnDeviceStateChanged 实现该方法:
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);
}
OnEnable和 OnDisable在……中 DeviceStateChanged 注册和取消事件。
/// 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的旋转方向移动,从而显示实时控制效果。

该图像展示了一个“光标模型”(Cursor Model),其中为便于清晰显示,已突出显示了前轴。 有关“光标模型”自定义的详细信息,请参阅“光标”文档。
源文件
本示例中使用的最终场景及相关文件均可从 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);
}
}
}