주요 콘텐츠로 건너뛰기
버전: 3.1.3

디바이스 매핑 튜토리얼

이 튜토리얼에서는 Haply 인버스 서비스를 사용하여 Unity 프로젝트에서 디바이스를 수동으로 나열, 매핑 및 연결하는 방법을 보여줍니다.

개요

디바이스맵퍼 컴포넌트는 Inverse3 및 버즈그립과 같은 Haply 디바이스와 씬의 디바이스 컨트롤러의 검색, 매핑, 연결을 관리합니다.

장면 설정

햅틱 릭을 생성하여 시작합니다: 빠른 시작 가이드에 설명된 대로 게임 오브젝트 > Haply > 햅틱 릭(양손)을 선택합니다.

디바이스 선택기 컴포넌트

라는 이름의 새 C# 스크립트를 만듭니다. DeviceSelector.cs 를 클릭하고 디바이스 매퍼 게임 오브젝트. 다음 프로퍼티를 DeviceSelector 클래스:

public Inverse3Controller leftInverse3;
public Inverse3Controller rightInverse3;

private DeviceMapper _deviceMapper;
private string _message;
private bool _waitingForVerseGripHandednessConfirm;

디바이스 매퍼 설정

에서 Awake 메서드를 초기화하려면 _deviceMapper 속성을 구독하고 Ready 이벤트를 발생시켜 디바이스 매퍼의 준비 상태를 처리합니다.

private void Awake()
{
_deviceMapper = GetComponent<DeviceMapper>();

_deviceMapper.autoFetchDeviceList = false; // Disable auto-fetch to manually fetch the device list
_deviceMapper.autoAssign = false; // Disable auto-assign to manually map devices
_deviceMapper.autoConnect = false; // Disable auto-connect to manually connect devices

_deviceMapper.Ready.AddListener(OnDeviceMapperReady);
}

장치 컨트롤러 가져오기

장치 컨트롤러를 얻으려면 다음을 사용할 수 있습니다. GetInverse3Controller 그리고 GetVerseGripController 메서드.

// Get the first Inverse3 controller in the scene
leftInverse3 = _deviceMapper.GetInverse3Controller();

// Get the first Inverse3 controller in the scene with the specified handedness
leftInverse3 = _deviceMapper.GetInverse3Controller(HandednessType.Left);

// Get the Inverse3 controller in the scene with the specified device ID
leftInverse3 = _deviceMapper.GetInverse3Controller("1309");

// Get the first VerseGrip controller in the scene
verseGripController = _deviceMapper.GetVerseGripController();

// Get the VerseGrip controller in the scene associated with the specified Inverse3 controller
leftVerseGripController = _deviceMapper.GetVerseGripController(leftInverse3);

디바이스 컨트롤러 핸드헬드 선택

장치가 준비되기 전에 다음을 사용하여 손맛을 선택할 수 있습니다. SelectedHandedness 속성을 설정합니다. 디바이스맵퍼는 선택한 핸디에 따라 연결된 디바이스를 컨트롤러에 자동으로 할당합니다.

// Select the left handedness for the left controller
leftInverse3.SelectedHandedness = HandednessType.Left;

// Select the right handedness for the right controller
rightInverse3.SelectedHandedness = HandednessType.Right;

장치 목록 가져오기

연결된 디바이스 목록을 가져오는 방법은 다음과 같습니다. FetchDeviceListOnce 메서드를 사용합니다.

// Fetch the list of connected devices
_deviceMapper.FetchDeviceListOnce();

장치 목록이 가져오고 나면 DeviceMapper 컴포넌트가 트리거됩니다. DeviceListReceived 이벤트. 이 이벤트를 사용하여 UI를 업데이트하거나 디바이스 목록과 관련된 다른 작업을 수행할 수 있습니다.

// Subscribe to the DeviceListReceived event
_deviceMapper.DeviceListReceived += OnDeviceListReceived;

private void OnDeviceListReceived(object sender, EventArgs e)
{
// Update the UI or perform any other operations
}

매핑 장치

장치 목록을 가져오면 다음을 사용하여 장치를 컨트롤러에 매핑할 수 있습니다. MapDevices 메서드를 사용합니다.

// Map the devices to the controllers
if (_deviceMapper.CanMapDevices())
_deviceMapper.MapDevices();

그리고 CanMapDevices 메서드는 디바이스 목록이 완전한지, 컨트롤러가 매핑할 준비가 되었는지 확인합니다.

장치 연결

디바이스가 매핑되면 다음을 사용하여 디바이스를 웹소켓 서버에 연결할 수 있습니다. Connect 메서드를 사용하여 실시간 데이터를 수신하고 디바이스를 제어할 수 있습니다.

정보

다음 예제에서는 ProbeCursorPosition 메서드를 호출하여 장치가 준비되면 커서 위치 프로빙을 시작합니다.

// Connect the devices to the WebSocket server to receive real-time data
if (_deviceMapper.State == DeviceMapperState.MAPPING_COMPLETE)
_deviceMapper.Connect();

장치 교체

컨트롤러 간에 장치를 교체하려면 다음을 사용할 수 있습니다. SwapInverse3 또는 SwapVerseGrip 메서드를 사용합니다.

다음 예는 사용자에게 오른쪽 컨트롤러의 버튼을 눌러 양손잡이 여부를 확인하거나 양손잡이가 잘못된 경우 장치를 교체하도록 요청하는 방법을 보여 줍니다.

에서 OnDeviceMapperReady 메서드를 구독하는 경우 ButtonDown 이벤트를 발생시켜 버튼 누름을 처리하고 사용자에게 손이 닿았는지 확인하도록 요청합니다.

private void OnDeviceMapperReady()
{
// Get the VerseGrip controllers using the DeviceMapper (must be done in Start or on DeviceMapper Ready event)
var leftVerseGrip = _deviceMapper.GetVerseGripController(leftInverse3);
var rightVerseGrip = _deviceMapper.GetVerseGripController(rightInverse3);

leftVerseGrip.ButtonDown.AddListener(OnButtonDown);
rightVerseGrip.ButtonDown.AddListener(OnButtonDown);

// Start waiting for button press on the right controller
_message = "Press the button on the right controller to confirm handedness.";
_waitingForVerseGripHandednessConfirm = true;
}

에서 OnButtonDown 메서드에서는 사용자가 오른쪽 컨트롤러의 버튼을 눌러 핸드핸드 여부를 확인하는지 확인합니다.

private void OnButtonDown(VerseGripController verseGrip, VerseGripEventArgs args)
{
if (_waitingForVerseGripHandednessConfirm)
{
if (verseGrip != _deviceMapper.GetVerseGripController(rightInverse3))
{
_deviceMapper.SwapVerseGrip();
}
_waitingForVerseGripHandednessConfirm = false;
}
}
private void OnGUI()
{
if (_waitingForVerseGripHandednessConfirm)
{
GUILayout.Label(_message);
}
}
경고

이 예제는 튜토리얼용이며 실제 제작에 사용해서는 안 됩니다. 애플리케이션에 적합한 방법을 찾아서 손맛을 확인하세요.

오류 처리

디바이스 매핑 프로세스 중에 오류가 발생하면 Error 이벤트가 트리거됩니다. 이 이벤트를 사용하여 오류를 처리하고 사용자에게 피드백을 제공할 수 있습니다.

// Subscribe to the Error event
_deviceMapper.ErrorOccured += OnError;

private void OnError(object sender, ErrorEventArgs e)
{
_message = e.ErrorMessage;
switch (e.ErrorCode)
{
// Handle the error...
}
}

소스 파일

이 예제에서 사용된 최종 씬과 모든 관련 파일은 Unity 패키지 관리자의 튜토리얼 샘플에서 임포트할 수 있습니다.

DeviceSelector.cs

경고

그리고 DeviceSelector 컴포넌트는 튜토리얼 용도로 사용되며 프로덕션 환경에서 사용해서는 안 됩니다. Unity 프로젝트에서 디바이스를 나열하고, 매핑하고, 연결하는 방법을 보여줍니다.

using Haply.Inverse;
using Haply.Inverse.DeviceControllers;
using Haply.Inverse.DeviceData;
using UnityEngine;

namespace Haply.Samples.Tutorials._7_DeviceMapping
{
public class DeviceSelector : MonoBehaviour
{
private DeviceMapper _deviceMapper;

public Inverse3Controller leftInverse3;
public Inverse3Controller rightInverse3;

private string _message;
private bool _waitingForVerseGripHandednessConfirm;

private void Awake()
{
_deviceMapper = GetComponent<DeviceMapper>();
_deviceMapper.autoAssign = false;
_deviceMapper.autoConnect = false;
_deviceMapper.autoFetchDeviceList = false;
_deviceMapper.Ready.AddListener(SetupVerseGripHandednessCheck);
_deviceMapper.Error += OnError;
leftInverse3.Ready.AddListener(OnDeviceReady);
rightInverse3.Ready.AddListener(OnDeviceReady);
}

// Start the verse grip handedness check (ask the user to press a button on the right controller)
private void SetupVerseGripHandednessCheck()
{
// Get the VerseGrip controllers using the DeviceMapper (must be done in Start or on DeviceMapper Ready event)
var leftVerseGrip = _deviceMapper.GetVerseGripController(leftInverse3);
var rightVerseGrip = _deviceMapper.GetVerseGripController(rightInverse3);

leftVerseGrip.ButtonDown.AddListener(OnButtonDown);
rightVerseGrip.ButtonDown.AddListener(OnButtonDown);

// Start waiting for button press on the right controller
_message = "Press the button on the right controller to confirm handedness.";
_waitingForVerseGripHandednessConfirm = true;
}

// Handle the button down event to confirm the verse grip handedness
private void OnButtonDown(VerseGripController verseGrip, VerseGripEventArgs args)
{
if (_waitingForVerseGripHandednessConfirm && args.Button is VerseGripButton.Button0 or VerseGripButton.Button1)
{
if (verseGrip == _deviceMapper.GetVerseGripController(rightInverse3))
{
_message = "VerseGrip handedness confirmed!";
}
else
{
_message = "Wrong controller button pressed. Swap the controllers.";
_deviceMapper.SwapVerseGrip();
}
_waitingForVerseGripHandednessConfirm = false;
}
else
{
_message = $"Button {args.Button} pressed on {verseGrip.DeviceId}";
}
}

// Handle the device ready event to start probing the cursor position
private void OnDeviceReady(Inverse3Controller device, Inverse3EventArgs _)
{
// Start probing the cursor position when the device is ready
device.ProbeCursorPosition();
}

// Handle errors from the device mapper
private void OnError(object sender, DeviceMapperErrorEventArgs e)
{
_message = e.ErrorMessage;
}

// Display the device list
private void DeviceListGUI()
{
if (_deviceMapper.GetNumInverse3() + _deviceMapper.GetNumVerseGrip() > 0)
{
GUILayout.Label("Connected devices:");
foreach (var device in _deviceMapper.GetInverse3Devices())
{
GUILayout.Label($"- {device}");
}
foreach (var device in _deviceMapper.GetVerseGripDevices())
{
GUILayout.Label($"- {device}");
}
}
}

// Display the device mapper state and actions.
private void DeviceMapperGUI()
{
switch (_deviceMapper.State)
{
case DeviceMapperState.UNINITIALIZED:
case DeviceMapperState.INITIALIZED:
if (GUILayout.Button(new GUIContent("List devices",
"Fetch the device list by HTTP request")))
{
_deviceMapper.FetchDeviceListOnce();
_message = "Fetching device list...";
}
break;

case DeviceMapperState.DEVICE_LIST_COMPLETE:
if (GUILayout.Button(new GUIContent("Map devices",
"Map the devices to the controllers according to the selected handedness")))
{
_deviceMapper.MapDevices();
_message = "Mapping devices...";
}
break;

case DeviceMapperState.MAPPING_COMPLETE:
if (!_deviceMapper.IsReady)
{
if (GUILayout.Button(new GUIContent("Connect",
"Connect the devices to WebSocket server to receive real-time data")))
{
_deviceMapper.Connect();
_message = "Connecting...";
}
}
break;

case DeviceMapperState.DEVICE_LIST_IN_PROGRESS:
case DeviceMapperState.MAPPING_IN_PROGRESS:
case DeviceMapperState.CONNECTED:
var style = new GUIStyle(GUI.skin.label) { normal = { textColor = Color.yellow } };
GUILayout.Label(_message, style);
break;

case DeviceMapperState.ERROR:
var errorStyle = new GUIStyle(GUI.skin.label) { normal = { textColor = Color.red } };
GUILayout.Label(_message, errorStyle);
if (GUILayout.Button(new GUIContent("Retry")))
{
_deviceMapper.Reset();
}
break;
}
}

// Display the device controller GUI for the inverse3 controller.
private void Inverse3ControllerGUI(Inverse3Controller controller)
{
// Before the device is assigned, we can select the handedness
if (!controller.Assigned)
{
GUILayout.Label("Inverse3Controller \u2192 <not assigned>", GUILayout.Width(800));

if (GUILayout.Button($"Filter: {controller.SelectedHandedness}"))
{
switch (controller.SelectedHandedness)
{
case HandednessType.Left:
controller.SelectedHandedness = HandednessType.Right;
break;
case HandednessType.Right:
controller.SelectedHandedness = HandednessType.Any;
break;
case HandednessType.Any:
controller.SelectedHandedness = HandednessType.Left;
break;
}
}
}

// Once the device is assigned, devices can be swapped
else
{
GUILayout.Label($"Inverse3Controller \u2192 #{controller.DeviceId}.{controller.Handedness}", GUILayout.Width(800));

// Swap the two inverse3 controllers' assigned devices
if (GUILayout.Button(new GUIContent("Swap inverse3",
"Swap the two inverse3 controller's assigned devices")))
{
_deviceMapper.SwapInverse3();
_waitingForVerseGripHandednessConfirm = true;
}
}

// Enable or disable the cursor position update
var probing= GUILayout.Toggle(controller.IsProbeCursorPosition, "Probe cursor position");
if (controller.IsReady && probing != controller.IsProbeCursorPosition)
{
controller.ProbeCursorPosition(probing);
}
}
private void VerseGripControllerGUI(VerseGripController controller)
{
// Before the device is assigned, we can select the VerseGrip type
if (!controller.Assigned)
{
GUILayout.Label("VerseGripController \u2192 <not assigned>", GUILayout.Width(800));
if (GUILayout.Button($"Filter: {controller.verseGripTypeFilter}"))
{
switch (controller.verseGripTypeFilter)
{
case VerseGripType.Wired:
controller.verseGripTypeFilter = VerseGripType.Wireless;
break;
case VerseGripType.Wireless:
controller.verseGripTypeFilter = VerseGripType.Any;
break;
case VerseGripType.Any:
controller.verseGripTypeFilter = VerseGripType.Wired;
break;
}
}
}

// Once the device is ready, devices can be swapped
else
{
GUILayout.Label($"VerseGripController \u2192 #{controller.DeviceId}.{controller.VerseGripType}", GUILayout.Width(800));

// Swap the two verse grip controllers' assigned devices
if (GUILayout.Button("Swap verse grip"))
{
_deviceMapper.SwapVerseGrip();
_waitingForVerseGripHandednessConfirm = true;
}
}
}

// Display the GUI
private void OnGUI()
{
// Show the device mapper state and actions
GUILayout.BeginArea(new Rect(10, 10, 400, 200), new GUIStyle(GUI.skin.box));
DeviceMapperGUI();
GUILayout.Space(10);
// Show the device list
DeviceListGUI();
GUILayout.EndArea();

// Show the left inverse3 controller
var leftRect = new Rect(0, Screen.height - 200, 300, 200);
leftRect.x = 10;
GUILayout.BeginArea(leftRect, new GUIStyle(GUI.skin.box));
GUILayout.Label(leftInverse3.gameObject.name, GUILayout.Width(600));

Inverse3ControllerGUI(leftInverse3);

GUILayout.Space(10);

// Show the associated verse grip controller
var leftVerseGripController = _deviceMapper.GetVerseGripController(leftInverse3);
VerseGripControllerGUI(leftVerseGripController);
GUILayout.EndArea();

// Show the right controller
var rightRect = new Rect(0, Screen.height - 200, 300, 200);
rightRect.x = Screen.width - rightRect.width - 10;
GUILayout.BeginArea(rightRect, new GUIStyle(GUI.skin.box));
GUILayout.Label(rightInverse3.gameObject.name, GUILayout.Width(600));

Inverse3ControllerGUI(rightInverse3);

GUILayout.Space(10);

// Show the associated verse grip controller
var rightVerseGripController = _deviceMapper.GetVerseGripController(rightInverse3);
VerseGripControllerGUI(rightVerseGripController);

GUILayout.EndArea();
}
}
}