VerseGrip 위치 제어 튜토리얼
이 튜토리얼에서는 VerseGrip의 회전 기능을 활용하여 Inverse3 기기의 커서 위치를 직접 제어하는 방법을 설명하며, 이 과정에서 고주파수 업데이트를 위해 Unity의 메인 스레드를 우회하는 방법을 다룹니다.
서론
...에서 소개된 방법과는 달리 “첫걸음” 사용 안내, 일반적인 재생 빈도(60Hz)로 작동하는 이 튜토리얼은 VerseGrip 장치의 회전 각도에 따라 더 높은 주파수(1~4kHz)로 Inverse3 의 커서 위치를 동적으로 조정하는 방법을 다룹니다.
이는 다음 이벤트를 통해 구현됩니다. DeviceStateChanged 햅틱 스레드에 의해 트리거되는 기능이 사용됩니다.
장면 설정
“시작하기” 가이드에 설명된 대로, 먼저 햅틱 리그를 생성해 보세요: GameObject > Haply > Haptic Rig (one hand).
VerseGripPositionControl 구성 요소
다음과 같은 이름의 새로운 C# 스크립트를 만드세요. 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: 커서가 이동하는 대상 위치.
메토드 어웨이크
메서드 내에서 등록하십시오 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);
}
}
}