跳过至主要内容

VerseGrip 位置控制教程

在本教程中,我们将介绍如何利用VerseGrip的旋转功能,绕过Unity的主线程,直接控制Inverse3 设备的光标位置,从而实现高频更新。

前言​

入门指南 如前所述,该方法以标准刷新率(60Hz)运行,但在本教程中,我们将基于VerseGrip设备的旋转,以更高的频率(1~4kHz)动态调整Inverse3 光标的位置。 这是由触觉线程触发的 DeviceStateChanged 这将通过利用活动来实现。

场景设置​

首先,创建一个 Haptic Rig:GameObject >Haply > Haptic Rig(单手)。详情请参阅《入门指南》。

VerseGripPositionControl 组件​

新的 C# 脚本 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;
});
}

在设备准备就绪之前,由于设备的左右配置(手性)尚未确定,因此无法使用工作区的中心。

为了根据 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的旋转方向移动,从而体验实时控制。

使用versegrip移动球的场景

为了便于理解,这张图片展示了一个将前向轴突出显示的Cursor 模型。 有关 Cursor 模型自定义的详细信息,请参阅Cursor 文档。

源文件​

本示例中使用的最终场景及所有相关文件均可从 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);
}
}
}