버즈그립 위치 제어 튜토리얼
이 튜토리얼에서는 VerseGrip의 회전을 사용하여 Inverse3 장치의 커서 위치를 직접 제어하고, 빈도가 높은 업데이트를 위해 Unity 메인 스레드를 우회하는 방법을 보여드립니다.
소개
에 표시된 방법과 달리 빠른 시작 가이드이 튜토리얼에서는 표준 업데이트 주파수(60Hz)로 작동하는 VerseGrip 디바이스의 회전에 따라 Inverse3 커서의 위치를 더 높은 주파수(1~4kHz)로 동적으로 조정하는 방법을 설명합니다.
이는 DeviceStateChanged 이벤트가 촉각 스레드에 의해 트리거됩니다.
장면 설정
햅틱 릭을 생성하여 시작합니다: 빠른 시작 가이드에 설명된 대로 게임 오브젝트 > Haply > 햅틱 릭(한 손)을 선택합니다.
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 장치에 대한 참조입니다.
- 속도: 커서가 움직이는 속도입니다.
- 움직임 제한 반경: 커서가 초기 위치에서 이동할 수 있는 최대 거리입니다.
- _targetPosition: 커서가 이동하는 목표 위치입니다.
깨어있는 방법
에서 Awake 메서드를 등록하고 Ready 이벤트를 호출하여 Inverse3 디바이스의 작업 공간 중심을 기준으로 커서의 목표 위치를 초기화합니다:
private void Awake()
{
    inverse3.Ready.AddListener((inverse3Controller, args) =>
    {
        _targetPosition = inverse3Controller.WorkspaceCenterLocalPosition;
    });
}
장치가 준비되기 전에는 아직 핸드가 결정되지 않았기 때문에 작업 공간 센터를 사용할 수 없습니다.
구현 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);
}
등록 및 등록 취소 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 커서를 길게 누릅니다.
- 버즈그립을 회전하여 Unity 씬에서 커서의 움직임을 관찰하면 버즈그립의 방향과 직접적으로 일치하는 커서의 움직임을 확인할 수 있습니다.
- 버즈그립의 버튼을 누르면 Inverse3 커서가 버즈그립의 회전 방향으로 이동하여 실시간 제어를 보여줍니다.

이 이미지는 커서 모델을 명확하게 하기 위해 앞으로 축을 강조 표시한 것입니다. 커서 모델 사용자 지정에 대한 자세한 내용은 커서 문서를 참조하세요.
소스 파일
이 예제에서 사용된 최종 씬과 모든 관련 파일은 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);
        }
    }
}