Tutoriel d'initiation au retour de force
Ce tutoriel vous guide dans la création d'une simulation haptique simple qui tient compte à la fois de la rigidité et de l'amortissement, et qui reproduit les propriétés physiques du contact avec un objet statique, tel qu'une boule. À la fin de ce tutoriel, vous disposerez d'une simulation vous permettant de percevoir la présence d'une boule et d'ajuster ses propriétés de rigidité et d'amortissement afin d'obtenir différentes expériences haptiques.
Introduction
Le principal défi de ce tutoriel consiste à développer une fonction permettant de calculer les forces générées lors du contact avec une sphère présentant à la fois une rigidité et un amortissement. Dans ce contexte, la rigidité agit comme un ressort et génère d'autant plus de force qu'elle est fortement comprimée. L'amortissement, quant à lui, décrit la résistance d'un objet au mouvement : plus celui-ci se déplace rapidement, plus cette résistance est importante.
Mise en scène
Commencez par configurer un Haptic Rig, comme indiqué dans le Guide « Premiers pas » décrites, et assurez-vous que la position, la rotation et la mise à l'échelle de l' Origine haptique auf (0, 0, 0) ou bien (1, 1, 1) sont fixées.
Créez ensuite une sphère nommée Sphere présentant les caractéristiques suivantes :
- Poste :
(0, 0, -0.1)(à environ 10 cm devant l'appareil) - Mise à l'échelle :
(0.2, 0.2, 0.2)(ce qui correspond à une sphère de 20 cm de diamètre)
Script de retour de force
Ajoutez au GameObject Sphère un nouveau script C# nommé SphereForceFeedback.cs hinzu.
Ce script calcule les forces exercées sur le curseur Inverse3 lors du contact avec la sphère, en tenant compte de la rigidité et de l'amortissement.
Définissez les propriétés suivantes pour le script :
[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;
Le calcul de la force ne doit être effectué que lorsque le curseur traverse la sphère, afin de simuler la sensation de toucher un objet physique doté d'une rigidité et d'un amortissement définis.
La méthode ForceCalculation est chargé de cette tâche et prend en compte à la fois la position et la vitesse du curseur :
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);
}
Initialisez dans la méthode Awake _ballPosition, _ballRadius et _cursorRadius, pour configurer les données de scène :
private void SaveSceneData()
{
var t = transform;
_ballPosition = t.position;
_ballRadius = t.lossyScale.x / 2f;
_cursorRadius = inverse3.Cursor.Radius;
}
private void Awake()
{
SaveSceneData();
}
Assurez-vous que la fonction de rappel OnDeviceStateChanged dans OnEnable enregistré et dans OnDisable est désactivé afin de permettre un traitement correct du retour de force pendant l'interaction :
protected void OnEnable()
{
inverse3.DeviceStateChanged += OnDeviceStateChanged;
}
protected void OnDisable()
{
inverse3.DeviceStateChanged -= OnDeviceStateChanged;
}
Expérience de jeu
Tout en maintenant la touche « Inverse3 » enfoncée, lancez le mode de lecture et essayez de toucher la boule. Vous devriez ressentir la présence de la boule et pouvoir ajuster ses propriétés de rigidité et d'amortissement via l'Inspecteur Unity. Cela permet de créer une sensation tangible d'interaction avec un objet virtuel.

Fichiers source
La scène complète et tous les fichiers associés à cet exemple peuvent être importés à partir de la section « Exemples de tutoriels » du Gestionnaire de paquets de Unity.
SphereForceFeedback.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);
}
}
}