Tutoriel sur la transformation de l'espace de travail de l'appareil
Ce tutoriel s'appuie sur le Tutoriel d'initiation au retour de force et explique comment régler la position, la rotation et la mise à l'échelle de l'appareil Inverse3 en l'adaptant grâce à ses propriétés et méthodes de transformation spatiale.
Introduction
Une fois que vous aurez configuré la scène pour le retour de force de base, vous remarquerez peut-être que les ajustements apportés à l'origine haptique ou au contrôleur haptique – tels que le déplacement, la mise à l'échelle ou la rotation – n'influencent pas le retour haptique comme prévu. Le retour haptique continue en effet de correspondre à une sphère invisible située devant l'appareil, qui n'est pas affectée par ces transformations.

Cet écart s'explique par le fait que les calculs de force utilisent les coordonnées réelles, non transformées, du curseur. Pour y remédier, nous utilisons les matrices de transformation mises en cache et compatibles avec les threads, fournies par le composant « Inverse3 Controller ». Cela nous permet d'appliquer des transformations en coordonnées mondiales aux calculs du retour haptique.
Mise en scène
Commencez par la scène du tutoriel d'initiation au retour de force et assurez-vous que la configuration (main gauche/droite) de la manette «Inverse3 » correspond à celle de votre appareil. Tournez la manette « Haptic Controller » de manière à ce que le logo « Haply » soit tourné vers la caméra, puis ajustez l'échelle de « Haptic Origin » si nécessaire pour agrandir la portée du curseur ou modifier sa position.


Modifications apportées au script de retour de force
Copiez le script SphereForceFeedback.cs tiré du tutoriel de base sur le retour de force et utilisez-le dans la fonction de rappel OnDeviceStateChanged a procédé aux ajustements suivants :
- Remplacez
device.CursorLocalPositionpardevice.CursorPosition. - Remplacez
device.CursorLocalVelocitypardevice.CursorVelocity. - Remplacez
device.CursorSetLocalForce(force)pardevice.CursorSetForce(force).
private void OnDeviceStateChanged(object sender, Inverse3EventArgs args)
{
var inverse3 = args.DeviceController;
var force = ForceCalculation(inverse3.CursorPosition, inverse3.CursorVelocity,
_cursorRadius, _ballPosition, _ballRadius);
inverse3.SetCursorForce(force);
}
Expérience de jeu
Maintenez le curseur « Inverse3 » enfoncé et lancez le mode de lecture. Essayez de toucher la boule comme dans l'exemple précédent. Vous devriez désormais ressentir un retour haptique précis, reflétant les transformations appliquées à « Haptic Origin » et au « Haptic Controller ».

Fichiers source
La scène complète et les fichiers associés à cet exemple peuvent être importés à partir de la section « Exemples de tutoriels » du Gestionnaire de packages de Unity.
La scène d'exemple contient des scripts supplémentaires permettant de personnaliser, lors de l'exécution, le contrôleur haptique et l'origine haptique.
SphereForceFeedback.cs
/*
* Copyright 2024 Haply Robotics Inc. All rights reserved.
*/
using Haply.Inverse.DeviceControllers;
using Haply.Inverse.DeviceData;
using UnityEngine;
namespace Haply.Samples.Tutorials._3_DeviceSpaceTransform
{
public class SphereForceFeedback : MonoBehaviour
{
// must assign in inspector
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 ??= FindFirstObjectByType<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. Using 'device.Position' instead of 'device.LocalPosition'
// ensures the force calculation considers the device's offset and rotation in world space.
var force = ForceCalculation(inverse3.CursorPosition, inverse3.CursorVelocity,
_cursorRadius, _ballPosition, _ballRadius);
// Apply the calculated force to the cursor. Using 'device.CursorSetForce' instead of
// 'device.CursorSetLocalForce' ensures that the force vector is correctly converted
// from world space to the device's local space.
inverse3.SetCursorForce(force);
}
}
}