メインコンテンツへスキップ

デバイスマッピングのチュートリアル

このチュートリアルでは、Unity プロジェクト内で「Haply 」の Inverse Serviceを使用して、デバイスを手動で一覧表示、マッピング、および接続する方法について解説します。

概要​

DeviceMapperコンポーネントは、Haply デバイス(Inverse3 やVerseGripなど)の検出、マッピング、接続を管理し、シーン内のデバイスコントローラーと連携して動作します。

シーン設定​

まず、Haptic Rig を作成します:GameObject >Haply > Haptic Rig (two hands)。具体的な手順については、入門ガイドをご覧ください。

DeviceSelector コンポーネント​

「」という名前の DeviceSelector.cs 新しい C# スクリプトを作成し、それを デバイスマッパー GameObject 上で。 で DeviceSelector このクラスには、以下のプロパティが定義されています:

public Inverse3Controller leftInverse3;
public Inverse3Controller rightInverse3;

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

Device Mapper の設定​

で Awake メソッド内の初期化 _deviceMapper 属性、および購読 Ready このイベントは、Device Mapper の準備完了後の処理を行うためのものです。

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 左右の手を選択します。 DeviceMapper は、選択された左右の手に基づいて、接続済みのデバイスを自動的に対応するコントローラーに割り当てます。

// 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 デバイスを WebSocket サーバーに接続し、リアルタイムデータを受信してデバイスを制御する方法。

情報

以下の例では、デバイスの準備が整った後、次のように呼び出します。 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のパッケージマネージャーにある「Tutorials」サンプルからインポートできます。

デバイスセレクタ.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();
}
}
}