フォースフィードバックの基礎チュートリアル
このチュートリアルでは、静的な物体(球体など)との接触時の物理的特性をシミュレートするために、剛性と減衰を含む基本的な触覚シミュレーションの作成方法を解説します。 このチュートリアルを完了すると、球体の存在を感知し、その剛性と減衰の特性を調整して、さまざまな触覚体験を得ることができるシミュレーションプログラムが完成します。
概要
このチュートリアルの主な課題は、剛性と減衰の両方を備えた球体 との接触時に生じる力を計算する関数を開発することです。 この文脈において、剛性はバネと同様の役割を果たし、圧縮されるほど大きな力が生じます。 一方、減衰は物体の運動に対する抵抗を表し、物体の移動速度が速いほど、受ける抵抗も大きくなります。
シーン設定
まず、以下の手順に従って 入門ガイド の説明に従って Haptic Rig を設定し、以下の点を確認してください。 ハプティック・オリジン 位置、回転、拡大縮小をそれぞれ次のように設定します。 (0, 0, 0) 和 (1, 1, 1)。
次に、名前を Sphere 球体を作成し、以下のプロパティを設定します:
- 位置:
(0, 0, -0.1)(装置の正面から約10センチメートルの位置) - ズーム:
(0.2, 0.2, 0.2)(直径20センチメートルの球体に対応)
フォースフィードバックスクリプト
「為」 スフィア ゲームオブジェクトに新しい C# スクリプトを追加し、名前を SphereForceFeedback.cs。
このスクリプトは、球体と接触した際に、剛性と減衰を考慮して、Inverse3 のカーソルに作用する力を計算します。
以下のプロパティを使用してスクリプトを初期化します:
[SerializeField]
private Inverse3 inverse3;
[Range(0, 800)]
public float stiffness = 300f;
[Range(0, 3)]
public float damping = 1f;
private Vector3 _ballPosition;
private float _ballRadius;
private float _cursorRadius;
カーソルが球体を貫通した場合にのみ、力計算を行うべきであり、これにより、特定の剛性と減衰を持つ実体物体に接触した際の感触をシミュレートすることができる。
ForceCalculation この計算は、カーソルの位置と速度を考慮しながら、以下の方法で行われます:
private Vector3 ForceCalculation(Vector3 cursorPosition, Vector3 cursorVelocity, float cursorRadius,
Vector3 otherPosition, float otherRadius)
{
var force = Vector3.zero;
var distanceVector = cursorPosition - otherPosition;
var distance = distanceVector.magnitude;
var penetration = otherRadius + cursorRadius - distance;
if (penetration > 0)
{
// Normalize the distance vector to get the direction of the force
var normal = distanceVector.normalized;
// Calculate the force based on penetration
force = normal * penetration * stiffness;
// Apply damping based on the cursor velocity
force -= cursorVelocity * damping;
}
return force;
}
private void OnDeviceStateChanged(object sender, Inverse3EventArgs args)
{
var inverse3 = args.DeviceController;
// Calculate the ball force
var force = ForceCalculation(inverse3.CursorLocalPosition, inverse3.CursorLocalVelocity,
_cursorRadius, _ballPosition, _ballRadius);
inverse3.SetCursorLocalForce(force);
}
で Awake その方法において、初期化 _ballPosition、_ballRadius 和 _cursorRadius,シーンデータを設定するには:
private void SaveSceneData()
{
var t = transform;
_ballPosition = t.position;
_ballRadius = t.lossyScale.x / 2f;
_cursorRadius = inverse3.Cursor.Radius;
}
private void Awake()
{
SaveSceneData();
}
それぞれ以下の場所で確認してください。 OnEnable 和 OnDisable 登録および登録解除の手順 OnDeviceStateChanged インタラクション中に力フィードバックを正しく処理するために、コールバックを設定します:
protected void OnEnable()
{
inverse3.DeviceStateChanged += OnDeviceStateChanged;
}
protected void OnDisable()
{
inverse3.DeviceStateChanged -= OnDeviceStateChanged;
}
ゲーム体験
Inverse3 のカーソルをホバーさせると、再生モードになり、球体に触れてみてください。 球体の存在を感じられるはずです。また、Unityのインスペクターで剛性や減衰のプロパティを調整することで、仮想オブジェクトとのインタラクションにおいてリアルな触感を得ることができます。

ソースファイル
この例の完全なシーンおよび関連するすべてのファイルは、Unityのパッケージマネージャーにある「Tutorials」サンプルからインポートできます。
スフィアフォースフィードバック.cs
/*
* Copyright 2024 Haply Robotics Inc. All rights reserved.
*/
using Haply.Inverse.DeviceControllers;
using Haply.Inverse.DeviceData;
using UnityEngine;
namespace Haply.Samples.Tutorials._2_BasicForceFeedback
{
public class SphereForceFeedback : MonoBehaviour
{
public Inverse3Controller inverse3;
[Range(0, 800)]
// Stiffness of the force feedback.
public float stiffness = 300f;
[Range(0, 3)]
public float damping = 1f;
private Vector3 _ballPosition;
private float _ballRadius;
private float _cursorRadius;
/// <summary>
/// Stores the cursor and sphere transform data for access by the haptic thread.
/// </summary>
private void SaveSceneData()
{
var t = transform;
_ballPosition = t.position;
_ballRadius = t.lossyScale.x / 2f;
_cursorRadius = inverse3.Cursor.Radius;
}
/// <summary>
/// Saves the initial scene data cache.
/// </summary>
private void Awake()
{
inverse3 ??= FindObjectOfType<Inverse3Controller>();
inverse3.Ready.AddListener((device, args) => SaveSceneData());
}
/// <summary>
/// Subscribes to the DeviceStateChanged event.
/// </summary>
private void OnEnable()
{
inverse3.DeviceStateChanged += OnDeviceStateChanged;
}
/// <summary>
/// Unsubscribes from the DeviceStateChanged event.
/// </summary>
private void OnDisable()
{
inverse3.DeviceStateChanged -= OnDeviceStateChanged;
inverse3.Release();
}
/// <summary>
/// Calculates the force based on the cursor's position and another sphere position.
/// </summary>
/// <param name="cursorPosition">The position of the cursor.</param>
/// <param name="cursorVelocity">The velocity of the cursor.</param>
/// <param name="cursorRadius">The radius of the cursor.</param>
/// <param name="otherPosition">The position of the other sphere (e.g., ball).</param>
/// <param name="otherRadius">The radius of the other sphere.</param>
/// <returns>The calculated force vector.</returns>
private Vector3 ForceCalculation(Vector3 cursorPosition, Vector3 cursorVelocity, float cursorRadius,
Vector3 otherPosition, float otherRadius)
{
var force = Vector3.zero;
var distanceVector = cursorPosition - otherPosition;
var distance = distanceVector.magnitude;
var penetration = otherRadius + cursorRadius - distance;
if (penetration > 0)
{
// Normalize the distance vector to get the direction of the force
var normal = distanceVector.normalized;
// Calculate the force based on penetration
force = normal * penetration * stiffness;
// Apply damping based on the cursor velocity
force -= cursorVelocity * damping;
}
return force;
}
/// <summary>
/// Event handler that calculates and send the force to the device when the cursor's position changes.
/// </summary>
/// <param name="sender">The Inverse3 data object.</param>
/// <param name="args">The event arguments containing the device data.</param>
private void OnDeviceStateChanged(object sender, Inverse3EventArgs args)
{
var inverse3 = args.DeviceController;
// Calculate the ball force
var force = ForceCalculation(inverse3.CursorLocalPosition, inverse3.CursorLocalVelocity,
_cursorRadius, _ballPosition, _ballRadius);
inverse3.SetCursorLocalForce(force);
}
}
}