跳转至主要内容

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旋转的方向移动。这展示了实时控制的效果。

带有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);
}
}
}